【数据结构】【C#】018-选择类排序:🔊堆排序(不稳定)

选择排序:堆排序(不稳定)

堆的定义:n 个元素的序列 (k1, k2, …, kn),当且仅当满足下列关系:任何一个非终端结点的值都大于等于(或小于等于)它左右孩子的值时,称之为堆。若序列{k1,k2,…,kn}是堆,则堆顶元素(即完全二叉树的根)必为序列中n个元素的最小值(或最大值) 。

可将堆序列看成完全二叉树,则: k2i 是 ki 的左孩子; k2i+1 是 ki 的右孩子。所有非终端结点的值均不大(小)于其左右孩子结点的值。堆顶元素必为序列中 n 个元素的最小值或最大值。

若:ki <= k2i , ki <= k2i+1,也就是说父小孩大,则为小顶堆(小根堆,正堆),反之,父大孩小,叫大顶堆(大根堆,逆堆)

【堆排序基本思想】:将无序序列建成一个堆,得到关键字最小(大)的记录;输出堆顶的最小(大)值后,将剩余的 n-1 个元素重又建成一个堆,则可得到 n 个元素的次小值;如此重复执行,直到堆中只有一个记录为止,每个记录出堆的顺序就是一个有序序列,这个过程叫堆排序。

堆排序需解决的两个问题:

1、如何由一个无序序列建成一个堆?

2、在输出堆顶元素后,如何将剩余元素调整为一个新的堆?


第二个问题解决方法——筛选:

所谓“筛选”指的是,对一棵左/右子树均为堆的完全二叉树,“调整”根结点使整个二叉树也成为一个堆。具体是:输出堆顶元素之后,以堆中最后一个元素替代之;然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换;重复上述操作,直至叶子结点,将得到新的堆,称这个从堆顶至叶子的调整过程为“筛选”。

例: (13, 38, 27, 49, 76, 65, 49, 97)

image

输出堆顶元素之后,以堆中最后一个元素替代之;

image

然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换

image
image

输出堆顶元素之后,以堆中最后一个元素替代之;

image

然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换

image
image

输出堆顶元素之后,以堆中最后一个元素替代之;

image

然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换

image

输出堆顶元素之后,以堆中最后一个元素替代之;

image

然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换

image

输出堆顶元素之后,以堆中最后一个元素替代之;

image

然后将根结点值与左、右子树的根结点值进行比较,并与其中小者进行交换

image
image

对深度为 k 的堆,“筛选”所需进行的关键字比较的次数至多为 2(k-1)。


第一个问题解决方法:

从无序序列的第n/2个元素(即无序序列对应的完全二叉树的最后一个内部结点)起,至第一个元素止,进行反复筛选。建堆是一个从下往上进行“筛选”的过程。把原始的序列一一对应的(从左到右,从上到下)建立一个完全二叉树即可,然后建立小顶堆(大顶堆)

image

1、建堆

image

2、调整,筛选过程

image
image
image
image
image

一趟堆排序完毕,选出了最值和堆里最后一个元素交换,继续

image
image
image

第二趟堆排序完毕,选出了次最值和剩下的元素的最后一个元素交换

image
image
image

第三趟堆排序完毕,重复往复,这样进行堆调整。

image
image

第四躺排序完毕,继续

image
image

第五躺排序完毕

image
image

第六趟排序完毕

image

最后全部堆排序完毕

image

这是整个建堆,调整为小顶堆的过程,也就是递增排序。具体是自上而下调整完全二叉树里的关键字,使其成为一个大顶堆(递减排序过程)

操作过程如下:

1、初始化堆:将R[1..n]构造为堆;

2、将当前无序区的堆顶元素R[1]同该区间的最后一个记录交换,然后将新的无序区调整为新的堆。

对于堆排序,最重要的两个操作就是构造初始堆和调整堆,其实构造初始堆事实上也是调整堆的过程,只不过构造初始堆是对所有节点都进行调整


C#代码实现:

自定义工具类:

参考笔记-016

堆排序类:

/// <summary>
/// 选择类排序
/// </summary>
public class SelectSort
{

