算法导论思考题13-1:持久动态集合中的持久二叉搜索树

来源:互联网 发布:证件照软件 编辑:程序博客网 时间:2024/06/13 05:36

目标:实现13-1的思考题,要求是使用红黑树进行插入和删除的维护,同时要求这个数据结构是具有full persistence性质(可以对任意时刻的数据结构进行操作,产生多个分支),相关数据结构的操作为插入insert,删除delete两个。

persistent data structures,持久数据结构。其特点是保存了数据结构在某些操作前后的状态。持久数据结构属于temporal data structures 的一种。

最简单的办法是每一次操作都去复制整个树的所有节点。不过13-1给我们提供了一个更好的解决办法,通过发掘每一次操作后数据结构的共性来减少某一操作所带来的消耗。

这里还需要发掘这种方法能带来多少收益。

对于具有红黑树性质的持久二叉搜索树的插入操作来说,插入一个元素会改变插入元素的父节点的左或者右节点的元素域,为了不去损坏插入前的搜索树,那么这就意味着这次插入节点x的父节点xp需要被复制出来,成为一个新的节点。这时候会发现一旦xp节点需要改变那么就会产生连锁效应,视数据结构而定有两点问题:

1.插入节点x的父节点xp的父节点xpp的孩子xp只能有一个,现在xp节点被复制产生了xp1节点,又xp1节点负责x节点的插入,而原来的xp节点所有成员不变。那么对于xpp 节点而言,它的孩子本身是xp,为了不让xp1节点悬空,只能在复制xpp节点,产生xpp1节点指向xp1节点。以此类推产生连锁效应,至少从插入节点位置到根节点都要进行复制。故而有了书上的示意图:

 

此图中插入节点为5,5的父节点xp1 为阴影7节点,xp为7无阴影节点,xpp为8无阴影节点,xpp1为8阴影节点。

2. 第二个问题根据具体数据结构而产生,如果节点中保护parent域,那么还会产生更多的连锁效应,当xp产生复制节点xp1的时候,x如果原来存在兄弟节点xb,那么x的兄弟节点xb原来的parent的域是指向xp的,现在新的树种xp1节点的另一个孩子如果还指向xb的话,那么xb的parent域有不能只想xp1,导致数据结构中的父子关系被破坏,所以xb节点就也要产生一个新的节点xb1,与xp1形成父子关系,以此类推,由于从跟节点到x节点的路径上所有节点都会被复制,那么这些节点的兄弟节点就也会被复制,造成插入的工作时间为O(n),这就回答了思考题中的d,第四个问题。

有着两点可以看出,不去维护红黑树性质并且也不带父节点的持久二叉搜索树的插入性能为O(log n),空间代价也为O(log n)。那么维护红黑树性质又需要多少时间和空间呢?

先来看红黑树插入的维护过程,x被插入后因为是红节点,有可能破坏了新产生的树Tree1的红黑树基本性质4,这时候插入的修复会去改变节点z,和parent,parent->parent,z的兄弟z-brother的颜色或者左右孩子关系。这四个节点在每一次rb-insert-fixup里面的循环中都有可能会被改变,那么就应该去复制这四个节点,而不论在循环的任意时刻z和parent,parent-parent都是已经复制过的节点,那么我们只需要再次复制z的兄弟节点即可。这样整个插入过程的时间消耗为log(n)+log(n),空间消耗为2*log(n)。

使用c来实现,数据结构为

