二叉堆简介
二叉堆是完全二元树或者是近似完全二元树,按照数据的排列方式可以分为两种:最大堆和最小堆。
① 最大堆:父结点的键值总是大于或等于任何一个子节点的键值;
② 最小堆:父结点的键值总是小于或等于任何一个子节点的键值。
二叉堆一般都通过"数组"来实现,下面是数组实现的最大堆和最小堆的示意图:
基本操作
二叉堆通常使用数组来实现(实际上可以用ArrayList,因为底层就是数组),数组实现的二叉堆,从根节点往下一层一层在数组依次摆放,父节点和子节点的位置存在一定的关系
假设"第一个元素"在数组中的索引为 0 的话,则父节点和子节点的位置关系如下:
① 索引为i的左孩子的索引是 (2*i+1);
② 索引为i的右孩子的索引是 (2*i+2);
③ 索引为i的父结点的索引是 floor((i-1)/2);
1. 插入
当向最大堆中添加数据时:先将数据加入到最大堆的最后,然后尽可能把这个元素往上挪,直到挪不动为止!
例子:在最大堆[90,80,70,60,40,30,20,10,50]中添加85
代码实现:
private int[] values = new int[16] ;
private int size;
/**
* 上浮
* @return
*/
private int fixUp() {
int j = size -1 ; //最后一个元素的下标
int f ; //父节点的下标
while((f = ((j -1) /2)) >= 0) { //通过父节点的下标
if(values[f] <= values[j])
break; //父节点的值小于子节点的值,则打适合的位置。
int temp = values[f] ;
values[f] = values[j];
values[j] = temp ;
j = f ;
}
return f;
}
/**
* 添加一个元素在最小堆中上
* @return
*/
public int push(int item) {
if(size >= values.length) Arrays.copyOf(values, size << 1) ;
values[size++] = item ;
return fixUp();
}
2. 删除
先删除该数据,然后用最大堆中最后一个的元素插入这个空位;接着,把这个“空位”尽量往上挪,直到剩余的数据变成一个最大堆
例子:从最大堆[90,85,70,60,80,30,20,10,50,40]中删除90
例子:从最大堆[90,85,70,60,80,30,20,10,50,40]中删除60
代码实现
/**
* 移除并获取一个堆顶元素
* @return 堆顶元素
*/
public int poll() {
if(size <= 0) throw new IllegalStateException("不存在元素");
int value = values[0];
values[0] = values[--size] ; //将最后一个元素提到堆顶
values[size] = 0 ; //清空最后一个的数据
fixDown(); //下沉操作
return value;
}
/**
* 下沉
* @return 下沉到适合位置的index
*/
private int fixDown() {
int f = 0 ; //父节点的index
int k ; //较小者子节点的index
while((k = (f << 1) + 1) < size) { //至少存在左子节点
if(k < size - 1) { //存在右子节点
if (values[k] > values[k + 1]) k++; //左右子节点进行比较。
}
if(values[f] <= values[k]) break; //父节点小于较小者子节点,则找到合适的位置,退出循环
int temp = values[f] ; values[f] = values[k]; values[k] = temp ;
f = k ;
}
return f;
}
3. 完整代码
/**
* 最小堆的超简化版实现
* Created by wang007 on 2018/6/12.
*/
public class MinHeap {
private int[] values = new int[16] ;
private int size;
/**
* 移除并获取一个堆顶元素
* @return 堆顶元素
*/
public int poll() {
if(size <= 0) throw new IllegalStateException("不存在元素");
int value = values[0];
values[0] = values[--size] ; //将最后一个元素提到堆顶
values[size] = 0 ; //清空最后一个的数据
fixDown(); //下沉操作
return value;
}
/**
* 下沉
* @return 下沉到适合位置的index
*/
private int fixDown() {
int f = 0 ; //父节点的index
int k ; //较小者子节点的index
while((k = (f << 1) + 1) < size) { //至少存在左子节点
if(k < size - 1) { //存在右子节点
if (values[k] > values[k + 1]) k++; //左右子节点进行比较。
}
if(values[f] <= values[k]) break; //父节点小于较小者子节点,则找到合适的位置,退出循环
int temp = values[f] ; values[f] = values[k]; values[k] = temp ;
f = k ;
}
return f;
}
/**
* 上浮
* @return
*/
private int fixUp() {
int j = size -1 ; //最后一个元素的下标
int f ; //父节点的下标
while((f = ((j -1) >>1)) >= 0) { //通过父节点的下标
if(values[f] <= values[j]) break; //父节点的值小于子节点的值,则打适合的位置。
int temp = values[f] ; values[f] = values[j]; values[j] = temp ;
j = f ;
}
return f;
}
/**
* 添加一个元素在最小堆中上
* @return
*/
public int push(int item) {
if(size >= values.length) Arrays.copyOf(values, size << 1) ;
values[size++] = item ;
return fixUp();
}
public static void main(String[] args) {
MinHeap heap = new MinHeap();
heap.push(4);
heap.push(2);
heap.push(7);
heap.push(9);
heap.push(1);
heap.push(5);
heap.push(10);
heap.push(3);
heap.push(2);
for (int i = 0 ;i< 9; i++) {
System.out.println(heap.poll());
}
}
}