堆排
一、概述
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。属于比较排序的一种。由 JWJ Williams 在 1964 年发明,同时也是堆诞生的时间。堆是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
堆排序被认为是一种改进的选择排序:与选择排序一样,堆排序将其输入分为已排序和未排序区域,并依次通过从未排序区域中获取最大(或最小)元素插入已排序区域。与选择排序不同,堆排序不会浪费时间对未排序区域进行线性时间扫描;相反,堆排序在堆数据结构中维护未排序区域,以便在每个步骤中更快地找到最大(或最小)元素。
堆排序的平均时间复杂度为
O
(
n
log
n
)
O (n \log n)
O(nlogn),空间复杂度为
O
(
1
)
O (1)
O(1)。它的最佳和最坏情况在一个小的常数因子之内,即都为
O
(
n
log
n
)
O (n \log n)
O(nlogn)。 尽管在大多数机器上的实践中比实施良好的快速排序要慢一些,但它最坏情况
O
(
n
log
n
)
O( n \log n )
O(nlogn) 运行时比快排更有优势。堆排 是一种原地算法,与快排一样属于不稳定排序。
二、分析
下面例子以升序做分析
- 将数据初始化,构建一个大根堆,从最后一个根节点开始调整
(
n
2
−
1
)
(\dfrac{n}{2} - 1)
(2n−1) ,直到所有根节点调整完
注:大根堆:所有根节点都比所属的子节点大 - 从大根堆中将根节点取出并与未排序的最后一个节点交换,排序检索范围减一
- 继续将剩余未排序数据进行大根堆调整
- 重复 2、3 步骤,直到所有元素都取完
三、代码实现
using System;
using System.Collections.Generic;
using System.Text;
namespace MarsUtil.Algorithm.Sort
{
public class Heapsort
{
/// <summary>
/// 堆排序升序 平均效率 nlogn
/// </summary>
/// <param name="list"></param>
/// <param name="len"></param>
public void SortAscending(List<int> list, int len)
{
//初始化,i 从最后一个父节点开始调整 (大根堆调整 list[i] >= list[2*i + 1] && list[i] >= list[2*i + 2])
for (int i = len / 2 - 1; i >= 0; i--)
{
_MaxHeapify(list, i, len - 1);
//DebugList(list);
}
// 先將第一个元素和已经排好的元素前一位做交換,再重新调整(刚调整的元素之前的元素),直到排序完毕
for (int i = len - 1; i > 0; i--)
{
_Swap(list, 0, i);
_MaxHeapify(list, 0, i - 1);
//DebugList(list);
}
}
/// <summary>
/// 堆排序降序 平均效率 nlogn
/// </summary>
/// <param name="list"></param>
/// <param name="len"></param>
public void SortDescending(List<int> list, int len)
{
// 初始化,i 从最后一个父节点开始调整 (小根堆调整 list[i] <= list[2*i + 1] && list[i] <= list[2*i + 2])
for (int i = len / 2 - 1; i >= 0; i--)
{
_MinHeapify(list, i, len - 1);
}
//先將第一个元素和已经排好的元素前一位做交換,再重新调整(刚调整的元素之前的元素),直到排序完毕
for (int i = len - 1; i > 0; i--)
{
_Swap(list, 0, i);
_MinHeapify(list, 0, i - 1);
}
}
// 大根堆
private void _MaxHeapify(List<int> list, int start, int end)
{
// 建立父节点索引和子节点索引
int dad = start;
int son = dad * 2 + 1;
while (son <= end)
{
// 如果子节点索引在合法范围内才做比较
if (son + 1 <= end && list[son] < list[son + 1]) //先比较两个子节点,选择最大的
{
son++;
}
// 如果父节点大于子节点则调整结束
if (list[dad] >= list[son])
{
return;
}
else
{
// 否则交换父子內容,再继续用子节点和孙节点比较
_Swap(list, dad, son);
dad = son;
son = dad * 2 + 1;
}
}
}
// 小根堆
private void _MinHeapify(List<int> list, int start, int end)
{
// 建立父节点索引和子节点索引
int dad = start;
int son = dad * 2 + 1;
while (son <= end)
{
// 如果子节点索引在合法范围内才做比较
if (son + 1 <= end && list[son] > list[son + 1]) //先比较两个子节点,选择最小的
{
son++;
}
// 如果父节点小于子节点则调整结束
if (list[dad] <= list[son])
{
return;
}
else
{
// 否则交换父子內容,再继续用子节点和孙节点比较
_Swap(list, dad, son);
dad = son;
son = dad * 2 + 1;
}
}
}
private void _Swap(List<int> list, int indexA, int indexB)
{
int count = list.Count;
if (0 <= indexA && indexA < count
&& 0 <= indexB && indexB < count)
{
int temp = list[indexA];
list[indexA] = list[indexB];
list[indexB] = temp;
}
}
public void DebugList(List<int> list)
{
StringBuilder sb = new StringBuilder();
list.ForEach(it =>
{
sb.Append(it + ", ");
});
Console.WriteLine(sb.ToString());
}
}
}
参考资料
[1] 堆排序:https://www.runoob.com/w3cnote/heap-sort.html
[2] Heapsort:https://en.wikipedia.org/wiki/Heapsort
[3] 堆排序:https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F
欢迎关注个人公众号,实时推送最新博文!