    //调整推
    public static void HeapAdjust(int[] List, int s, int last) //s 为临时堆顶 索引  last 为 堆尾索引
    {
       
        int maxTemp = s;

        int sLChild = 2 * s;   // s 节点的 左孩子索引
        int sRChild = 2 * s + 1; // s 节点的 右孩子索引


        if (s <= last / 2)  //完全二叉树的叶子结点不需要调整,没有孩子
        {
            if (sLChild <= last && List[sLChild] > List[maxTemp])//如果 当前 结点的左孩子比当前结点记录值大,调整,大顶堆
            {
                //更新 maxtemp
                maxTemp = sLChild;
            }
            if (sRChild <= last && List[sRChild] > List[maxTemp])//如果 当前 结点的右孩子比当前结点记录值大,调整,大顶堆
            {
                //更新 maxtemp
                maxTemp = sRChild;
            }

            //如果调整了就交换,否则不需要交换
            if (List[maxTemp] != List[s])
            {
                //不使用中间变量交换两数的值
                List[maxTemp] = List[maxTemp] + List[s];
                List[s] = List[maxTemp] - List[s];
                List[maxTemp] = List[maxTemp] - List[s];

                //交换完毕,防止调整之后的新的以 maxtemp 为父节点的子树不是大顶堆,再调整一次
                HeapAdjust(List, maxTemp, last);
            }

        }
    }

    //建立堆
    public static void BuildHeap(int[] List, int last)
    {
        //明确,具有 n 个结点的完全二叉树(从左到右,从上到下),编号后,有如下关系,设 a 结点编号为 i,若 i 不是第一个结点,那么 a 结点的双亲结点的编号为[i / 2]

        //非叶节点的最大序号值为 last / 2
        for (int i = last / 2; i >= 0; i--)
        {
            //从头开始调整为大顶堆
            HeapAdjust(List, i, last);
        }
    }

    /// <summary>
    /// 3、选择排序:堆排序
    /// </summary>
    /// <param name="List">数组</param>    
    public static void HeapSort(int[] List)
    {
        //建立大顶堆
        BuildHeap(List, List.Length-1);

        //调整堆
        for (int i = List.Length - 1; i >= 1; i--)
        {
            //将堆顶记录和当前未经排序子序列中最后一个记录相互交换
            //即每次将剩余元素中的最大者list[0] 放到最后面 list[i]
            List[i] = List[i] + List[0];
            List[0] = List[i] - List[0];
            List[i] = List[i] - List[0];

            //重新筛选余下的节点成为新的大顶堆
            HeapAdjust(List, 0, i - 1);
            Utilit.Print(List, i);
        }
    }
}

测试用例:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class _013_SelectSort : MonoBehaviour
{


    void Start()
    {
        int[] a = Utilit.RandArray(20, 10);

        int[] b = (int[])a.Clone();

        Debug.Log("---------------简单选择排序--------------");
        SelectSort.SampleSelectSort(a);

        //Debug.Log("-------------错误简单选择排序--------------");
        //SelectSort.ErrorSampleSelectSort(b);

        Debug.Log("-------------选择排序:堆排序--------------");
        SelectSort.HeapSort(b);

    }

}

输出结果:

img.jpg

注:

堆排序的时间复杂度和空间复杂度:

  1. 对深度为 k 的堆,“筛选”所需进行的关键字比较的次数至多为 2(k-1);

  2. 对 n 个关键字,建成深度为
    image

的堆,所需进行的关键字比较的次数至多 4n;

  1. 调整“堆顶” n-1 次,总共进行的关键字比较的次数不超过
    image

,因此,堆排序的时间复杂度为 O(nlogn),与简单选择排序 O(n^2) 相比时间效率提高了很多。

空间复杂度:S(n) = O(1)

堆排序是一种速度快且省空间的排序方法。相对于快速排序的最大优点:最坏时间复杂度和最好时间复杂度都是 O(n log n),适用于记录较多的场景(记录较少就不实用),类似折半插入排序,在 T(n)=O(n log n)的排序算法中堆排序的空间复杂度最小为1。

堆排序的稳定性:不稳定排序算法
[参考文献]https://www.cnblogs.com/kubixuesheng/p/4359406.html


选择类排序总结:

img.jpg
img.jpg
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 1)这本书为什么值得看: Python语言描述,如果学的Python用这本书学数据结构更合适 2016年出版,内容...
    孙怀阔阅读 12,438评论 0 15
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,654评论 0 13
  • 第一章 绪论 什么是数据结构? 数据结构的定义:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。 第二章...
    SeanCheney阅读 5,741评论 0 19
  • 天雨湿阶苔, 云开几浮白。 花枝出清瘦, 桃颜一笑开。
    乱棋阅读 181评论 0 0
  • 爱到肋骨里 累到手无缚鸡之力 心还想 勒紧你
    段童阅读 278评论 0 2