大顶堆的定义:每一个父节点都大于其子结点,根结点是最大的结点
数组转换为堆:层次遍历,第n层填满2^(n-1)次个,最后一层可以填满也可以不填满
第i个结点的左子结点为2*i+1 右子结点为2*i+2
堆结点的基本思想:将数组构造为大顶堆,把根结点放到无序数组的最后
剩余的元素无序,继续构造大顶堆,将根节点放到无序数组后
…………
重复以上步骤 最后可以得到一个有序数组
堆排序的完整代码如下:
public static void heapSort(int[] arr)
{
int temp;
//最后一个非叶子结点:arr.Length / 2 - 1
for (int i = arr.Length / 2 - 1; i >= 0; i--)
{
adjustHeap(arr, i, arr.Length);
}//for循环结束后得到了第一个大顶堆
for (int j = arr.Length-1; j > 0; j--)
{
//交换 把根节点沉到最后
temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
//排好后仍有j个元素无序 如第一轮排完后只有一个元素有序,剩余arr.Length-1
//个元素无序,所以无序数组长度为j
//每次构造大顶堆都是从头开始构造的
adjustHeap(arr, 0, j);
}
}
//将数组转换为大顶堆
/// <summary>
/// 将以i对应的非叶子结点的数调整为大顶堆
/// </summary>
/// <param name="arr">待调整的数组</param>
/// <param name="i">非叶子结点的索引</param>
/// <param name="length">多少个元素还是无序 length逐渐减少</param>
public static void adjustHeap(int[] arr, int i, int length)
{
int temp = arr[i];//先取出当前元素的值
//k=i*2+1是i结点的左子节点 k=i*2+2是i结点的右子结点
for (int k = i * 2 + 1; k < length; k = k * 2 + 1)
{
//k+1<length为了防止数组越界
if (k + 1 < length && arr[k] < arr[k + 1])//i的左子结点小于右子结点
{
k++;//k指向右子节点
}
if (arr[k] > temp)//子结点大于父结点
{
arr[i] = arr[k];
i = k;//让i指向k继续循环 i可以理解为用来跟踪本轮循环最小值的位置
}
else
{
break;//break的原因是从左到右,从下到上调整
}
}
//for循环结束后 已经将i为父结点的树的最大值放在了最顶上(局部大顶堆)
arr[i] = temp;//将temp值放到调整后的位置
}