堆
定义
某个节点的值总是不大于或不小于其父节点的值的一棵完全二叉树。堆本质上是一个数组,利用逻辑关系构成了一棵树。其实现的排序性能达到了O(nlgn)
。
应用场景
堆排序、Java中的PriorityQueue
实现
public class Head{
private static int caculateLeftChildIndex(int index){
return (index <<1 )+1;
}
private static int caculateRightChildIndex(int index){
return (index <<1 )+2;
}
private static int caculateParentIndex(int index){
return (index -1 )>>1;
}
public static void headSort(int []array){
for(int i=array.length-1 ;i>0;i--){
swap(array, 0, i);
downHead(0, array, i);
}
}
public static void buildHead(int []array){
for(int i=(array.length-1)/2 ;i>=0;i--){
downHead(i, array, array.length);
}
}
public static void upHead(int index,int []array,int length){
int parentIndex=caculateParentIndex(index);
if(parentIndex >= 0 && array[parentIndex] < array[index]){
swap(array,parentIndex,index);
index=parentIndex;
upHead(index, array, length);
}
}
public static void downHead(int index,int []array,int length){
int leftChildIndex=caculateLeftChildIndex(index);
int rightChildIndex=caculateRightChildIndex(index);
int largestIndex=index;
if(leftChildIndex < length && array[largestIndex] < array[leftChildIndex]){
largestIndex=leftChildIndex;
}
if(rightChildIndex < length && array[largestIndex] < array[rightChildIndex]){
largestIndex=rightChildIndex;
}
if(largestIndex != index){
swap(array, largestIndex, index);
downHead(largestIndex, array,length);
}
}
private static void swap(int []array,int i,int j){
if(i == j){
return;
}
array[i]=array[i]^array[j];
array[j]=array[i]^array[j];
array[i]=array[i]^array[j];
}
public String toString(int []array){
return Arrays.toString(array);
}
public static void main(String []args){
int []array={9,7,6,3,5,2,1,8};
//upHead(7, array, array.length);
buildHead(array);
headSort(array);
System.out.println(Arrays.toString(array));
}
}
哈夫曼树
定义
给定n个权值作为n个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,又称哈夫曼树。特点是权值大的离根节点近。
应用场景
文件的压缩
实现
采用自底向上的方式进行构建,每次选取两个权值最小的节点构成一颗新的树,新树的新权值需要参与接下来树的构建,直到所有的节点构建完毕:
public class HuffmanTree {
private Queue<Node> queue;
private int[] array;
private final int DEFALUTSIZE=1<<5;
private int size;
private Node first;
public HuffmanTree(){
queue=new PriorityQueue<>();
array=new int[DEFALUTSIZE];
}
public void insert(int value){
this.array[size]=value;
size++;
}
public void preOrder(Node node){
if (node==null){
return;
}
System.out.print(node.value+" ");
preOrder(node.leftChild);
preOrder(node.rightChild);
}
public Node createHuffmanTree(){
for (int i=0;i<size;i++){
queue.offer(new Node(array[i]));
}
while (queue.size()>1){
Node node1=queue.poll();
Node node2=queue.poll();
Node parent=new Node(node1.value+node2.value);
parent.leftChild=node1;
parent.rightChild=node2;
node1.parent=parent;
node2.parent=parent;
queue.offer(parent);
}
first=queue.poll();
return first;
}
public void enCode(Node root,String encode){
if (root.rightChild==null && root.leftChild==null){
System.out.println(root.value+"被编码为:"+encode);
}
if (root.leftChild!=null){
encode+="0";
enCode(root.leftChild,encode);
int length=encode.length();
encode=encode.substring(0,length-1);
}
if (root.rightChild!=null){
encode+="1";
enCode(root.rightChild,encode);
int length=encode.length();
encode=encode.substring(0,length-1);
}
}
class Node implements Comparable{
Node leftChild;
Node rightChild;
Node parent;
int value;
Node(int value){
this.value=value;
}
@Override
public int compareTo(Object o) {
Node node=(Node)o;
if (node.value>this.value){
return -1;
}else if (node.value<this.value){
return 1;
}
return 0;
}
}
}
二叉搜索树
定义
所有节点的左孩子比它小,右孩子比它大的一颗二叉树,可以包含等于的情况。一般用作搜索。缺点是如果插入的顺序不当,那么二叉搜索树会退化成线性操作,而平衡树以及红黑树就是为了防止该种情况出现的。
删除
二叉搜索树的删除是找到要被删除的节点的前驱(左孩子里最大的)或者后继(右孩子里最小的)的值来替换它,然后删除原来的前驱或者后继所在的节点,如图:
AVL树
定义
AVL树是一个高度平衡的二叉搜索树,是平衡树的一种,其最短路径与最长路径的绝对值之差不大于1。缺点是为了保持高度的平衡性而造成其插入性能比不上其它局部不平衡而整体平衡的红黑树,但是查找性能是比后者好的。
应用场景
windows对进程地址空间的管理用到了AVL树
实现
- 左左情况:需要将插入的节点以及其父节点、祖父节点做右旋操作,使其父节点代替祖父节点,祖父节点成为其父节点的右孩子
[外链图片转存失败(img-KhRCeg3s-1566869439724)(http://suyeq.com:8888/CloudNotes/Treeimg/311901354227875.png)] - 右右情况:需要将插入的节点以及其父节点、祖父节点做左旋操作,使其父节点代替祖父节点,祖父节点成为其父节点的左孩子
- 左右情况:先进行左旋操作,再进行右旋操作
- 右左情况:先进行右旋操作,再进行左旋操作
[外链图片转存失败(img-fgKr25TM-1566869439726)(http://suyeq.com:8888/CloudNotes/Treeimg/312151521573646.png)]
其删除实现利用二叉搜索树的删除,然后再进行平衡操作(旋转操作)。
红黑树
定义:
- 红黑树是每个节点都带有颜色属性的二叉查找树,有如下要求:
- 节点是红色或黑色。
- 根是黑色。
- 所有叶子都是黑色(叶子是NIL节点或者是黑色节点,NIL节点是占位的,当做没有内容的黑色节点)。
- 每个红色节点必须有两个黑色的子节点。(从每个叶子到根的所有路径上不能有两个连续的红色节点。)
- 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。
其中性质5与6保证了红黑树最长路径不会超过其最短路径的两倍,保证了平衡性,红黑树其实是牺牲了一些平衡性来换取插入删除的性能上的提升,插入删除查找平均时间均为O(lgn)
。
应用场景
C++的STL中的Map等,epoll在内核中的实现,用红黑树管理事件块,nginx中,用红黑树管理timer等,Java的TreeMap、HashMap等实现,著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块
插入实现
我们规定插入的新节点的颜色始终为红色,因为这样就可以不违反性质6,让红黑树的实现尽量简单些。
- 当前树是空树,那么直接将新插入的节点变成黑色
- 插入节点的父节点是黑色,那么直接插入,不做其他改变
- 如果新节点的父节点以及叔父节点都为红色,那么需要将父节点以及叔父节点变成黑色
- 如果新节点的父节点是红色,而叔父节点是黑色或者缺失,(即两种情况,左右以及右左情况)那么需要对其进行旋转操作,使其变为第五种情况(左左或者右右情况)
- 如果新节点的父节点是红色,叔父节点是黑色或者缺失,且是左左或者右右情况,那么需要进行旋转操作
[外链图片转存失败(img-2fYXMkcU-1566869439728)(http://suyeq.com:8888/CloudNotes/Treeimg/Red-black_tree_5.png)]
红黑树的删除操作与AVL树的一样,利用二叉搜索树的性质来找到前驱或者后继节点替换,然后进行红黑树的旋转变色操作使其符合红黑树的性质。
B树
定义
是一种自平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的动作,都在对数时间内完成。概括来说是一个一般化的二叉查找树,它的性质如下:
- 每个节点最多有m(m>=2)颗子树
- 除了根节点以及叶子节点外,每个分支节点都至少有ceil(m/2)颗子树
- 根节点至少含有两个节点,除非B树只有根节点
- 所有的叶子节点都在同一层上
- 有i个孩子的节点,关键字就有i-1个,且按递增排列
注:蓝色为关键字,黄色为指向其它节点的指针
应用场景
与红黑树相比,B树的优势体现在它的树高度比红黑树要小得多,因为红黑树的度适中为2,而B树不定。这样就可以减少检索的次数,如果体现在磁盘里,那么就能减少访问磁盘的次数,从而减少查询数据的时间。所以B树的主要应用在文件系统以及数据库系统,比如MySql采用B+树实现。
B树的高度
N个总关键字数的m阶的B树的最大高度为:logceil(m/2)((N+1)/2 )+1
B+树
定义
B+树是B树的变体,主要的不同体现在下面几个方面:
- 所有的关键字都包含于叶子节点中,而B树一部分关键字在非叶子节点中
- 叶子节点按递增顺序排列,且每个叶子结点都是连接的,形成一个有序的数组
- 所有的非叶子结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字
- 非叶子节点中不包含关键字指向具体信息的指针,只包含关键字,比起B树空间使用得少
[外链图片转存失败(img-wogypHm0-1566869439729)(http://suyeq.com:8888/CloudNotes/Treeimg/B+Tree.jpg)]
与B树的比较
B+树在磁盘中区间查询的性能比B树高,因为可以直接在叶子结点顺序遍历,顺序遍历省去了磁盘寻道时间的消耗,这是数据库索引选择B+树来实现的主要原因
B*树
定义
B*树是B+树的变体,不同体现于以下方面:
- B*树中非根和非叶子结点再增加指向兄弟的指针
- B*树定义了非叶子结点关键字个数至少为(2/3)*m,B+树为1/2
简单来说,就是一颗丰满的B+树。。。
ps:以上所有图片均来自网络