继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

树结构浅析

爱与我同在
关注TA
已关注
手记 16
粉丝 15
获赞 84

二分搜索树

https://img4.mukewang.com/5bd04f0b0001cdb405580292.jpg

https://img1.mukewang.com/5bd04f3c0001ba5305580303.jpg

向二分搜索树添加元素

我们把null 也理解为一个数的话,我们就不需要考虑null这个特殊的情况。如果我们插入的node为空,我们插入新元素之后就会产生新的节点,就是new node(e),之后就是比较。小于0插入左子树,大于0插入右子树。等于0什么都不干,无论向那插入元素,我们都会重新复制左子树和又子树。

public  void add(E e){
   root=add(root,e);
 }
 //递归函数
 private Node add(Node node,E e){
     //首先看递归终止条件
     if(node==null){
         size++;
         return new Node(e);
     }
     //递归调用
     if(e.compareTo(node.e)<0){
        node.left=add(node.left,e);
     }else if (e.compareTo(node.e)>0){
        node.right= add(node.right,e);
     }
     return node;
 }

二分搜索树的前序遍历


https://img.mukewang.com/5bd051a000016ae805580217.jpg

https://img3.mukewang.com/5bd051ba0001313c05580178.jpg

https://img4.mukewang.com/5bd051c10001ba3b05580214.jpg

https://img1.mukewang.com/5bd051cf0001cee705540257.jpg

https://img2.mukewang.com/5bd051d70001d9c605520221.jpg

二分搜索树前序遍历的非递归实现

又叫做深度优先遍历-----利用数据结构 栈

https://img3.mukewang.com/5bd051eb00015fa005580315.jpg

又称为广度优先遍历----利用数据结构  队列

https://img.mukewang.com/5bd052030001244005580241.jpg

https://img2.mukewang.com/5bd0520d0001468e05570267.jpg

删除而分搜索树的任意元素

https://img4.mukewang.com/5bd052540001da3805580218.jpg

https://img2.mukewang.com/5bd0526900018c1005580225.jpg

集合基础和基于二分搜索树的集合实现

https://img3.mukewang.com/5bd0527e000194f005580166.jpg

https://img1.mukewang.com/5bd0528b0001928605440371.jpg

https://img.mukewang.com/5bd052a20001247a05580335.jpg

映射基础(map)

https://img1.mukewang.com/5bd052ba0001b6bb05500349.jpg

https://img.mukewang.com/5bd052ca0001f1b205290373.jpg

https://img3.mukewang.com/5bd052df0001bc7305570256.jpg

https://img.mukewang.com/5bd052fc00019f2c05580227.jpg

https://img4.mukewang.com/5bd0530d00012ddf05580248.jpg

https://img4.mukewang.com/5bd0532700019dde05580236.jpg

想堆中添加元素和Sift up

首先在数组最后的位置添加一个元素,现在不满足堆定义,所以需要调整,只需要从新添加节点,向上比较就可以了。

https://img2.mukewang.com/5bd0534100019ad805570217.jpg

从堆中取出元素和sift down

 取元素是取堆顶的元素,之后将数组最后一个元素放到堆顶,此时不符合堆的定义,所以要将新放入堆顶元素和他的孩子节点比较(那个大和那个比)

https://img.mukewang.com/5bd0535200015d6305580231.jpg


Trie 字典树(多叉树)

https://img3.mukewang.com/5bd053610001852705570237.jpg

https://img2.mukewang.com/5bd05372000187ef05580154.jpg

https://img2.mukewang.com/5bd0537a0001b2f205580261.jpg

https://img4.mukewang.com/5bd0538100010a2505580333.jpg

平衡树和AVL

https://img1.mukewang.com/5bd0538d00012d4505580307.jpg

标注高度2节点是叶子节点 高度为1  4节点左子树高度为2,3节点高度为3,7节点高度为1.

 

平衡因子就是左子树高度减右子树高度,2因为是叶子节点,所以平衡因子是0,4节点左子树高度是1,右子树是0 所以平衡因子是1,5节点做子树高度是2,右子树高度是1,所以平衡因子是1.


