堆排序是一种利用堆结构进行排序的算法,堆是一种特殊的树形数据结构,满足以下性质:
- 堆中任意节点的值都不大于或不小于其子节点的值,分别称为最大堆和最小堆;
- 堆总是一棵完全二叉树。
堆排序的基本思路是先将待排序数组构建成一个堆,然后依次将堆顶元素(最大元素或最小元素)取出并放到最终的有序序列中,再重新调整堆使其满足堆的性质。具体实现可以分为两个过程:构建堆和排序。
构建堆:
- 从待排序数组中选取一个节点作为根节点,将其与最后一个节点交换,使最后一个节点成为新的根节点。
- 对新的根节点进行下沉操作,即将其与其子节点中较大(或较小)的一个交换位置,直到堆的性质被满足。
- 重复步骤 1 和 2 直到所有节点都被遍历过。
排序:
- 依次将堆顶元素(最大元素或最小元素)取出并放到最终的有序序列中;
- 将堆的最后一个元素放到堆顶,重新调整堆使其满足堆的性质;
- 重复步骤 1 和 2 直到所有元素都被取出。
下面是堆排序的实现(以最大堆为例):
void heapInsert(vector<int>& array, int targetIndex)
{
while (array[targetIndex] > array[(targetIndex - 1) / 2])
{
swap(array[targetIndex], array[(targetIndex - 1) / 2]);
targetIndex = (targetIndex - 1) / 2;
}
}
void heapify(vector<int>& array, int index, int heapSize)
{
while (index < heapSize)
{
int maxChildIndex;
int leftChildIndex = index * 2 + 1;
if (leftChildIndex >= heapSize)
{
return;
}
int rightChildIndex = leftChildIndex + 1;
if (rightChildIndex >= heapSize)
{
maxChildIndex = leftChildIndex;
}
else
{
maxChildIndex = array[leftChildIndex] > array[rightChildIndex] ? leftChildIndex : rightChildIndex;
}
if (array[index] > array[maxChildIndex])
{
return;
}
swap(array[index], array[maxChildIndex]);
index = maxChildIndex;
}
}
void heapSort(vector<int>& array)
{
int n = array.size();
if (n < 2)
{
return;
}
// Build the max heap
for (int i = 0; i < n; ++i)
{
heapInsert(array, i);
}
// Take the max value, and use the last element to do the heapify job
for (int i = array.size() - 1; i > 0; --i)
{
swap(array[0], array[i]);
heapify(array, 0,i);
}
}
堆排序的时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),空间复杂度为 O ( 1 ) O(1) O(1)。