typedef struct node{struct node *left;struct node *right;int color;
int data;int size;} *RBT;
实现这样的插入过程有个难点,没有parent所带来的问题,这个可以用辅助栈来解决。
对于删除操作,情况更加复杂,最大的问题在于没有parent的节点,在删除过程中无法确认其祖先是谁,如果节点的键值会有重复的话,那么就不得不去遍历这些相同键值的子树一确定所要删除的节点的祖先。将删除的过程分解为一下几步:
1.先实现一个不使用哨兵节点作为叶节点的红黑树。
2.再重新跟到删除节点上的parent结点,仅在当前删除过程中有效。沿用插入过程的办法是用辅助栈来记录删除节点的祖先结点。
3.最后在结合persistent性质实现当每次有需要改动的节点的时候就递归复制改变了的结点。
下面是一些实现细节的讨论,先考虑不具有持久性的节点删除方法rb_delete,和相应的修复红黑性质修复过程rb_delete_fixup。
讨论rb_delete之前先说明一下当前的数据结构,首先节点中没有父节点域,还有就是搜索树具有红黑树性质,并且搜索树中没有哨兵节点(也就是叶节点)。这与算法导论上的伪代码是不太相同的。
rb_delete在调用之前搜索树T和删除节点z必须满足如下条件,1,搜索树必须具有红黑树基本性质。2,搜索树中允许存在键值重复的节点,但删除的结点z必须存在,并且有且只能有一个。
rb_delete完成的工作的本质是在当前数据结果下从树T中删除节点z,但是不能使用父节点和哨兵节点,并且维护红黑树性质本身。在算法导论中将删除节点z和维护搜索树的红黑性质两个部分分开实现,于是就有了rb_delete_fixup过程。我应该采用相同的做法,所以rb_delete完成了一下3点工作:
1,将节点z用z右子树的最小的节点y替换,如果z的右子树不存在那么就将z的左子树来替换z(无论z左子树是否存在)
2,还需要锁定一个节点x,x子树是整个搜索树中唯一可能违反红黑树性质的子树,且可能会违反的红黑树性质有三个,1,跟为黑色节点。2,叶节点黑高相同。3,红节点的两个子节点必须为黑节点。同时使一辅助数组存储这节点x的所有祖先节点。
3,使用x来调用rb_delete_fixup来修复搜索树T的红黑性质
当这些工作完成后,现在就需要去进行具有persistent性质的红黑树的删除分析。
和插入的时候一样,我们本来可以每进行一次操作都去重新复制整个搜索树,但是这样的空间开销甚大。这里可以有几种做法能减少时间,在删除过程中两个函数rb_delete和rb_delete_fixup中肯定有某些节点会改变。那么我们可以仍然像之前插入时候的做法一样,只要有某个节点改变了,就递归的去复制所有相关的节点,在删除过程中,和删除修复过程中可以肯定需要改变的节点有以下几种:
1,节点x的所有祖先节点,其中x是在rb_delete中寻找到的,用于替换节点y的节点,且x可能为空节点。
2,x的兄弟节点,这里的x确实在rb_delete_fixup的8种(4*2)情况中都一定会改变。
3. x的兄弟节点的孩子节点(如果存在),这也在rb_delete_fixup中的部分情况会改变。
最简单的识别方法是,当rb_delete和rb_delete_fixup两个函数中一旦要去改变某个节点的成员,那么就复制这个节点。不过这样做会造成重复复制节点,浪费空间。所以需要理论分析节点在什么时候复制会只需要一次复制就能完成。先从rb_delete开始考虑,z和y都必然是节点x的祖先节点,所以即使节点z被删除,节点y改变都在节点x的祖先之中,故而在rb_delete中,我们复制所有的x的祖先节点即可。
在rb_delete_fixup中,无论那种情况,我们都要去改变x的兄弟节点x-brother,所以每次while循环都至少复制一次x的兄弟节点。最好的办法就是每当要去改变节点的内容的时候就去复制这个节点,这是肯定不会发生错误的。但是x的所有祖先节点已经在rb_delete中被复制过了,所以我们在rb-delete_fixup中只负责x的兄弟节点和兄弟节点的孩子节点的复制。在rb_delete_fixup的每一次循环中都去维护这么一个状态,那就是x节点的兄弟节点整个子树在本次删除操作之后就没有被复制过,或者改变过。x的节点子树在本次循环中不再去改变,x的祖先节点全部被复制了,并且它们都存储在辅助栈中。通过分析rb_delete_fixup的代码可以发现确实while循环中实在维护这一性质的。
最后是实践验证了,使用两组10000个随机生成的数据,并且在每次操作之后都使用一个check函数取验证它是否任然具有红黑树的性质,再对比原始的非持久性红黑树在同样数据下的删除或者插入操作,目前并没有发现问题,一下是全部代码。
#include <stdio.h>#include <malloc.h>#include <stdlib.h>#include <time.h>#define BLACK 1#define RED   0#define RBT_NODE RBT#define ROOT(T) (T->left)typedef struct node{struct node *left;struct node *right;struct node *parent;int color;int data;int size;} *RBT;#define RBTREE_INIT(name) {0,0, 0,BLACK,0,0} #define NODE_INIT(name) {0,0,0,RED,0,0};static RBT_NODE garbage[500];static int garbageSize = 0;static RBT_NODE trace[64];static int trace_size;void deleteAll(){for(int i = 0;i<garbageSize;++i)free(garbage[i]);}static int valid;int black_hight;int check(RBT T,RBT_NODE n,int level){if(n == T->left&&level != 1){puts("leaf node problem:n == T->left&&trace_size != 1"); return 0;}if(n==0){int t  = 0;//trace[trace_size] = n;for(int i = 1;i<level;++i){if(trace[i]->color == BLACK){t++;}}if(valid){if(black_hight != t){puts("black_hight not eq");return 0;}}else{valid = 1;black_hight = t;}}else{trace[level] = n;if(n->color == RED){if(n->left==0&&n->right!=0){puts("red node not followd black node 1");return 0;}else if(n->left!=0&&n->right==0){puts("red node not followd black node 2");return 0;}else if(n->left!=0&&n->left->color!=BLACK||n->right!=0 &&n->right->color!=BLACK){puts("red node not followd black node 3");return 0;}}if(!check(T,n->left,level+1)){return 0;}if(!check(T,n->right,level+1)){return 0;}}return 1;}RBT_NODE new_node(RBT_NODE in){if(in == 0) return in;RBT_NODE y = (RBT_NODE)malloc(sizeof(struct node));//garbage[garbageSize++] = y;garbageSize++;//if(garbageSize%100 == 0)//printf("garbageSize = %d\n",garbageSize);y->left = in->left;y->right = in->right;y->data = in->data;y->color = in->color;y->size = in->size;}RBT_NODE  copy_node(RBT_NODE  in,RBT_NODE parent){RBT_NODE temp = new_node(in);if(in == parent->left){parent->left = temp;}else{parent->right = temp;}return temp;}void left_rotate(RBT T,RBT_NODE x){/*旋转分析:完成树的分支的旋转,并维护树的本身特性。不改变trace栈。进入条件trace栈顶存储着x的父亲。*///puts("left_rotate in");if(x == 0||trace[trace_size-1]->left != x&&trace[trace_size-1]->right!=x){puts("error left_rotate ");}//x != NullRBT_NODE parent = trace[trace_size - 1];//parent为x的父母,x!=Nullstruct node * y = x->right;//parent为x的父母,x!=Null,y等于x->rightif(y == 0) return ;//parent为x的父母,x!=Null,y等于x->right且y不为空。x->right = y->left;//int temp = y->size;y->size = x->size;x->size-=temp;if(y->left != 0){x->size += y->left->size;}y->left = x; if(x == parent->left){parent->left = y;}else{parent->right = y;}//puts("left_rotate out");}void right_rotate(RBT T,RBT_NODE x){//puts("right_rotate in");if(x == 0||trace[trace_size-1]->left != x&&trace[trace_size-1]->right!=x){puts("error right_rotate ");}RBT_NODE parent = trace[trace_size - 1];struct node *y = x->left;if(y == 0) return ;x->left = y->right;int temp = y->size;y->size = x->size;x->size -= temp;if(x->left != 0){x->size += x->left->size;}y->right = x;if(parent->left == x){parent->left = y;}else{parent->right = y;}//puts("right_rotate out");}void rb_insert_fixup(RBT T,RBT_NODE z){//printf("T->left->data = %d\n",T->left->data);RBT_NODE y;RBT_NODE temp;RBT_NODE parent = trace[--trace_size];while(parent->color == RED){RBT_NODE pparent = trace[trace_size-1];if(parent == pparent->left){y = pparent->right;if(y!=0&&y->color == RED){//因为这里要去改变y的颜色所以也不能和一前公用节点,//不然会破坏原来的树parent->color = BLACK;temp = new_node(y);pparent->right = temp;z = pparent;pparent->color = RED;temp->color = BLACK;trace_size--;}else{if(z == parent->right){RBT_NODE temp = z;z = parent;left_rotate(T,z);parent = temp;}parent->color = BLACK;pparent->color = RED;trace_size --;right_rotate(T,pparent);break;}}else{y = pparent->left;if(y!=0&&y->color ==RED){parent->color = BLACK;z = pparent;pparent->color = RED;temp = new_node(y);pparent->left = temp;temp->color = BLACK;trace_size--;}else{if(z == parent->left){RBT_NODE temp = z;z = parent;right_rotate(T,z);parent = temp;}parent->color = BLACK;pparent->color = RED;trace_size --;left_rotate(T,pparent);break;}}parent = trace[--trace_size];}T->left->color = BLACK;}RBT  rb_insert(RBT T,RBT_NODE z){    RBT temp,newT = new_node(T);trace_size = 1;    trace[0] = newT;    newT->size++;RBT_NODE y = newT;RBT_NODE x = newT->left;while(x != 0){temp = new_node(x);temp->size++;trace[trace_size] = temp;trace_size++;if(x == y->left){y->left = temp;}else{y->right = temp;}y = temp;if(z->data < x->data){x = x->left;}else{x = x->right;}}if(y == newT){newT->left = z;}else if(z->data < y->data){y->left = z;}else{y->right = z;}z->left = 0;z->right = 0;z->color= RED;z->size = 1;//printf("i am fine");rb_insert_fixup(newT,z);return newT;}void rb_transplant(RBT_NODE u,RBT_NODE v){RBT_NODE parent = trace[trace_size-1];if(u==parent->left){parent->left = v;}else parent->right = v;//v->parent = u->parent;//copy_node(v,u);//free(v);}RBT_NODE tree_minimum(RBT T,RBT_NODE x){RBT_NODE p = x;while(p->left != 0){trace[trace_size++] = p;p = p->left;}return p;}void rb_delete_fixup(RBT T,RBT_NODE x){RBT_NODE w,parent;//当x不为跟节点,且x的颜色为黑或者x为空节点时候while(x != T->left && (x==0 || x->color == BLACK)){//parent始终为x父亲,trace中始终为parent的祖先节点// w始终跟踪x的兄弟 parent = trace[--trace_size];if( x == parent->left){w = parent->right;w = copy_node(w,parent);if(w->color == RED){if(w->left == 0 ||w->right == 0)puts("total wrong w->left == 0 ||w->right == 0 in rb_delete_fixup 1");w->color = BLACK;parent->color = RED;left_rotate(T,parent);trace[trace_size++] = w;w = parent->right;if(w == 0){puts("error here");}w = copy_node(w,parent);}//现在w的颜色必为黑色,且w不为空节点。if((w->left == 0 || w->left->color == BLACK) &&(w->right == 0|| w->right->color ==BLACK)){w->color = RED;x = parent;}else {if(w->right == 0 || w->right->color == BLACK){//if(w->left != 0){w->left = copy_node(w->left,w);w->left->color = BLACK;//}w->color = RED;trace[trace_size++] = parent;right_rotate(T,w);trace_size--;w = parent->right;}w->color = parent->color;parent->color = BLACK;if(w->right->color != RED){puts("something wrong");}w->right = copy_node(w->right,w);w->right->color = BLACK;left_rotate(T,parent);x = T->left;}}else{w = parent->left;w = copy_node(w,parent);if(w->color == RED){if(w->left == 0 ||w->right == 0)puts("total wrong w->left == 0 ||w->right == 0 in rb_delete_fixup 2");w->color = BLACK;parent->color =RED;right_rotate(T,parent);trace[trace_size++] = w;w = parent->left;if(w == 0){puts("error here");}w = copy_node(w,parent);}if((w->left == 0 || w->left->color == BLACK) && (w->right == 0|| w->right->color ==BLACK)){//x->parent->color = BLACK;w->color = RED;x = parent;}else{if(w->left == 0 || w->left->color == BLACK){w->right  =copy_node(w->right,w);w->right->color = BLACK;w->color = RED;trace[trace_size++] = parent;left_rotate(T,w);trace_size--;w = parent->left;}w->color = parent->color;parent->color = BLACK;w->left = copy_node(w->left,w);w->left->color = BLACK;right_rotate(T,parent);x = T->left;}}}if(x != 0)x->color = BLACK;}int search_node(RBT_NODE root,RBT_NODE z,int level){/*关于search——node的有效性证明,root代表整个树的根节点,z代表需要搜索的节点,level代表当前搜索的root节点的深度并把结果存储在全局栈root中。对于当前搜索的深度level而言,trace中level以前的所有节点都是root的祖先节点。那么无论当前到何处,只要root不为零,且root不等于 z那么给trace加入root节点作为z的父节点是正确的,*/if(root != 0){trace_size = level;if(root->data == z->data){if(z==root) return 1;else {trace[level] = root;if(search_node(root->right,z,level+1)) return 1;if(search_node(root->left,z,level+1)) return 1;}}else if(root->data < z->data){trace[level] = root;return search_node(root->right,z,level+1);}else{trace[level] = root;return search_node(root->left,z,level+1);}}return 0;}RBT rb_delete(RBT T,RBT_NODE z){RBT re;if(z == T){puts("cann't delete the tree");}//trace_size = 1;//用以找到z的祖先节点if(!search_node(T->left,z,1)){puts("why i can't search it");return 0;}//完成z的祖先节点的复制re = trace[0] = new_node(T);for(int i = 1;i<trace_size;++i){trace[i] = copy_node(trace[i],trace[i-1]);}//现在尝试检查search node效果RBT_NODE y =z;RBT_NODE x,temp;int y_original_color = y->color;if(z->left == 0){//此时y=z,x为z的孩子//完成x和x的祖先节点的复制x = z->right;rb_transplant(z,x);}else if(z->right == 0){//完成x和x的祖先节点的复制x = z->left;//x = new_node(temp);rb_transplant(z,x);}else{int temp = trace_size;//试图向trace中添加ztrace[trace_size++] = z;y = tree_minimum(T,z->right);//for(int i = 0;i<trace_size;i++){//printf("%p ->left = %p ->right = %p \n",trace[i],trace[i]->left,trace[i]->right);//}y_original_color = y->color;x = y->right;if(trace[trace_size-1] != z){for(int i = temp;i<trace_size;++i){trace[i] = copy_node(trace[i],trace[i-1]);}y = copy_node(y,trace[trace_size-1]);rb_transplant(y,y->right);y->right = z->right;//y->right->parent = y;}elsey = new_node(y);int k = trace_size;trace_size = temp;rb_transplant(z,y);trace[trace_size] = y;trace_size = k;y->left = z->left;//y->left->parent = y;y->color = z->color;}if(y_original_color == BLACK)rb_delete_fixup(re,x);return re;}void print(RBT T,RBT_NODE N){if(N != 0){printf("data = %d color = %d size = %d ",N->data,N->color,N->size);if(N->left!=0)printf("N->left->data = %d ", N->left->data);if(N->right!=0)printf("N->right->data = %d ", N->right->data);puts("");print(T,N->left);print(T,N->right);}}int main(){struct  node T = RBTREE_INIT(&T);RBT_NODE in[10000];RBT_NODE tree = &T;RBT_NODE tree_5k,tree_use;freopen(".//data1.txt","r",stdin);freopen(".//out21.txt","w",stdout);for(int i = 0;i< 10000;i++){in[i] = (RBT_NODE)malloc(sizeof(struct node));//in[i]->data = rand()%100000;scanf("%d",&in[i]->data);//printf("%d ",in[i]->data);tree = rb_insert(tree,in[i]);//puts("-----------------------------------------------------------------");trace[0] = tree;valid = 0;if(!check(tree,tree->left,1)){puts("error");print(tree,tree->left);return 0;}if(i == 5000){tree_5k = tree;//print(tree,tree->left);}}tree_5k = tree;///puts("-----------------------------------------------------------------");for(int i = 0;i<10000;++i){//printf("i = %d\n",i);rb_delete(tree,tree->left);trace[0] = tree;valid = 0;if(!check(tree,tree->left,1)){puts("error");//print(tree,tree->left);return 0;}if(i == 5000){ tree_use = tree;}}print(tree_5k,tree_5k->left);print(tree_use,tree_use->left);return 0;}


原创粉丝点击