目录
大根堆:根节点为最大的堆。
小根堆:根节点为最小的堆。
堆总是一颗完全二叉树。
堆的底层是一个数组,但是在应用的时候会将其看成一颗完全二叉树,所以他也有一些二叉树的性质:
假设i为元素的下标
(1)i不为0的节点的双亲节点的下标为:(i-1)/ 2;如果i为0,那i就是根节点。
(2)下标为i的节点的左孩子下标:i*2+1,否则没有左孩子
(3)下标为i的节点的右孩子下标:i*2+2,否则没有右孩子
堆的底层是一个数组,所以在创建堆之前要先建立一个数组
- 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;
- }
- }
- }
将新增的元素放到队尾,然后对其进行向上调整,和向下调整类似,只不过顺序由下向上,并且新增的元素一次只有一个,所以只需要进行一次即可
- 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;
- }
- }
- }
直接返回数组的第一个元素
- //判断堆是否为空
- private boolean isEmpty() {
- return this.usedSize == 0;
- }
-
- public int peek() {
- if (isEmpty()) {
- return -1;
- }
- return this.elem[0];
- }
弹出元素相当于删除元素,将首元素和尾元素交换将其删除,然后进行向下调整
- 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;
- }
对堆进行升序排序,需要建立大根堆,每次将堆顶元素放到相对应的尾部,然后再进行像下调整,比如:第一次放到到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--;
- }
- }