检查二分搜索树性质和平衡性

旋转操作的基本原理

https://img1.mukewang.com/5bd053e600012f5805170274.jpg

https://img4.mukewang.com/5bd053f70001a71005170238.jpg

https://img2.mukewang.com/5bd0541100011a8105060212.jpg

将相当于 将y这个节点顺时针的向右旋转。这样得到新的树 既满足二分搜索树又满足平衡树。

左旋转和右旋转的实现

https://img4.mukewang.com/5bd054230001cb2c05570137.jpg

https://img3.mukewang.com/5bd05429000199c505580223.jpg

LR和RL

https://img.mukewang.com/5bd054340001f3e605440142.jpg

https://img4.mukewang.com/5bd0543b000133fb05480191.jpg

https://img3.mukewang.com/5bd054400001248305790215.jpg

https://img1.mukewang.com/5bd0544400013f5806020248.jpg

https://img4.mukewang.com/5bd054490001eb3a06020197.jpg

从AVL树中删除元素

// 删除以node为根的二分搜索树中值为e的节点,递归算法
 //返回删除节点删除后新的二分搜索树的根
 private Node remove(Node node, K key) {
     //递归到底
     if(node==null){
         return null;
     }
     Node retNode;
     if(key.compareTo(node.key)<0){
         node.left= remove(node.left,key);
         retNode=node;
     }else if (key.compareTo(node.key)>0){
         node.right= remove(node.left,key);
         retNode=node;
     }else {
         if(node.left==null){
             Node rightNode=node.right;
             node.right=null;
             size--;
             retNode=rightNode;
         }
         else if (node.right==null){
             Node leftNode=node.left;
             node.left=null;
             size--;
             retNode=leftNode;
         }else {
             //待删除节点左右子树军不为null
             //找到比待删除节点大的最小节点,级待删除节点右子树的最小节点
             //用这个节点顶替待删除节点的位置
             Node successor=minimun(node.right);
             successor.right=remove(node.right,successor.key);
             successor.left=node.left;
             node.left=node.right=null;
             retNode=successor;
         }
         
     }
     
     if(retNode==null){
         return null;
     }
 
     //更新height  就是1+左右子树最大height值
     retNode.height=1+Math.(getHeight(retNode.left),getHeight(retNode.right));
 
     //计算平衡因子
     int balanceFactor=getBalanceFactor(retNode);
 
     //平衡的维护
     //LL
     if(balanceFactor>1&& getBalanceFactor(retNode.left)>0){//需要进行右选装
         return rightRotate(retNode);
     }
     //RR
     if (balanceFactor<-1&&getBalanceFactor(retNode.right)<=0) //就是右子树比左子树高,并且高度差比一大,对于节点来说 他的右子树平衡因子是小于等于0的(就是向右倾斜)
     {
         return leftRoate(retNode);
     }
     //LR
     if(balanceFactor>1&&getBalanceFactor(retNode.left)<0){
         //对当前节点左孩子进行左旋转
         retNode.left= leftRoate(retNode.left);
         return rightRotate(retNode);
     }
     //RL
     if(balanceFactor<-1&&getBalanceFactor(retNode.right)>0){
         //对当前节点左孩子进行左旋转
         retNode.right= rightRotate(retNode.right);
         return leftRoate(retNode);
     }
     return retNode;
 
 }


红黑树与2-3树

https://img1.mukewang.com/5bd0546b000190c605260170.jpg

2-3树

https://img1.mukewang.com/5bd058d80001d65605520278.jpg

https://img2.mukewang.com/5bd05921000182f005520214.jpg

树的绝对平衡性

添加节点:从根节点出发添加节点,2-3树添加节点永远不会添加到空的位置。首先添加42节点,再添加37,根据二分搜索树特性,37小于42所以应该添加到左子树中,但是左子树为空,所以新节点应该融合到最后的叶子结点(现在就是42这个节点),42本省是2节点,经过融合变成了3节点,在添加12节点,因为12小于37所以应该添加到37左子树中,但由于左子树为空,所以找最后一个叶子节点融合,12,37,42 此时就变成4节点了,但2-3不存在4节点,所以,此时需要分裂。如下图

