目录
1.堆的性质
大根堆:根节点为最大的堆。
小根堆:根节点为最小的堆。
堆总是一颗完全二叉树。
2.堆的存储方式
堆的底层是一个数组,但是在应用的时候会将其看成一颗完全二叉树,所以他也有一些二叉树的性质:
假设i为元素的下标
(1)i不为0的节点的双亲节点的下标为:(i-1)/ 2;如果i为0,那i就是根节点。
(2)下标为i的节点的左孩子下标:i*2+1,否则没有左孩子
(3)下标为i的节点的右孩子下标:i*2+2,否则没有右孩子
3.堆的创建
堆的底层是一个数组,所以在创建堆之前要先建立一个数组
public class TestHeap {
public int[] elem;
public int usedSize;//数组元素个数
public static final int DEFAULT_SIZE = 10;//数组的初始容量
public TestHeap() {
elem = new int[DEFAULT_SIZE];
}
}
我们以大根堆为例
既然是创建大根堆,那么它的每颗子树都需要保证根节点是该树的最大值,所以我们从最后一个有孩子的子树开始对堆依此进行向下调整:
首先找到该子树的左孩子节点,然后进入循环,每次找到左右孩子的最大值,让其与双亲节点相比较,大的放到双亲节点,然后让双亲节点指向孩子节点,进行循环操作,直到双亲节点的下标超过节点总数(等于也不行)
public void createHeap(int[] array) {
for (int i = 0; i < array.length; i++) {
elem[i] = array[i];
usedSize++;
}
for (int parent = (usedSize - 1 - 1) / 2; parent >= 0; parent--) {
shiftDown(parent, usedSize);//向下调整
}
}
public void shiftDown(int parent, int len) {
int child = parent * 2 + 1;
while (child < len) {
if (child + 1 < len && elem[child] < elem[child + 1]) {
child++;
}
if (elem[parent] < elem[child]) {
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
parent = child;
child = parent * 2 + 1;
} else {
break;
}
}
}
4.堆的增删查改
4.1 offer() 增添元素
将新增的元素放到队尾,然后对其进行向上调整,和向下调整类似,只不过顺序由下向上,并且新增的元素一次只有一个,所以只需要进行一次即可
public void offer(int val) {
if (isFull()) {
//扩容
this.elem = Arrays.copyOf(this.elem, this.elem.length * 2);
}
this.elem[this.usedSize] = val;
this.usedSize++;
//向上调整
shiftUp(usedSize - 1);
}
private boolean isFull() {
return this.usedSize == this.elem.length;
}
public void shiftUp(int child) {
int parent = (child - 1) / 2;
while (parent >= 0) {
if (elem[parent] < elem[child]) {
int tmp = elem[parent];
elem[parent] = elem[child];
elem[child] = tmp;
child = parent;
parent = (parent - 1) / 2;
} else {
break;
}
}
}
4.2 peek() 获取堆顶元素
直接返回数组的第一个元素
//判断堆是否为空
private boolean isEmpty() {
return this.usedSize == 0;
}
public int peek() {
if (isEmpty()) {
return -1;
}
return this.elem[0];
}
4.3 pop() 弹出堆顶第一个元素并返回
弹出元素相当于删除元素,将首元素和尾元素交换将其删除,然后进行向下调整
public int pop() {
if (isEmpty()) {
return -1;
}
int tmp = elem[0];
elem[0] = elem[this.usedSize - 1];
elem[this.usedSize - 1] = tmp;
//删除最后一个元素
this.usedSize--;
//向下调整
shiftDown(0, usedSize);
return tmp;
}
5.堆排序
对堆进行升序排序,需要建立大根堆,每次将堆顶元素放到相对应的尾部,然后再进行像下调整,比如:第一次放到到i位置,下一次放到i-1位置,以此类推……
public void heapSort() {
int end = usedSize - 1;
while (end > 0) {
//将堆顶元素与其相对的位置交换
int tmp = elem[end];
elem[end] = elem[0];
elem[0] = tmp;
//向下调整,将最大值放到堆顶
shiftDown(0, end);
end--;
}
}