PART1(算法思想简介)
1.实现:
1.整个树是按照中序访问下标顺序不变来维护的(reverse操作变化了区间中各下标内存储的结点内容,但整棵树按照下标顺序访问依旧不变)
2.难道不是splay时只要不断rotate(x)就行了嘛??
图来源(最后一个图虽然稍有错误,但是大体上是对的)
这样一字旋转的时候转动fa转动x交替进行,就不会出现转来转去还是一条链的情况了
a.在有了伸展树后的第一个问题是如何表示任意一个【a,b】区间,显然只要用splay的操作将树根变为a-1,将树根的右节点变为b+1,则该节点的左子树即表示区间【a,b】
2.时间复杂度:
3.特别优势:
a.用伸展树解决数列维护问题,可以支持两个线段树无法支持的操作:在某个位置插入一些数和删除一些连续的数。
b.但是代码量很大,遇到问题还是应当首先考虑线段树,认真斟酌后再考虑splay tree
4.适用情况:
5.需要注意的点:
这种题目并不适合将成员和函数封装成为一个树的结构体,为何?因为树的结构体只能从固定的root结点访问过来,但是这道题任何结点都可能成为访问操作的root结点。所以如果封装成一个结构体,大大减少了它的灵活性,对这个结构来说是十分不好的.....枯了
// tree.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include <iostream> #include <cstdio> #include <cstdlib> #include <algorithm> #include <cmath> #include <cstring> #include <string> #include <vector> #include <queue> #include <sstream> #include <stack> #include <map> #include <ctime> #include <array> #include <set> using namespace std; vector<string> departString_string(string data) { vector<int> back_part;//output type int i, j; vector<string> part; string A_part; stringstream room; room.str(data); while (room >> A_part) part.push_back(A_part); return part; } //———————————————— //版权声明:本文为CSDN博主「systemyff」的原创文章,遵循CC 4.0 BY - SA版权协议,转载请附上原文出处链接及本声明。 //原文链接:https ://blog.csdn.net/u014377763/article/details/113845555 template<class ElemType> struct tree_point { ElemType data;//数据 struct tree_point* l_child, * r_child;//左、右孩子指针 }; template<class ElemType> class BinaryTree { private: vector<tree_point<ElemType>*> outlist; tree_point<ElemType>* root; // 头指针 public: BinaryTree() :root(NULL) { //无参数的构造函数 } ~BinaryTree() { //析构函数 } void BinaryTree_fron(vector<ElemType> lis, ElemType nut) { stack<tree_point<ElemType>*> s; tree_point<ElemType>* p_Parent = NULL, * p_Child = NULL; int i = 0; int flag = 0;//控制左右 p_Parent = new tree_point<ElemType>; p_Parent->data = lis[i]; p_Parent->l_child = p_Parent->r_child = NULL; s.push(p_Parent); root = p_Parent; i = 1; flag = 0; while (!s.empty()) { if (lis[i] != nut) { p_Parent = new tree_point<ElemType>; p_Parent->data = lis[i]; p_Parent->l_child = p_Parent->r_child = NULL; if (flag == 0) { p_Child = s.top(); p_Child->l_child = p_Parent; } else if (flag == 1) { p_Child = s.top(); s.pop(); p_Child->r_child = p_Parent; } s.push(p_Parent); flag = 0; } else { if (flag == 0) flag = 1; else if (flag == 1) s.pop(); } i++; } } tree_point<ElemType>* get_root() { return root; } void qianxu(tree_point<ElemType>* t) { outlist.push_back(t); if (t->l_child != NULL) qianxu(t->l_child); if (t->r_child != NULL) qianxu(t->r_child); return; } void zhongxu(tree_point<ElemType>* t) { if (t->l_child != NULL) zhongxu(t->l_child); outlist.push_back(t); if (t->r_child != NULL) zhongxu(t->r_child); return; } void houxu(tree_point<ElemType>* t) { if (t->l_child != NULL) houxu(t->l_child); if (t->r_child != NULL) houxu(t->r_child); outlist.push_back(t); return; } int c_t = 0; void cengxu(tree_point<ElemType>* t) { vector<tree_point<ElemType>* > q; q.push_back(t); int last = 1, cur = 0; while (cur < q.size()) { last = q.size(); while (cur < last) { outlist.push_back(q[cur]); if (q[cur]->l_child) q.push_back(q[cur]->l_child); if (q[cur]->r_child) q.push_back(q[cur]->r_child); ++cur; } c_t++; } } void out_lis() { int i; for (i = 0; i < outlist.size(); i++) { cout << outlist[i]->data; if (i != outlist.size() - 1) cout << ","; else cout << endl; } outlist.clear(); } int cnt = 0, p_cnt = 0; void t_cnt(tree_point<ElemType>* t) { p_cnt++; if (t->l_child != NULL && t->r_child != NULL) cnt++; if (t->l_child != NULL) t_cnt(t->l_child); if (t->r_child != NULL) t_cnt(t->r_child); } int cengshu() { c_t = 0; cengxu(root); outlist.clear(); return c_t; } int P_cengshu(tree_point<ElemType>* t) { c_t = 0; cengxu(t); outlist.clear(); return c_t; } int twocnt() { cnt = 0; t_cnt(root); return cnt; } int P_twocnt(tree_point<ElemType>* t) { cnt = 0; t_cnt(t); return cnt; } int pointcnt() { p_cnt = 0; t_cnt(root); return p_cnt; } tree_point<ElemType>* father_p; void find_fatherNow(tree_point<ElemType>* t, ElemType ss) { if (t->l_child != NULL) if (t->l_child->data == ss) { father_p = t; return; } if (t->r_child != NULL) if (t->r_child->data == ss) { father_p = t; return; } if (t->l_child != NULL) find_fatherNow(t->l_child, ss); if (t->r_child != NULL) find_fatherNow(t->r_child, ss); return ; } tree_point<ElemType>* find_father(tree_point<ElemType>* t, ElemType ss) { father_p = NULL; find_fatherNow(t, ss); return father_p; } tree_point<ElemType>* nas_p; void find_selfNOW(tree_point<ElemType>* t, ElemType ss) { if (t->data == ss) nas_p = t; if (t->l_child != NULL) find_selfNOW(t->l_child, ss); if (t->r_child != NULL) find_selfNOW(t->r_child, ss); return ; } tree_point<ElemType>* find_self(tree_point<ElemType>* t, ElemType ss) { nas_p = NULL; find_selfNOW(t, ss); return nas_p; } void fast_delChild(tree_point<ElemType>* t,bool flag) { if (flag == 0) t->l_child = NULL; else t->r_child = NULL; return; } void slow_delChild(tree_point<ElemType>* t, bool flag) { if (flag == 0) t->l_child = NULL; else t->r_child = NULL; return; } }; int main() { string s, ins, nulls; vector<string> part_in; BinaryTree<string> a; cin >> nulls; cin.get(); getline(cin, ins); part_in = departString_string(ins); string found; cin >> found; bool flag; cin >> flag; //====================================== /*for (auto i : part_in) { cout << i << endl; } cout << part_in.size() << endl; cout << nulls << endl;*/ //====================================== a.BinaryTree_fron(part_in, nulls); /*a.qianxu(a.get_root()); a.out_lis(); a.zhongxu(a.get_root()); a.out_lis(); a.houxu(a.get_root()); a.out_lis(); cout << endl;*/ //cout<<a.pointcnt()<<endl; /*a.cengxu(a.get_root()); a.out_lis();*/ //cout << a.cengshu() << endl; tree_point<string>* ans = a.find_father(a.get_root(), found); if (ans == NULL) cout << "NULL" << endl; else { if (flag == 0)// left child { if (ans->l_child == NULL || ans->l_child->data==found) cout << "NULL" << endl; else { cout << ans->l_child->data << endl; } } else { if (ans->r_child == NULL || ans->r_child->data == found) cout << "NULL" << endl; else { cout << ans->r_child->data<< endl; } } } //cout << a.P_cengshu(ans) << endl; return 0; }
6.函数、变量名的解释+英文:
rotate(v.旋转,转动;轮班;轮换)
7.dalao分析:
基础详解+代码(这个大佬的代码实测已经不太对劲了)
基础模板版本
#include<cstdio> #include<cmath> #include<algorithm> #include<set> #include<map> #include<cstring> #include<string> #include<vector> #include<queue> #include<iomanip> #include<iostream> #include<stack> using namespace std; ///-------------------------------------------------------常用规定部分 //----------------------便于更改 //nodeType(1~1e7) 都是int就不必麻烦了 #define valueType_ int//valueType(1~1e19) //----------------------通用 #define inf_ 0x3f3f3f3f//这是给int的inf,值为1061109567,2^31-1为2147483647 #define reg_ register int #define for_(i, n) for(int i = 1; i <= n; i++) #define forReg_(i, n) for(reg_ i = 1; i <= n; i++) #define ll_ long long #define ull_ unsigned long long //----------------------边访问 #define visitEdge_ int i = p[u]; ~i; i = e[i].next #define defineV_ int v = e[i].v #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue //----------------------线段树 #define mid_ int mid = (l+r)>>1//mid的定义 #define len_ (r-l+1) #define lId_ id<<1 #define rId_ id<<1|1 #define lSon_ id<<1,l,mid #define rSon_ id<<1|1,mid+1,r #define include_(x,y,l,r) x<=l && r<=y //----------------------其它 const int maxN_ = 1e6+10; const int maxM_ = 1e4+10; ///-------------------------------------------------------变量声明部分 //--------------------------------------其它 struct SPLAY_NODE { int fa, son[2]; int size, cnt, tag;//size:子树大小,cnt:该大小节点的个数,tag:表示以x为根的树要不要reverse valueType_ value; } sNodeg[maxN_]; int original[maxN_],root,nodeNum;//original:原始数据,nodeNum:the number of sNodeg by now(动态改变的) ///--------------------------------------------------------函数声明部分 //--------------------------------------其它 inline bool Which(int x); //x是父亲的右子树吗 inline void UpDate(int x); inline void PushDown(int x); int BuildSplayTree(int l, int r, int fa); inline void Rotate(int x); inline void Splay(int x,int goal); inline int Find(int order);//找到中序遍历第order个数 inline void Reverse(int x,int y); inline void SplayDfs(int now); ///--------------------------------------------------------main函数部分 int main() { //freopen("in.txt","r", stdin); //freopen("out.txt","w", stdout); ios::sync_with_stdio(false); //InitEdge(); int n, m, x, y; cin >> n >> m; original[1] = -inf_, original[n+2] = inf_; for_(i, n) original[i+1] = i; root = BuildSplayTree(1,n+2,0);//有一个良好的定义变量习惯很重要……重复定义同一个变量(比如全局的和局部的同名)那么就会发生覆盖。 for_(i, m) { cin >> x >> y; Reverse(x+1, y+1); } SplayDfs(root); return 0; } ///--------------------------------------------------------函数定义部分 //----------------------------------其它 inline bool Which(int x) //x是父亲的右子树吗 { return x == sNodeg[sNodeg[x].fa].son[1]; } //作用:如果这是非0(相当于NULL),就由两个子树的size更新x的size //要求:需要在之前保证两个子树值的正确 inline void UpDate(int x) { if(x) { sNodeg[x].size = sNodeg[x].cnt; if(sNodeg[x].son[0]) sNodeg[x].size += sNodeg[sNodeg[x].son[0]].size; if(sNodeg[x].son[1]) sNodeg[x].size += sNodeg[sNodeg[x].son[1]].size; } } //解释:lazy数组存在的最上层的Node的结点本来就保证内容正确,但他下面的子树无法保证正确 //作用:将x没有传递下去的操作传递给两个子树,使得子树的数据正确 inline void PushDown(int x) { if(x && sNodeg[x].tag) { sNodeg[sNodeg[x].son[1]].tag ^= 1; sNodeg[sNodeg[x].son[0]].tag ^= 1; swap(sNodeg[x].son[1],sNodeg[x].son[0]); sNodeg[x].tag=0; } } //作用:按照原始数据的下标(而非权值大小)来建立slayTree(这样splayTree的结构就和下标挂钩了) //解释:如果想要tree结构和大小挂钩,就应当按照大小拍了序才build(本质还是结构和下标挂钩) int BuildSplayTree(int l, int r, int fa) { if(l > r) return 0; mid_; int x = ++nodeNum; sNodeg[x].fa = fa; sNodeg[x].cnt = 1; sNodeg[x].value = original[mid]; sNodeg[x].son[0] = BuildSplayTree(l, mid - 1, x); sNodeg[x].son[1] = BuildSplayTree(mid + 1, r, x); UpDate(x); return x; } //作用:x通过左旋或者右旋顶替父亲结点(并且如果父亲结点是根的话要更新root) inline void Rotate(int x) { int fa = sNodeg[x].fa; int ffa = sNodeg[fa].fa; PushDown(fa), PushDown(x);//因为fa在上面,所以先传递fa bool w = Which(x);//w==1是fa的右子树 sNodeg[fa].son[w] = sNodeg[x].son[w^1]; if(sNodeg[x].son[w^1]) sNodeg[sNodeg[fa].son[w]].fa=fa; sNodeg[fa].fa=x; sNodeg[x].fa=ffa; sNodeg[x].son[w^1]=fa; if(ffa) { sNodeg[ffa].son[sNodeg[ffa].son[1]==fa]=x; } UpDate(fa);//维护fa if(fa == root) root = x; } //作用:将x上升到父亲结点是goal inline void Splay(int x,int goal) { int fa = sNodeg[x].fa; int ffa = sNodeg[fa].fa; for(PushDown(x); fa != goal;fa = sNodeg[x].fa,ffa = sNodeg[fa].fa)//我觉得这里的pushDown是没用的,情况一rotatex自己会更新,情况而rotate(y)不影响x,rotate(x)自己也会更新,不rotate的时候,没必要下放rotate(不过如果是求和lazy另当别论,毕竟后面还要update(x)的,对!就是为了后面的update) {//不要忘记每次更新一下fa和ffa if(ffa != goal)//如果需要转移两次及以上 (Which(x)^Which(fa))?Rotate(x):Rotate(fa);//位异或(^),不同才为1,即之字形转x,一字型转fa Rotate(x); } UpDate(x); } //作用:找到中序遍历第order个数 inline int Find(int order) { int x = root; while(1) { PushDown(x); if(order <= sNodeg[sNodeg[x].son[0]].size) x = sNodeg[x].son[0]; else { order -= sNodeg[sNodeg[x].son[0]].size + 1; if(!order) return x;//当根节点为要求点时就在此处(还未进入右子树)退出 x = sNodeg[x].son[1]; } } } //作用:颠倒中序遍历的[x,y]区间的数 inline void Reverse(int x,int y) { int l = x-1, r = y+1; l = Find(l), r = Find(r); //解释:由于l肯定小于r,所以目标区间肯定是root(l)的右子树(r)的左子树 Splay(l, 0); Splay(r, l); int pos = sNodeg[root].son[1]; pos = sNodeg[pos].son[0]; sNodeg[pos].tag ^= 1; } //作用:中序遍历展示value inline void SplayDfs(int now) { PushDown(now); if(sNodeg[now].son[0]) SplayDfs(sNodeg[now].son[0]); if(sNodeg[now].value!=-inf_ && sNodeg[now].value!=inf_) cout << sNodeg[now].value << " "; if(sNodeg[now].son[1]) SplayDfs(sNodeg[now].son[1]); }
#include <cstdio> #include <algorithm> #include <cstring> #include <ctime> #include <iostream> using namespace std; //nodeType(1~1e7) 都是int就不必麻烦了 #define valueType_ int//valueType(1~1e19) //----------------------通用 #define inf_ 2000000000//这是给int的inf,值为1061109567,2^31-1为2147483647 #define reg_ register int #define for_(i, n) for(int i = 1; i <= n; i++) #define forReg_(i, n) for(reg_ i = 1; i <= n; i++) #define ll_ long long #define ull_ unsigned long long //----------------------边访问 #define visitEdge_ int i = p[u]; ~i; i = e[i].next #define defineV_ int v = e[i].v #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue //----------------------线段树 #define mid_ int mid = (l+r)>>1//mid的定义 #define len_ (r-l+1) #define lId_ id<<1 #define rId_ id<<1|1 #define lSon_ id<<1,l,mid #define rSon_ id<<1|1,mid+1,r #define include_(x,y,l,r) x<=l && r<=y //----------------------其它 const int maxN_ = 200007; const int maxM_ = 1e4+10; ///-------------------------------------------------------变量声明部分 //--------------------------------------其它 int val[maxN_], n, m; struct SPLAY_TREE { #define lson s[x].son[0] #define rson s[x].son[1] struct data { int val, siz, son[2], fa, cnt; } s[maxN_ * 10]; int tot, root; inline int NewNode() { s[++tot].cnt = 1; return tot; } inline int Which(int x) { return s[s[x].fa].son[1] == x; } inline void PushUp(int x) { s[x].siz = s[lson].siz + s[rson].siz + s[x].cnt; } inline void Rotate(int x) { int fa = s[x].fa, ffa = s[fa].fa, op = Which(x); s[fa].son[op] = s[x].son[op ^ 1]; if (s[fa].son[op]) s[s[fa].son[op]].fa = fa; s[x].son[op ^ 1] = fa, s[fa].fa = x, s[x].fa = ffa; if (ffa) s[ffa].son[s[ffa].son[1] == fa] = x; PushUp(fa), PushUp(x); } void Splay(int x, int &tar) { for (int u = s[tar].fa, fa; (fa = s[x].fa) != u; Rotate(x)) if (s[fa].fa != u) Rotate(Which(fa) == Which(x) ? fa : x); tar = x; } void Build(int l, int r, int &x) { x = NewNode(); int mid = (l + r) >> 1; s[x].val = val[mid]; if (mid > l) Build(l, mid - 1, lson), s[lson].fa = x; if (r > mid) Build(mid + 1, r, rson), s[rson].fa = x; PushUp(x); } void Insert(int &x, int fa, int val) { if (!x) x = NewNode(), s[x].fa = fa, s[x].val = val; else if(s[x].val == val) s[x].cnt++; else Insert(s[x].son[val > s[x].val], x, val); PushUp(x); } int Find(int x, int val) { if (s[x].val == val) return x; else return Find(s[x].son[val > s[x].val], val); } int GetPre(int x, int val) { if (!x) return 0; if (s[x].val < val) { int a = x, b = GetPre(rson, val); return b == 0 ? a : b; } else return GetPre(lson, val); } int GetNext(int x, int val) { if (!x) return 0; if (s[x].val > val) { int a = x, b = GetNext(lson, val); return b == 0 ? a : b; } else return GetNext(rson, val); } void Delete(int val) { int x = Find(root, val), l, r; if(x && s[x].cnt > 1) { s[x].cnt--; Splay(x, root); return ; } Splay(x, root), l = lson, r = rson; while (s[l].son[1]) l = s[l].son[1]; Splay(l, s[x].son[0]); s[r].fa = l, s[l].fa = 0, s[l].son[1] = r, PushUp(l), root = l; } int Getrank(int val) { int x = GetPre(root, val); Splay(x, root); return s[lson].siz+s[x].cnt; } int Getnum(int x, int kth) { if (kth <= s[lson].siz) return Getnum(lson, kth); else if(kth > s[lson].siz + s[x].cnt) return Getnum(rson, kth - s[lson].siz - s[x].cnt); else return x; } void Print1ToTot() { cout <<"tot"<< tot<<endl; for(int i = 1; i <= tot; i++) { cout <<i<<"'s value:"<< s[i].val <<" cnt: "<<s[i].cnt<<" sum: "<<s[i].siz<<endl; } cout << endl; } void PrintfMidDfs(int x) { if(!x) return ; PrintfMidDfs(s[x].son[0]); cout <<s[x].val<<endl; PrintfMidDfs(s[x].son[1]); } } bst; ///--------------------------------------------------------函数声明部分 //--------------------------------------其它 ///--------------------------------------------------------main函数部分 int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", &val[i]); sort(val + 1, val + 1 + n); val[0] = -inf_, val[1 + n] = inf_; bst.Build(0, 1 + n, bst.root); int lastans = 0, op, x, y, ans = 0; for (int i = 1; i <= m; ++i) { scanf("%d%d", &op, &x); x ^= lastans, y = 0; if (op == 1) bst.Insert(bst.root, 0, x), y = bst.tot; if (op == 2) bst.Delete(x); if (op == 3) lastans = bst.Getrank(x), ans ^= lastans; if (op == 4) y = bst.Getnum(bst.root, x + 1), lastans = bst.s[y].val, ans ^= lastans; if (op == 5) y = bst.GetPre(bst.root, x), lastans = bst.s[y].val, ans ^= lastans; if (op == 6) y = bst.GetNext(bst.root, x), lastans = bst.s[y].val, ans ^= lastans; ///这一步可以加上 if (y && (i % 10 == 0)) bst.Splay(y, bst.root); } printf("%d ", ans); return 0; } ///--------------------------------------------------------函数定义部分 //----------------------------------其它
/** 函数不只可以返回一个值,它可以通过函数的参数列表的应用返回一堆值(见search函数的parent的的用法,可对比root的用法) **/ /** 不仅可以通过递归访问树,也可以通过非递归(while)啊!这样节省了代码和思考的经历,妙! **/ #include<cstdio> #include<cmath> #include<algorithm> #include<set> #include<map> #include<cstring> #include<string> #include<vector> #include<queue> #include<iomanip> #include<iostream> #include<stack> using namespace std; ///-------------------------------------------------------常用规定部分 //----------------------便于更改 //nodeType(1~1e7) 都是int就不必麻烦了 #define valueType_ int//valueType(1~1e19) //----------------------通用 #define inf_ 0x3f3f3f3f//这是给int的inf,值为1061109567,2^31-1为2147483647 #define reg_ register int #define for_(i, n) for(int i = 1; i <= n; i++) #define forReg_(i, n) for(reg_ i = 1; i <= n; i++) #define ll_ long long #define ull_ unsigned long long //----------------------边访问 #define visitEdge_ int i = p[u]; ~i; i = e[i].next #define defineV_ int v = e[i].v #define defineVAvoidFa_ int v = e[i].v;if(v == fa) continue //----------------------线段树 #define mid_ int mid = (l+r)>>1//mid的定义 #define len_ (r-l+1) #define lId_ id<<1 #define rId_ id<<1|1 #define lSon_ id<<1,l,mid #define rSon_ id<<1|1,mid+1,r #define include_(x,y,l,r) x<=l && r<=y //----------------------其它 const int maxN_ = 1e7+10; const int maxM_ = 1e4+10; ///-------------------------------------------------------变量声明部分 //--------------------------------------其它 struct SPLAY_NODE { int fa, son[2]; int size, cnt, tag;//size:子树大小,cnt:该大小节点的个数,tag:表示以x为根的树要不要reverse valueType_ value; } sNodeg[maxN_]; int originalg[maxN_],rootg,nodeNumg;//original:原始数据,nodeNum:the number of sNodeg by now(动态改变的)有效值[1,n] ///--------------------------------------------------------函数声明部分 //--------------------------------------其它 inline bool Which(int x); //x是父亲的右子树吗 inline void UpDate(int x); inline void PushDown(int x); int BuildSplayTree(int l, int r, int fa); inline void Rotate(int x); inline void Splay(int x,int goal); inline int FindOder(int order);//找到中序遍历第order个数 inline void Reverse(int x,int y); inline void SplayDfs(int now); int SearchValue(int x, valueType_ value, int &fa); bool Search(int x, int value); inline int CreatSplayNode(int value = 0); inline int SplayInsert(valueType_ value); inline int NextHalf(int x,int op); inline int Next(int value,int op); bool Delete(valueType_ value); int CountOder(int value); int FindKthNumber(int k); inline valueType_ Read() { valueType_ x=0; char c=getchar(); while(c<'0'||c>'9') c=getchar(); while(c>='0'&&c<='9') { x=(x<<1)+(x<<3)+c-'0'; c=getchar(); } return x; } ///--------------------------------------------------------main函数部分 int main() { //freopen("in.txt","r", stdin); //freopen("out.txt","w", stdout); //ios::sync_with_stdio(false); //InitEdge(); int n, m, num; n = Read(); m = Read(); SplayInsert(-inf_); SplayInsert(inf_); for(int i = 1; i <= n; i++) { num = Read(); SplayInsert(num); } int ans = 0, last = 0; while(m--) { int opt, x; opt = Read(); x = Read(); if (opt==1) SplayInsert(x); if (opt==2) Delete(x); if (opt==3) x = x^last,last = CountOder(x),ans ^= last; //printf("%d ",CountOder(x)); if (opt==4) x = x^last,last = FindKthNumber(x+1), ans ^= last; //printf("%d ",FindKthNumber(x+1));//注意还有-inf呢 if (opt==5) x = x^last,last = sNodeg[Next(x, 0)].value, ans ^= last; //printf("%d ",sNodeg[Next(x, 0)].value); if (opt==6) x = x^last,last = sNodeg[Next(x,1)].value, ans ^= last; //printf("%d ",sNodeg[Next(x,1)].value); } printf("%d ", ans); return 0; } ///--------------------------------------------------------函数定义部分 //----------------------------------其它 inline bool Which(int x) //x是父亲的右子树吗 { return x == sNodeg[sNodeg[x].fa].son[1]; } //作用:如果这是非0(相当于NULL),就由两个子树的size更新x的size //要求:需要在之前保证两个子树值的正确 inline void UpDate(int x) { if(x) { sNodeg[x].size = sNodeg[x].cnt; if(sNodeg[x].son[0]) sNodeg[x].size += sNodeg[sNodeg[x].son[0]].size; if(sNodeg[x].son[1]) sNodeg[x].size += sNodeg[sNodeg[x].son[1]].size; } } //解释:lazy数组存在的最上层的Node的结点本来就保证内容正确,但他下面的子树无法保证正确 //作用:将x没有传递下去的操作传递给两个子树,使得子树的数据正确 inline void PushDown(int x) { if(x && sNodeg[x].tag) { sNodeg[sNodeg[x].son[1]].tag ^= 1; sNodeg[sNodeg[x].son[0]].tag ^= 1; swap(sNodeg[x].son[1],sNodeg[x].son[0]); sNodeg[x].tag=0; } } //作用:x通过左旋或者右旋顶替父亲结点(并且如果父亲结点是根的话要更新root) inline void Rotate(int x) { int fa = sNodeg[x].fa; int ffa = sNodeg[fa].fa; PushDown(fa), PushDown(x);//因为fa在上面,所以先传递fa bool w = Which(x);//w==1是fa的右子树 sNodeg[fa].son[w] = sNodeg[x].son[w^1]; if(sNodeg[x].son[w^1]) sNodeg[sNodeg[fa].son[w]].fa=fa; sNodeg[fa].fa=x; sNodeg[x].fa=ffa; sNodeg[x].son[w^1]=fa; if(ffa) { sNodeg[ffa].son[sNodeg[ffa].son[1]==fa]=x; } UpDate(fa);//维护fa if(fa == rootg) rootg = x; } //作用:将x上升到父亲结点是goal inline void Splay(int x,int goal) { int fa = sNodeg[x].fa; int ffa = sNodeg[fa].fa; for(PushDown(x); fa != goal; fa = sNodeg[x].fa,ffa = sNodeg[fa].fa) //我觉得这里的pushDown是没用的,情况一rotatex自己会更新,情况而rotate(y)不影响x,rotate(x)自己也会更新,不rotate的时候,没必要下放rotate(不过如果是求和lazy另当别论,毕竟后面还要update(x)的,对!就是为了后面的update) { //不要忘记每次更新一下fa和ffa if(ffa != goal)//如果需要转移两次及以上 (Which(x)^Which(fa))?Rotate(x):Rotate(fa);//位异或(^),不同才为1,即之字形转x,一字型转fa Rotate(x); } UpDate(x); rootg = x;//这一句加是为了防止splay(root)这种情况....离谱....这是我自己加的 } //作用:找到中序遍历第order个数 inline int FindOder(int order) { int x = rootg; while(1) { PushDown(x); if(order <= sNodeg[sNodeg[x].son[0]].size) x = sNodeg[x].son[0]; else { order -= sNodeg[sNodeg[x].son[0]].size + 1; if(!order) return x;//当根节点为要求点时就在此处(还未进入右子树)退出 x = sNodeg[x].son[1]; } } } //作用:颠倒中序遍历的[x,y]区间的数 inline void Reverse(int x,int y) { int l = x-1, r = y+1; l = FindOder(l), r = FindOder(r); //解释:由于l肯定小于r,所以目标区间肯定是root(l)的右子树(r)的左子树 Splay(l, 0); Splay(r, l); int pos = sNodeg[rootg].son[1]; pos = sNodeg[pos].son[0]; sNodeg[pos].tag ^= 1; } //作用:中序遍历展示value inline void SplayDfs(int now) { PushDown(now); if(sNodeg[now].son[0]) SplayDfs(sNodeg[now].son[0]); if(sNodeg[now].value!=-inf_ && sNodeg[now].value!=inf_) cout << sNodeg[now].value<<" cnt:"<<sNodeg[now].cnt << " fa:"<<sNodeg[now].fa<<" size:"<<sNodeg[now].size<<endl; if(sNodeg[now].son[1]) SplayDfs(sNodeg[now].son[1]); } //使用指南:这个只是单纯的搜索函数,如果真的使用搜索应当调用search函数,因为根据splayTree,每搜索一次还要splay一下 //作用:找到value所在的结点所对应的nodeNum //参数:x,当前根节点;value:目标值 //返回值:有这么个结点,就返回这个结点的nodeNum,没有就返回NULL //附加返回值:parent(返回的是value对应结点的parent) //前提要求:tree的结构是按照value从小到达的 int SearchValue(int x, valueType_ value, int &fa) { if(x == 0) return 0; if(value < sNodeg[x].value)//在左子树 { fa = x; return SearchValue(sNodeg[x].son[0], value, fa); } else if(sNodeg[x].value < value) { fa = x; return SearchValue(sNodeg[x].son[1], value, fa); } return x;//有这么个数 } //查找函数,带调整功能 //参数:根结点,需要查找的val //返回:true or false bool Search(int x, int value) { int fa = 0; int temp = SearchValue(x, value, fa); if(temp && temp != rootg) { Splay(temp, 0); return true; } return false; } //作用:新填一个值为value下标为nodeNum的结点 //返回值:新建结点的下标 inline int CreatSplayNode(int value) { ++nodeNumg; sNodeg[nodeNumg].fa = 0; sNodeg[nodeNumg].value = value; sNodeg[nodeNumg].cnt = 1; sNodeg[nodeNumg].size = 1; sNodeg[nodeNumg].tag = 0; return nodeNumg; } //作用:插入一个值为value的点,如果该点存在,就cnt++(tree的结构依旧要按照value从小到大来排序) //前提要求:tree的结构是按照value从小到达的 //返回值:返回x inline int SplayInsert(valueType_ value) { int fa = 0;//不可忽略赋值 int x = SearchValue(rootg, value, fa); if(x == 0)//需要插入数据 { x = CreatSplayNode(value); sNodeg[x].fa = fa; if(fa) { if(sNodeg[fa].value > value)//x的value更小就搞成左节点,否则就搞成右节点 sNodeg[fa].son[0]=x; else sNodeg[fa].son[1]=x; UpDate(fa); } } else sNodeg[x].cnt++; Splay(x, 0);//x和他的祖先们都会在这个操作中得到update,所以不用担心 return x; } //功能:中序遍历查找value的前驱(0)或者后继(1) //答案:如果答案没有前驱或者后驱,先插入再删除 inline int NextHalf(int x,int op) { /** //fa根节点,此时x的父节点(存在的话)就是根节点 if(fa && sNodeg[fa].value > value && op && sNodeg[x].son[1]==0) return fa;//如果当前节点的值大于x并且要查找的是后继 if(fa && sNodeg[fa].value < value && !op && sNodeg[x].son[0]==0) return fa;//如果当前节点的值小于x并且要查找的是前驱 //下面三行存疑????? **/ x = sNodeg[x].son[op];//查找后继的话在右儿子上找,前驱在左儿子上找 while(sNodeg[x].son[op^1]) x = sNodeg[x].son[op^1];//要反着跳转,否则会越来越大(越来越小) return x;//返回位置 } inline int Next(int value,int op) { int fa = 0; int x = SearchValue(rootg, value, fa);//从根节点开始访问,找出value==value的结点返回其nodeNum int ans = 0; if(x == 0) { x = SplayInsert(value); Splay(x, 0); ans = NextHalf(x, op); Delete(value); nodeNumg--; } else { Splay(x, 0); ans = NextHalf(x, op); } //cout <<value<<" "<<op<<"ans"<< ans << endl; return ans; } //作用:删除一个值为value的结点 //返回:如果删除成功了true,如果树上根本就没有这个点false(但我觉得这应该不会发生,要不然next哪里找?) bool Delete(valueType_ value) { //下面四行就很妙啊!!要删除一个区间也是这么删除的 int last = Next(value, 0);//查找value的前驱 int next = Next(value, 1);//查找value的后继 Splay(last,0); Splay(next,last); // int x = sNodeg[next].son[0];//后继的左儿子就是x if(sNodeg[x].cnt > 0) { sNodeg[x].cnt--; if(sNodeg[x].cnt) Splay(x, 0); else//这个节点直接丢掉(不存在了) { sNodeg[next].son[0] = 0; Splay(next, 0);//要update一下 } return true; } else return false; } //作用:计算value排序为多少 int CountOder(int value) { int fa = 0; int x = SearchValue(rootg, value, fa);//从根节点开始访问,找出value==value的结点返回其nodeNum int ans; if(x == 0) { x = SplayInsert(value); Splay(x, 0); ans = sNodeg[sNodeg[x].son[0]].size;//加上了自己的1,还得把-inf_减去 Delete(value); nodeNumg--; } else { Splay(x, 0); ans = sNodeg[sNodeg[x].son[0]].size; } return ans; } //作用:找到第k大的数值为多少 //返回值:对应点的value int FindKthNumber(int k) { int x = rootg; if(sNodeg[x].size < k) return 0;//上来就不满足条件 while(1) { int ls = sNodeg[x].son[0]; if(k > sNodeg[ls].size) { if(k > sNodeg[ls].size+sNodeg[x].size) { k -= sNodeg[ls].size+sNodeg[x].size; x = sNodeg[x].son[1]; } else return sNodeg[x].value; } else x = ls; } }
#include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; typedef struct SplayNode *Tree;///typedef就是给A一个新名字B,此处是给struct SPLAY_NODE *新名字为S_NODE typedef int ElementType; struct SplayNode { Tree parent; //该结点的父节点,方便操作 ElementType val; //结点值 Tree lchild; Tree rchild; SplayNode(int val=0) //默认构造函数 { parent=NULL; lchild=rchild=NULL; this->val=val; } }; bool search(Tree &,ElementType); Tree *search_val(Tree&,ElementType,Tree&); bool insert(Tree &,ElementType); Tree left_single_rotate(Tree&,Tree); Tree right_single_rotate(Tree &,Tree ); void LR_rotate(Tree&,Tree ); void RL_rotate(Tree&,Tree ); void right_double_rotate(Tree&,Tree ); void left_double_rotate(Tree&,Tree ); void SplayTree(Tree &,Tree); void up(Tree &,Tree ); Tree *Find_Min(Tree &); void remove(Tree &,ElementType); //查找函数,带调整功能 //参数:根结点,需要查找的val //返回:true or false bool search(Tree &root,ElementType val) { Tree parent=NULL; Tree *temp=NULL; temp=search_val(root,val, parent); if (*temp && *temp!=root) { SplayTree(root,*temp); return true; } return false; } //具体的查找函数 //参数:根,需要查找的val,父节点指针 //成功:返回其结点 //失败:返回其引用,方便后面的插入操作 Tree *search_val(Tree &root,ElementType val,Tree &parent) { if (root==NULL) return &root; if (root->val>val) return search_val(root->lchild,val,parent=root); else if(root->val<val) return search_val(root->rchild,val,parent=root); return &root; } //插入函数 //参数:根,需要插入的val //返回:true or false bool insert(Tree &root,ElementType val) { Tree *temp=NULL; Tree parent=NULL; //先查找,如果成功则无需插入,否则返回该结点的引用。 temp=search_val(root,val,parent); if (*temp==NULL) //需要插入数据 { Tree node=new SplayNode(val); *temp=node; //因为是引用型,所以这里直接赋值,简化了很多了。 node->parent=parent; //设置父节点。 return true; } return false; } //单左旋操作 //参数:根,旋转结点(旋转中心) //返回:当前子树中的新根 Tree left_single_rotate(Tree &root,Tree node) { if (node==NULL) return NULL; Tree parent=node->parent; //其父结点 Tree grandparent=parent->parent; //其祖父结点 parent->rchild=node->lchild; //设置其父节点的右孩子 if (node->lchild) //如果有左孩子则更新node结点左孩子的父节点信息 node->lchild->parent=parent; node->lchild=parent; //更新node结点的左孩子信息 parent->parent=node; //更新原父节点的信息 node->parent=grandparent; if (grandparent) //更新祖父孩子结点的信息 { if (grandparent->lchild==parent) grandparent->lchild=node; else grandparent->rchild=node; } else //不存在祖父节点,则原父节点为根,那么旋转后node为根 root=node; return node; } //单右旋操作 //参数:根,旋转结点(旋转中心) //返回:当前子树中的新根 Tree right_single_rotate(Tree &root,Tree node) { if (node==NULL) return NULL; Tree parent,grandparent; parent=node->parent; grandparent=parent->parent; parent->lchild=node->rchild; if (node->rchild) node->rchild->parent=parent; node->rchild=parent; parent->parent=node; node->parent=grandparent; if (grandparent) { if (grandparent->lchild==parent) grandparent->lchild=node; else grandparent->rchild=node; } else root=node; return node; } //双旋操作(LR型),于AVL树类似 //参数:根,最后将变成子树根结点的结点 void LR_rotate(Tree &root,Tree node) { left_single_rotate(root,node); //先左 right_single_rotate(root,node);//后右 } //双旋操作(RL型),于AVL树类似 //参数:根,最后将变成子树根结点的结点 void RL_rotate(Tree&root,Tree node) { right_single_rotate(root,node); //先右后左 left_single_rotate(root,node); } //两次单右旋操作 //参数:根,最后将变成子树根结点的结点 void right_double_rotate(Tree &root,Tree node) { right_single_rotate(root,node->parent); //先提升其父节点 right_single_rotate(root,node); //最后提升自己 } //两次单左旋操作 //参数:根,最后将变成子树根结点的结点 void left_double_rotate(Tree &root,Tree node) { left_single_rotate(root,node->parent); left_single_rotate(root,node); } //Splay调整操作 void SplayTree(Tree &root,Tree node) { while (root->lchild!=node && root->rchild!=node && root!=node) //当前结点不是根,或者不是其根的左右孩子,则根据情况进行旋转操作 up(root, node); if (root->lchild==node) //当前结点为根的左孩子,只需进行一次单右旋 root=right_single_rotate(root, node); else if(root->rchild==node) //当前结点为根的右孩子,只需进行一次单左旋 root=left_single_rotate(root, node); } //根据情况,选择不同的旋转方式 void up(Tree &root,Tree node) { Tree parent,grandparent; int i,j; parent=node->parent; grandparent=parent->parent; i=grandparent->lchild==parent ? -1:1; j=parent->lchild==node ?-1:1; if (i==-1 && j==-1) //AVL树中的LL型 right_double_rotate(root, node); else if(i==-1 && j==1) //AVL树中的LR型 LR_rotate(root, node); else if(i==1 && j==-1) //AVL树中的RL型 RL_rotate(root, node); else //AVL树中的RR型 left_double_rotate(root, node); } //操作当前子树的最小结点 //返回:其最小结点的引用 Tree *Find_Min(Tree &root) { if (root->lchild) return Find_Min(root->lchild); return &root; } //删除操作 void remove(Tree &root,ElementType val) { Tree parent=NULL; Tree *temp; Tree *replace; Tree replace2; temp=search_val(root,val, parent); //先进行查找操作 if(*temp) //如果查找到了 { if (*temp!=root) //判断是否是根结点,不是根结点,则需要调整至根结点 SplayTree(root, *temp); //调制根结点或者本来就是根结点后进行删除,先查看是否有替代元素 if (root->rchild) { //有替代元素 replace=Find_Min(root->rchild); //找到替换元素 root->val=(*replace)->val; //替换 if ((*replace)->lchild==NULL) //左子树为空 { replace2=*replace; *replace=(*replace)->rchild; //重接其右孩子 delete replace2; } else if((*replace)->rchild==NULL) //右子树为空 { replace2=*replace; *replace=(*replace)->lchild; //重接其左孩子 delete replace2; } } else { //无替代元素,则根直接移向左子树,不管左子树是否为空都可以处理 replace2=root; root=root->lchild; delete replace2; } } } //前序 void PreOrder(Tree root) { if (root==NULL) return; printf("%d ",root->val); PreOrder(root->lchild); PreOrder(root->rchild); } //中序 void InOrder(Tree root) { if (root==NULL) return; InOrder(root->lchild); printf("%d ",root->val); InOrder(root->rchild); } int main() { Tree root=NULL; insert(root, 11); insert(root, 7); insert(root, 18); insert(root, 3); insert(root, 9); insert(root, 16); insert(root, 26); insert(root, 14); insert(root, 15); search(root,14); printf("查找14: "); printf("前序:"); PreOrder(root); printf(" "); printf("中序:"); InOrder(root); printf(" "); // remove(root,16); // remove(root,26); // remove(root,11); remove(root,16); printf("删除16: "); printf("前序:"); PreOrder(root); printf(" "); printf("中序:"); InOrder(root); printf(" "); return 0; }
PART2(算法各种类型(并附上代码))
PART3(算法的延伸应用)
PART4(对算法深度的理解)
PART5(与其相关的有趣题目)