堆排序(Heap Sort)是一种基于比较的排序算法,它利用堆这种数据结构所设计。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
在堆排序算法中,我们首先将待排序的序列构造成一个大顶堆(或小顶堆),此时,整个序列的最大值(或最小值)就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值(或最小值)。然后将剩余n-1个序列重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。
以下是堆排序算法的C#实现:
using System;
class Program
{
static void Main(string[] args)
{
int[] arr = { 12, 11, 13, 5, 6, 7 };
HeapSort(arr);
Console.WriteLine("Sorted array: ");
PrintArray(arr);
}
// 堆排序方法
static void HeapSort(int[] arr)
{
int n = arr.Length;
// 构建大顶堆
for (int i = n / 2 - 1; i >= 0; i--)
{
Heapify(arr, n, i);
}
// 一个个从堆顶取出元素
for (int i = n - 1; i > 0; i--)
{
// 移动当前根到末尾
Swap(ref arr[0], ref arr[i]);
// 调用max heapify在减少的堆上
Heapify(arr, i, 0);
}
}
// 维持堆的性质
static void Heapify(int[] arr, int n, int i)
{
int largest = i; // 初始化最大为根
int left = 2 * i + 1; // 左 = 2*i + 1
int right = 2 * i + 2; // 右 = 2*i + 2
// 如果左子节点大于根
if (left < n && arr[left] > arr[largest])
{
largest = left;
}
// 如果右子节点大于目前最大
if (right < n && arr[right] > arr[largest])
{
largest = right;
}
// 如果最大不是根
if (largest != i)
{
Swap(ref arr[i], ref arr[largest]);
// 递归地调整受影响的子树
Heapify(arr, n, largest);
}
}
// 交换两个元素
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
// 打印数组的方法
static void PrintArray(int[] arr)
{
foreach (int i in arr)
{
Console.Write(i + " ");
}
Console.WriteLine();
}
}
在这个实现中,HeapSort
方法是堆排序的入口点。它首先通过从最后一个非叶子节点开始向前遍历,对每个节点调用 Heapify
方法来构建大顶堆。然后,它通过将堆顶元素(即当前最大值)与数组的最后一个元素交换,并减小堆的大小(不考虑已经排序的最后一部分),再对新的堆顶元素调用 Heapify
方法来保持堆的性质,从而逐步将数组排序。
Heapify
方法是维持堆性质的关键。它接受一个数组、堆的大小和当前节点的索引作为参数。它首先假设当前节点是子树中的最大值,然后与其子节点进行比较。如果发现有子节点大于当前节点,则更新最大值的索引,并在需要时递归地调用 Heapify
方法以保持子树的堆性质。
Swap
方法用于交换两个整数变量的值。
PrintArray
方法用于打印排序后的数组。
堆排序的时间复杂度为O(n log n),是一种不稳定的排序算法,但它不需要额外的存储空间(除了几个临时变量用于交换),并且在实际应用中表现出良好的性能。