堆再内存中是以数组的形式存储的,所以是层序。在逻辑上是一颗二叉树,而且只能是满二叉树或完全二叉树。如果堆的顺序被打乱,就需要向下调整函数重新建堆。
本篇使用建小堆和逆序的逻辑
向下调整函数
示意图: 代码如下:
//向下调 void AdjustDwon(HPDataType* a, int n, int parent)//n是数据个数 { assert(a); int child = parent * 2 + 1; //左孩子 //左孩子等于父亲乘2加1 while (child < n) { //如果右孩子没越界, 并且左右孩子中右孩子较小, 就选右孩子 if (child + 1 < n && a[child] > a[child + 1]) { child++; } //只要孩子小于父亲,就下调, 直到调到合适的位置 if (a[child] < a[parent]) { //交换父亲和孩子的数据 HPDataType tmp = 0; tmp = a[child]; a[child] = a[parent]; a[parent] = tmp; //更改下标,孩子的数据挪到了父亲的位置,下标自然也要挪 parent = child; child = parent * 2 + 1; } else { break; } } }
建堆
如果数组是完全无序,则不断的需要使用向下调整函数建堆。
示意图: 代码如下:
for (int i = (n - 1 - 1) / 2; i >= 0; i--) /*向下调整建小堆 n - 1是最后一个孩子的下标,((n - 1)- 1)/2 是最后一个父亲的下标*/ { if (a[i] >= a[i * 2 + 1] || a[i] >= a[i * 2 + 1 + 1]) //如果父亲比孩子大,就需要父亲下调重新建队 { AdjustDwon(a, n, i); //下调 } }
这样我们就建好堆了。
排序
堆的第一个数一定是最小的(小堆),把第一个数和最后一个数交换,这样就排好一个数了,再因为最后一个数再第一的位置,所以让总数减一,调用向下调整函数重新建堆,再把第一个数和最后一个数交换,如此往复,直到数组有序。代码如下:
for (int i = n; i > 0; i--) { //把第一个数据和最后一个数据交换 int tmp = a[i - 1]; //i 是个数 i-1 是下标 a[i - 1] = a[0]; a[0] = tmp; AdjustDwon(a, i - 1, 0); // i-1 和 i-1 的右边已经是有序数组,不需要参与堆的重新排序 }
时间复杂度:
专门写了以篇文章讲复杂度,去看看吧