https://img4.mukewang.com/5bd05964000111d503120142.jpg

此时添加18,从根节点开始,18小于37,所在看左子树,18大于12,所以应该放到12的

右子树中,但右子树为空,所以进行融合,再添加节点6,有需要融合形成4节点,在进行分裂,

https://img.mukewang.com/5bd0598a0001054303290186.jpg

如果和根节点分裂一样,那么此时2-3树就不是绝对平衡树,因为根节点到叶子节点的高度不一样了。

https://img3.mukewang.com/5bd059950001005803690137.jpg

为了平衡,我们需要把分裂的非叶子节点向上融合,如下图

https://img1.mukewang.com/5bd059ab0001a7e904750145.jpg

再添加11,添加5

https://img4.mukewang.com/5bd059b50001e54a05770177.jpg

https://img4.mukewang.com/5bd059bb0001cb8e05930186.jpg

此时出现4节点了所以需要分裂  平切向上融合

https://img2.mukewang.com/5bd059d200011c2205140179.jpg

https://img2.mukewang.com/5bd059dd00013e9b05180150.jpg

此时又出现4节点,需要分裂,由于是跟界定所以不用向上融合

https://img1.mukewang.com/5bd059ec00010dbc05460227.jpg

红黑树与2-3树的等价

https://img.mukewang.com/5bd059f70001daa905570225.jpg

3节点在红黑树中用一条红遍相连表示

https://img1.mukewang.com/5bd05a0e0001c44705570244.jpg

我们没有必要记录那个是红边那个是黑边,由于和父亲节点相连的边只有一条,所以我们把对应的节点表示为红色的。b节点和c节点在原来2-3树中时3节点。

https://img4.mukewang.com/5bd05a330001083105580217.jpg

https://img1.mukewang.com/5bd05a3d0001ad4505580227.jpg

https://img3.mukewang.com/5bd05a430001896b05570169.jpg

红黑树的基本性质和复杂度分析

https://img2.mukewang.com/5bd05a4c000106ff05580187.jpg

保持跟节点为黑色和左旋转

2-3树添加节点永远不会添加到空节点,只会和原有节点融合。

如果融合的节点是2节点,那么直接形成一个3节点

如果融合的节点是3节点,那么也直接融合形成一个4节点,然后在向上分裂。

添加新节点 永远是红色的

从最初是情况添加,首先添加42,我们让我们的红黑树的根为42,并且让跟节点变成黑色,

在插入新节点37,根据二分搜索树的规则,需要插入到42的做子树位置,并且是红节点,此时也符合红黑树的定义。

https://img.mukewang.com/5bd05a8d000144b903860147.jpg

红节点添加到黑节点左侧非常容易,但是根据二分搜索树的原则,新节点有可能添加到二分搜索树的如下图,我们新添加元素在黑节点右侧,现在显然不符合红黑树基本定义(红黑树中所有红节点都是向左倾斜的),所以我们需要左旋转。

https://img1.mukewang.com/5bd05a9f0001b49e05150121.jpghttps://img3.mukewang.com/5bd05aa70001641205290126.jpg

左旋转过程如下图:

  首先node的右孩子等于X的左子树,在让X左子树等于node,之后在维护节点颜色,X的颜色等于node的颜色,node的颜色应该为红色。

https://img3.mukewang.com/5bd05aba0001d9c805430143.jpg

https://img3.mukewang.com/5bd05abf0001682f05570157.jpg

https://img1.mukewang.com/5bd05ac40001aba605580159.jpg

颜色翻转和右旋转

情况1:

颜色的翻转:我们看添加一个新元素,相当于在2-3树中在3节点添加元素,之后向上分裂,根节点需要向上融合,所以根节点为红色,叶子节点变为黑色。

https://img4.mukewang.com/5bd05ad80001f42605580138.jpg

