手记

树结构浅析

二分搜索树

向二分搜索树添加元素

我们把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;
 }

二分搜索树的前序遍历


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

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

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

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

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

映射基础(map)

想堆中添加元素和Sift up

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

从堆中取出元素和sift down

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


Trie 字典树(多叉树)

平衡树和AVL

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

 

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


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

旋转操作的基本原理

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

左旋转和右旋转的实现

LR和RL

从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树

2-3树

树的绝对平衡性

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

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

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

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

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

再添加11,添加5

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

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

红黑树与2-3树的等价

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

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

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


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

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

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

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

添加新节点 永远是红色的

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

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

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

左旋转过程如下图:

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

颜色翻转和右旋转

情况1:

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

情况2

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

红黑树中添加新元素

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

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

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

在进行颜色翻转

情况一

情况2

情况3

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

//返回红黑树根节点,并且为黑色
 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;
 }

红黑树的性能测试

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

 

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

 

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

 

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

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

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

与hashmap的不同点:

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

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

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

0人推荐
随时随地看视频
慕课网APP