堆排序之堆的概念—插入、删除、建堆
程序员文章站
2024-02-14 09:50:16
...
内容会持续更新,有错误的地方欢迎指正,谢谢!
堆的性质
- 性质:完全二叉树 或 近似完全二叉树(不是满二叉树的完全二叉树)。
- 分类:最大堆:父节点的值不小于子节点;最小堆:父节点的值不大于子节点。
- 左右子节点:没有大小的顺序。
- 堆的存储:
一般都用数组来存储堆。下标为i的节点的父节点的下标为(i–1)/2。根节点的左右子节点下标分别为 2∗i+1 和 2∗i+2。例如:下标为0的节点左右子节点的下标分别为1和2。 - 堆的操作:插入、删除、建堆。
最小堆的图:
插入
原理:新元素必须插入到二叉树的末尾(即堆尾)。如果该数组构成的二叉树不满足堆的性质,则需要重新排列元素—“上浮”操作。
最小堆的插入操作图:
最大堆的插入操作的代码:
//非常建议各位看官根据对原理的理解,写出自己的代码,切忌看我的代码去理解算法,否则很快就会忘记!
void MaxHeapFixUp(int* array, int index)
{
//index为插入的节点的下标,则其父节点为(index-1)/2。
int indexFather = (index - 1) / 2;
int temp = 0;
while(index!=0)
{
if (array[indexFather] < array[index])
{
temp = array[index];
array[index] = array[indexFather];
array[indexFather] = temp;
}
else
break;
index = indexFather;
indexFather = (index - 1) / 2;
}
}
删除
原理:删除操作必须删除该二叉树的根节点(即堆顶)。堆中最后一个元素用来补空,再重新排列元素—“下沉”操作。
最小堆的删除操作图:
删除操作的代码:
//非常建议各位看官根据对原理的理解,写出自己的代码,切忌看我的代码去理解算法,否则很快就会忘记!
void MaxHeapFixDown(int* array, int length, int index)
{
///index为待下沉的父节点的下标,其子节点为2*index+1和2*index+2。
//本代码段只包含对根节点的下沉部分。
int temp = array[index];
int indexOfSon = 2 * index + 1;
while (indexOfSon < length)
{
if (indexOfSon + 1 < length&&array[indexOfSon + 1] > array[indexOfSon])
++indexOfSon;
if (array[indexOfSon] < temp)
break;
array[index] = array[indexOfSon];
index = indexOfSon;
indexOfSon = 2 * index + 1;
}
array[index] = temp;
}
建堆(堆调整)
最小堆的初始图:
原理:很明显,对叶子结点来说,可以认为它已经是一个合法的堆了,即 20,60, 65,4, 49 都分别是一个合法的堆。只要从 a[4]=50 开始向下进行堆调整就可以了。然后再对 a[3]=30,a[2] = 17,a[1] = 12,a[0] = 9 分别作一次向下堆调整就可以了。
下面展示了建最小堆的步骤:
建最大堆的代码:
void MakeMaxHeap(int* array, int length)
{
//length/2-1恒等于最后一个根节点的下标。
for (int i = length / 2 - 1; i >= 0; --i)
{
//调用删除(下沉)操作
MaxHeapFixDown(array, 10, i);
}
}
堆排序的实现
请查看我的另一篇博客,里面有详细地介绍:树形选择排序—堆排序
上一篇: Unity Shader——Unity的着色器编写
下一篇: SQL 分组计算 topN