完美二叉树
若像下图这样写
当child为堆顶时,计算parent为0(不会是-0.5,向上取整为0),while判断parent为0符合条件进入循环,此时if(a[child]==a[parent]),跳出循环。这只是程序能巧合运行,将代码循环条件改为child>0,即优化为上面代码即可
向下调整算法
1.接口是 HPDataType* a, int n, int parent
2.算出孩子child(以左孩子为例),使用假设法,始终让child为小的孩子
3.(1)将parent与孩子对比,若孩子小于双亲则交换,同时继续判断交换下去的值是否需要再次交换,所以将parent改为child,重新计算child。
(2) 否则break
4.while循环,若直至child到从下往上第一层,parent为从下往上第二层,若再执行一次3(1),child不存在(超出了数组),此时child与parent都是从下往上第一层同一个节点,不需要再循环,所以循坏结束条件是child<n.
pop 删除:在小堆的基础上(用插入(包含向上调整法),根的左右子树是小堆),交换首尾元素后删除尾元素(左子树和右子树是小堆,将最小的堆顶元素交换到数组尾部),然后使用向下调整算法(将首元素与下面的两个元素中小的元素依次交换,交换到不能交换为止,建立小堆)。
删除的都是剩余数据中最小的数据,所以会由小到大依次删除数据
孩子给双亲
以 以下图中算法给数组进行堆排序,HPPush函数传的是结构体变量地址,该函数额外申请内存存放堆,需要消耗额外的空间来存放堆,空间复杂度O(n)
Destroy(&hp);
}
以下图片没有传结构体变量指针原因,而是直接传数组首元素地址,直接在所给数组基础上排序,不用再调用排序函数(排序函数需要消耗额外的空间),减少空间复杂度,
以下算法直接在所给数组上进行堆排序,不用再调用排序函数,减少空间复杂度
向上调整算法排序数组/向下调整算法排序数组
向上调整算法/向下调整算法可以分别构建大和小堆
将无序的数组用向上调整算法/向下调整算法重新排列成小堆(以向上调整算法建小堆为例),再将数组的首尾元素交换,此时尾元素不在数组中算,将数组用向下调整法重新拍列成小堆,将数组长度减一,重复循环至循环结束,即可得到一个由大到小的数组。即下方的//降序,建小堆。反之亦然。
void HeapSort(int* a, int n)
{
// 降序,建小堆
// 升序,建大堆
for (int i = 1; i < n; i++) 因为是直接在数组本身上调整排序,所以直接从第二个元素开始与第 一个元素比较
{
AdjustUp(a, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
void TestHeap2()
{
int a[] = { 4,2,8,1,5,6,9,7,2,7,9};
HeapSort(a, sizeof(a) / sizeof(int));
}
int main()
{
TestHeap2();
return 0;
}
向下调整算法排序数组还有另外一种算法
按照大堆重新排列
为了确保除根外的左右子树是按大堆排列,我们可以从倒数第一个非叶子节点(最后一个元素的双亲节点)开始用向下调整算法调大堆,倒数第二个非叶子节点开始调大堆,以此类推,直至除根外的左右子树是按大堆排列,然后再用向下调整算法
将无序的数组用向下调整算法建堆按照大堆重新排列,再将数组的首尾元素交换,此时尾元素不在数组中算,将数组用向下调整法重新拍列成大堆,将数组长度减一,重复循环至循环结束。即可得到一个由小到大的数组。即下方的//降序,建小堆。
void HeapSort(int* a, int n)
{
for (int i = (n-1-1)/2; i >= 0; i--)
{
AdjustDown(a, n, i);
}
int end = n - 1;
while (end > 0)
{
Swap(&a[0], &a[end]);
AdjustDown(a, end, 0);
--end;
}
}
void TestHeap2()
{
int a[] = { 4,2,8,1,5,6,9,7,2,7,9};
HeapSort(a, sizeof(a) / sizeof(int));
}
int main()
{
TestHeap2();
return 0;
}
向上调整算法logN
向下调整算法logN
向上调整算法排序数组O(N*logN)向下调整算法排序数组O(N*logN)
向下调整算法建堆O(N)
循环条件