我所知道的三种常用的复杂度为O(nlogn)的算法有三种,快速排序,堆排序,归并排序,今天说说堆排序。
首先说几个概念:
1.堆实际上是一棵完全二叉树,其任何一非叶节点满足性质:
任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。
2.大根堆:所有节点的子节点比其自身小的堆
3.小根堆:所有节点的子节点比其自身大的堆
4.大根堆的堆顶元素就是这个堆中最大的元素。
直接上代码:
using System;
/// <summary>
/// 堆排序
/// 思路是改良简单选择排序每次寻找最小值的过程
/// 将前几次寻找的历史记录下来存在二叉树中
/// 从而减少了比较的次数,优化了性能
/// 复杂度为O(nlogn)
/// </summary>
namespace test
{
class MainClass
{
public static void Main (string[] args)
{
int [] arr = {3, 2, 7, 5, 4, 23, 54, 23, 65, 23, 65, 786, 21, 623, 75};
HeapSort (arr);
foreach (int i in arr) {
Console.WriteLine (i);
}
}
//堆排序算法
private static void HeapSort (int [] arr) {
//先求出大根堆(及堆顶元素为最大,根节点从上到下越来越小的堆)
//从而使无序数列变成一个有序堆
for (int i = arr.Length / 2 - 1; i >= 0; i--) {
HeapAdjust (arr, i, arr.Length);
}
//将堆顶元素与最后一个元素交换,求局部大根堆
int temp;
for (int i = arr.Length - 1; i > 0; i--) {
temp = arr [i];
arr [i] = arr [0];
arr [0] = temp;
HeapAdjust (arr, 0, i);
}
}
//求大根堆的方法
private static void HeapAdjust (int [] arr, int index, int length) {
int temp = arr [index]; //提前存下关键值
for (int i = index * 2 + 1; i < length; i = i * 2 + 1) { //遍历每层结点
if (i < length - 1 && arr [i] < arr [i + 1]) { //比较左孩子结点和右孩子结点,取大的索引位
i++;
}
if (arr [index] >= arr [i]) { //如果关键值本来就比孩子节点大直接跳出
break;
}
arr [index] = arr [i]; //否则将关键值放在大的孩子结点位置
index = i;
}
arr [index] = temp; //将大的孩子结点值放在一开始传进的关键值位置
}
}
}