《算法导论》笔记(6)链表到红黑树 部分习题

习题10.2-8, 记第i个元素的np为np1,np2… 则np0xor np1 xor np2 xor np3 ….npi得到npixor np0,np0是表头的地址。若定义np0为0x00000000,(np1xor np2 xor np3…. xor npi)即为第i+1个元素的相对地址。若要逆转链表,则查找时从最末位向前连续xor即可。

习题10.4-5 二叉树那章有道题相同,不用栈不用递归遍历。访问方法是几条移动规则:1,到底则向上移,2,向右上则输出1个元素z并移动到minimum(z.right),3,向左上不输出,4,一个状态指示怎么走,前进或者后退状态,不同条件下翻转。

minimum(x){
 if x.right!=null x=x.right;
 if x.left!=null x=x.left;
 while(x.left) x=x.left; return x;
}
walk(x){
 x=minimum(x);
 direction=forward;
 while(x!=root){
 if(direction==forward) visit(x); 
 if(x.right==null){direction=backward; continue;}
 if(x==x.p.left && direction==forward) {if(x.right!=null) x=minimum(x); else x=x.p; continue;}
 if(x==x.p.left && direction==backward) {x=x.p; direction=forward; continue;}
 if(x==x.p.right && direction=backward) {x=x.p; continue;}
 if(x==x.p.right && direction==forward) {x=minimum(x); continue;}
 }
}

习题10.4-6 左孩子右兄弟表示法需要3个指针,前驱,后继,孩子,那么只用2个指针的方法就是将前驱和后继异或,布尔变量表示第一个兄弟,它的指针是父指针与弟指针的异或。遍历方法同10.2-8

思考题12-1 相同关键字的二叉搜索树。红黑树中的应用就是mulitset和multimap。如果是随机在x.left或x.right插入相同元素,那么随着旋转操作,可能出现相同元素并不连接在一起的问题。搜索x得到第一个x,为了搜索后续的x,需要递归搜索x的左子树和右子树。那么最差性能是O(mi*lg(n)),m是重复元素的个数。如果是在重复元素处加一个表记录所有相同重复元素的位置,那么每次需更新全部的表,开销更大。

思考题12-2 基数树。想起了哈夫曼编码。

习题13.3-6 没有父指针的红黑树插入过程。猜测方法是用search_p(k)函数找到父结点,并判断是父结点的左孩子还是右孩子。

习题13.4-7 插入时,考察插入元素及其父叔的情况,删除是逆过程,而且只要不再插入新的元素,删除后的树与原来保持一致

思考题13-3 AVL树。左右子树高度差大于1,则右旋或左旋。左子树hl,其左右枝为hll,hlr,右子树hr,其左右枝为hrl,hrr。左旋时,hll与hlr高度+1,hrl高度-1,hrr高度-2,所以对于左低右高的情况,若hrl高于hrr则先右旋右子树保证hrl不高于hrr,然后左旋。右旋时同理。

balance(x){
 if(x.l.h<x.r.h){
  if(x.r.l.h>x.r.r.h) right_rotate(x.r);
  left_rotate(x);
 }
 if(x.l.h>x.r.h){
  if(x.l.l.h<x.l.r.h) left_rotate(x.l);
  right_rotate(x); 
 }
}
insert(x,k){
 x=find_and_add(x,k);
 while(x!=null) {x=x.p; balance(x);};
}
find_and_add(x,k){
 if(x.r==null && k>x.value) {x.r.add(k); x=x.r;}
 elseif(x.l==null && k<x.value) {x.l.add(k); x=x.l;}
 return x;
 if(k>x.value) find_and_add(x.r, k);
 if(k<x.value) find_and_add(x.l, k);
}

思考题13-4 treap树。插入后,检查x与x.p的priority,若需要交换,x在左则右旋,x在右则左旋。
balance(x){
 if(x=x.p.right && x.priority>x.p.priority) left_rotate(x.p);
 if(x=x.p.left && x.priority>x.p.priority) right_rotate(x.p); 
}
insert(x,k){
 x=find_and_add(x,k);
 while(x!=root) balance(x);
}
但是如果一种方法,不把新元素加入到最底层,而是直接插入到两个元素之间,x.p.priority<z.priority<x.priority,那么将z插入到x.p与x之间,并调整z、x、x.p的左右关系。插入时不需要旋转。

insert(x,k){
 while(k.priority>x.priority){
  if(k.value>x.value) {if(x.right) {x=x.right; continue;} else{x.add_right(k); return;}}
  elseif(k.value<x.value) {if(x.left) {x=x.left; continue;} else{x.add_left(k); return;}}
 }
 if(x==x.p.left) {
  if(k>x.value) { x.p.add_left(k); k.add_left(x); return;}
  elseif(k<x.value) { x.p.add_right(k); k.add_right(x); return;}
 }
 if(x==x.p.right) {
  if(k>x.p.value){ x.p.add_right(k); k.add_left(x); return;}
  elseif(k<x.p.value){ x.p.add_right(k); k.add_right(x); return;}
 } 
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值