排序之堆排

堆排

一、概述

  堆排序Heapsort)是指利用这种数据结构所设计的一种排序算法。属于比较排序的一种。由 JWJ Williams1964 年发明,同时也是堆诞生的时间是一个近似完全二叉树的结构,并同时满足堆的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。

  堆排序被认为是一种改进的选择排序:与选择排序一样,堆排序将其输入分为已排序未排序区域,并依次通过从未排序区域中获取最大(或最小)元素插入已排序区域。与选择排序不同,堆排序不会浪费时间对未排序区域进行线性时间扫描;相反,堆排序在堆数据结构中维护未排序区域,以便在每个步骤中更快地找到最大(或最小)元素

  堆排序的平均时间复杂度为 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) 运行时比快排更有优势。堆排 是一种原地算法,与快排一样属于不稳定排序
堆排序示意图

堆排序示意图

二、分析

下面例子以升序做分析

  1. 将数据初始化,构建一个大根堆,从最后一个根节点开始调整 ( n 2 − 1 ) (\dfrac{n}{2} - 1) (2n1) ,直到所有根节点调整完
    注:大根堆:所有根节点都比所属的子节点大
  2. 从大根堆中将根节点取出并与未排序的最后一个节点交换,排序检索范围减一
  3. 继续将剩余未排序数据进行大根堆调整
  4. 重复 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

欢迎关注个人公众号,实时推送最新博文!
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值