堆,顾名思义,小山堆,最标准的形状就是金字塔
1
2 3
4 5 6 7
这个三角就是简易的堆
我们可以看出堆的关系时根节点下有两个子节点构成,根节点为k,左孩子就是2k,右孩子就是2k+1,颠倒来讲,当孩子节点的索引为k,那么它的母节点的索引为k/2,这个概念是我们接下来讨论shiftup和shiftdown问题的关键。
堆的子节点都要比父节点小
ShiftUp
上浮操作呢在逻辑上要比下沉操作简单,所以先来讨论上浮操作。
ShiftUp为上浮操作,插入操作主要是由ShiftUp完成,新增接点在尾部插入,然后将count+1,用上浮操作对数字进行上浮,让插入的数字上浮到应该在这个堆中所处的位置。传入count为形参k,所在的位置不断上浮,当母节点(k/2)上的元素值小于子元素(k)则发生一次交换,来维持这个堆.。
具体代码如下
public void insert(int val) {
arr[count + 1] = val;
shiftUp(++count);
}
public void shiftUp(int k) {
while (k >= 0 && arr[k] > arr[k / 2]) {
swap(k / 2, k);
k /= 2;
}
}
ShiftDown操作
shiftdown操作有点复杂,主要是子节点选择方面,我们可以先将k*2设成j,再将左右子节点进行比较,我们选取较大的那个,与k的元素进行比较,取最小者进行交换操作。代码如下
public void shiftDown(int k) {
while (2 * k <= count) {
int j = k * 2;
if (j + 1 <= count && arr[j + 1] > arr[j]) {
j += 1;
}
if (arr[j] > arr[k]) {
swap(j, k);
k = j;
} else {
break;
}
}
}
堆排序
堆排序也是一个比较高效的排序算法,主要是在于我们只需要去最大的0位元素,然后将它做类似弹出(POP)的操作即可实现,首先取出第一位元素,将最后一位(k)元素与第一位(0)元素进行交换,对有效个数进行-1操作(count–),这时,k位元素为最大值,并且已被丢弃,而第一位为最小,执行shiftdown让此时的第一位元素下沉维护堆的性质。
public int extractMax() {
int ret = arr[0];
swap(0, count);
count--;
shiftDown(0);
return ret;
}