https://img.mukewang.com/5bd05af30001320705580145.jpg

情况2

添加新元素 12  两个红节点在42的左侧,我们现在要实现右旋转,首先42的左孩子指向T1,37的右孩子指向42,在调整相应颜色,37由于代替了原来的42的节点位置,所以37颜色变为原来42的颜色,由于他们还是3节点结构所以42变为红色,在按情况1中进行颜色翻转。

https://img3.mukewang.com/5bd05b1b0001400605580244.jpg

https://img2.mukewang.com/5bd05b220001f11905580233.jpg

https://img3.mukewang.com/5bd05b280001b2de05580203.jpg

红黑树中添加新元素

如果出现以下情况,相当于往3节点的中间位置添加元素。

https://img2.mukewang.com/5bd05b390001774805580201.jpg

首先基于37这个节点左旋转

https://img.mukewang.com/5bd05b440001730504170139.jpg

在针对42节点在进行右旋转

https://img2.mukewang.com/5bd05b4e0001df3204130129.jpg

在进行颜色翻转

https://img1.mukewang.com/5bd05b590001ad2304160128.jpg

情况一

https://img1.mukewang.com/5bd05b620001adcd05580136.jpg

情况2

https://img3.mukewang.com/5bd05b6c00018ef105580185.jpg

情况3

https://img2.mukewang.com/5bd05b7800012b0205580197.jpg

所以红黑树的添加都可以用着一个逻辑链条完成

https://img2.mukewang.com/5bd05b820001c14605580221.jpg

//返回红黑树根节点,并且为黑色
 public void add(K key,V value) {
     root = add(root, key,value);
     root.color=;
 }
 
 
 
 //递归函数
 private Node add(Node node, K key,V value) {
     //首先看递归终止条件
     if (node == null) {
         size++;
         return new Node(key,value);
     }
     //递归调用
     if (key.compareTo(node.key) < 0) {
         node.left = add(node.left, key,value);
     } else if (key.compareTo(node.key) > 0) {
         node.right = add(node.right, key,value);
     }else {//相等
         node.value=value;
     }
 
     //判断是否需要左旋转
     if(isRed(node.right)&&!isRed(node.left)){
         node=leftRtate(node);
     }
     //判断是否需要右旋转
     if(isRed(node.left)&&isRed(node.left.left)){
         rightRotate(node);
     }
     //判读是否需要颜色翻转
     if (isRed(node.left)&&isRed(node.left)){
         flipColors(node);
     }
     return node;
 }

红黑树的性能测试

https://img3.mukewang.com/5bd05b9a00015d8205580248.jpg

在这个测试用例中 红黑树并不是最优,原因有一下几个

 

1.用例样本数太小,对于比较简单或少的数据,简单算法才是最优

 

2.红黑树并不是严格的平衡树,从根到叶子节点,高度最多有可能达到2倍的longN,是比AVL树要高一些的,所以在查询操作中红黑树并不占优势,红黑树真正占优势的操作是添加和删除的操作

 

我们只看添加操作的测试用例

https://img3.mukewang.com/5bd05bb800014dd505570233.jpg

https://img1.mukewang.com/5bd05bbe0001868704980147.jpg

https://img2.mukewang.com/5bd05bd30001a24905440132.jpg

ConcurrentHashMap(jdk7,基于分段锁处理的)

ConcurrentHashMap 底层结构任然是数组和链表,与HashMap不同的是最外层不是一个大数组而是一个Segment 数组

https://img4.mukewang.com/5bd05beb0001553b05580343.jpg

与hashmap的不同点:

hashmap  线程不安全,如许key和value为空,不容许在遍历的时候修改

ConcurrentHashMap 线程安全 不如许key和value为空,如许遍历的时候修改,并且跟新对后面的遍历可见

java8下优化的ConcurrentHashMap  将底层数结构中的链表用了红黑树来提高并发性(默认并发数达到8时将链表变为红黑树)

https://img1.mukewang.com/5bd05c070001652c05430327.jpg

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP