1. 堆逻辑上是一棵完全二叉树
2. 堆物理上是保存在数组中
3. 满足任意结点的值都大于其子树中结点的值,叫做大堆,或者大根堆,或者最大堆
4. 反之,则是小堆,或者小根堆,或者最小堆
5. 堆的基本作用是,快速找集合中的最值
操作-向下调整
前提:左右子树必须已经是一个堆,才能调整。
说明:
1. array 代表存储堆的数组
2. size 代表数组中被视为堆数据的个数
3. index 代表要调整位置的下标
4. left 代表 index 左孩子下标
5. right 代表 index 右孩子下标
6. min 代表 index 的最小值孩子的下标
过程(以小堆为例):
- index 如果已经是叶子结点,则整个调整过程结束
- 判断 index 位置有没有孩子
- 因为堆是完全二叉树,没有左孩子就一定没有右孩子,所以判断是否有左孩子
- 因为堆的存储结构是数组,所以判断是否有左孩子即判断左孩子下标是否越界,即 left >= size 越界
- 确定 left 或 right,谁是 index 的最小孩子 min
- 如果右孩子不存在,则 min = left
- 否则,比较 array[left] 和 array[right] 值得大小,选择小的为 min
- 比较 array[index] 的值 和 array[min] 的值,如果 array[index] <= array[min],则满足堆的性质,调整结束
- 否则,交换 array[index] 和 array[min] 的值
- 然后因为 min 位置的堆的性质可能被破坏,所以把 min 视作 index,向下重复以上过程
图示:
public static void shiftDown(int[] array, int size, int index) {
int left = 2 * index + 1;
while (left < size) {
int min = left;
int right = 2 * index + 2;
if (right < size) {
if (array[right] < array[left]) {
min = right;
}
}
if (array[index] <= array[min]) {
break;
}
int t = array[index];
array[index] = array[min];
array[min] = t;
index = min;
left = 2 * index + 1;
}
}
给定数组,建一个大堆,实现插入弹出等操作
import java.util.Arrays;
public class HeapDemo {
public int[] elem;
public int usedSize;
public HeapDemo() {
this.elem = new int[10];
}
/**
* 在这里 为什么可以传len
* 是因为每棵树的结束位置 实际上都是一样的
* @param parent
* @param len 假设长度为10 len 就是10
* 时间复杂度:O(logn)
*/
public void adjustDown(int parent,int len) {
int child = 2*parent+1;//下标从零开始
//child < len 说明有左孩子
while (child < len) {
//child+1 < len 判断的是 当前是否 有右孩子
if(child+1 < len && this.elem[child] < this.elem[child+1]) {
child++;//选出左右孩子的最大值
}
//child 下标 一定是 左右孩子的最大值下标
if(this.elem[child] > this.elem[parent]) {
int tmp = this.elem[child];
this.elem[child] = this.elem[parent];
this.elem[parent] = tmp;
parent = child;
child = 2*parent+1;
}else {
// this.elem[child] <= this.elem[parent] 后续就不需要循环了
//后面的都已经是大根堆了
break;
}
}
}
//时间复杂度:nlogn
public void crateBigHeap(int[] array) {
for (int i = 0; i < array.length; i++) {
this.elem[i] = array[i];
this.usedSize++;
}
//elem当中已经存放了元素
for(int i = (this.usedSize-1-1) /2; i>= 0;i--) {
adjustDown(i,this.usedSize);
}
}
public void adjustUp(int child) {
int parent = (child-1)/2;
while (child > 0) {
if(this.elem[child] > this.elem[parent]) {
int tmp = this.elem[child];
this.elem[child] = this.elem[parent];
this.elem[parent] = tmp;
child = parent;
parent = (child-1)/2;
}else {
break;
}
}
}
/**
* 逻辑:放到数组的最后一个位置
* 然后向上调整
* @param val
*/
public void push(int val) {
if(isFull()) {
this.elem =Arrays.copyOf(this.elem,2*this.elem.length);
}
this.elem[this.usedSize] = val;
this.usedSize++;
adjustUp(this.usedSize-1);
}
/**
* 第一个 和 最后一个换
* 向下调整 0下标这棵树
* @return
*/
public int poll() {
if(isEmpty()) {
throw new RuntimeException("队列为空!");
}
int ret = this.elem[0];
//删除
int tmp = this.elem[0];
this.elem[0] = this.elem[this.usedSize-1];
this.elem[this.usedSize-1] = tmp;
this.usedSize--;//9
adjustDown(0,this.usedSize);
return ret;
}
public int peek() {
if(isEmpty()) {
throw new RuntimeException("队列为空!");
}
return this.elem[0];
}
public boolean isEmpty() {
return this.usedSize == 0;
}
public boolean isFull() {
return this.usedSize == this.elem.length;
}
public void show() {
for (int i = 0; i < this.usedSize; i++) {
System.out.print(this.elem[i] +" ");
}
System.out.println();
}
}