堆排序算法(图解详细流程)_堆排序的详细过程-程序员宅基地

技术标签: 算法  堆排序  图解过程  堆结构  

堆排序的时间复杂度O(N*logN),额外空间复杂度O(1),是一个不稳定性的排序

目录

一 准备知识

1.1  大根堆和小根堆

二 堆排序基本步骤

2.1 构造堆

2.2 固定最大值再构造堆

三 总结

四 代码

 


一 准备知识

的结构可以分为大根堆和小根堆,是一个完全二叉树,而堆排序是根据的这种数据结构设计的一种排序,下面先来看看什么是大根堆和小根堆

1.1  大根堆和小根堆

性质:每个结点的值都大于其左孩子和右孩子结点的值,称之为大根堆;每个结点的值都小于其左孩子和右孩子结点的值,称之为小根堆。如下图

 我们对上面的图中每个数都进行了标记,上面的结构映射成数组就变成了下面这个样子

还有一个基本概念:查找数组中某个数的父结点和左右孩子结点,比如已知索引为i的数,那么

1.父结点索引:(i-1)/2(这里计算机中的除以2,省略掉小数)

2.左孩子索引:2*i+1

3.右孩子索引:2*i+2

所以上面两个数组可以脑补成堆结构,因为他们满足堆的定义性质:

大根堆:arr(i)>arr(2*i+1) && arr(i)>arr(2*i+2)

小根堆:arr(i)<arr(2*i+1) && arr(i)<arr(2*i+2)

二 堆排序基本步骤

基本思想:

1.首先将待排序的数组构造成一个大根堆,此时,整个数组的最大值就是堆结构的顶端

2.将顶端的数与末尾的数交换,此时,末尾的数为最大值,剩余待排序数组个数为n-1

3.将剩余的n-1个数再构造成大根堆,再将顶端数与n-1位置的数交换,如此反复执行,便能得到有序数组

2.1 构造堆

将无序数组构造成一个大根堆(升序用大根堆,降序就用小根堆)

假设存在以下数组

主要思路:第一次保证0~0位置大根堆结构(废话),第二次保证0~1位置大根堆结构,第三次保证0~2位置大根堆结构...直到保证0~n-1位置大根堆结构(每次新插入的数据都与其父结点进行比较,如果插入的数比父结点大,则与父结点交换,否则一直向上交换,直到小于等于父结点,或者来到了顶端)

插入6的时候,6大于他的父结点3,即arr(1)>arr(0),则交换;此时,保证了0~1位置是大根堆结构,如下图:

                                     (友情提示:待交换的数为蓝色,交换后的数为绿色)

 插入8的时候,8大于其父结点6,即arr(2)>arr(0),则交换;此时,保证了0~2位置是大根堆结构,如下图

插入5的时候,5大于其父结点3,则交换,交换之后,5又发现比8小,所以不交换;此时,保证了0~3位置大根堆结构,如下图 

插入7的时候,7大于其父结点5,则交换,交换之后,7又发现比8小,所以不交换;此时整个数组已经是大根堆结构 

 

2.2 固定最大值再构造堆

此时,我们已经得到一个大根堆,下面将顶端的数与最后一位数交换,然后将剩余的数再构造成一个大根堆

                                    (友情提示:黑色的为固定好的数字,不再参与排序) 

 此时最大数8已经来到末尾,则固定不动,后面只需要对顶端的数据进行操作即可,拿顶端的数与其左右孩子较大的数进行比较,如果顶端的数大于其左右孩子较大的数,则停止,如果顶端的数小于其左右孩子较大的数,则交换,然后继续与下面的孩子进行比较

下图中,5的左右孩子中,左孩子7比右孩子6大,则5与7进行比较,发现5<7,则交换;交换后,发现5已经大于他的左孩子,说明剩余的数已经构成大根堆,后面就是重复固定最大值,然后构造大根堆

如下图:顶端数7与末尾数3进行交换,固定好7,

剩余的数开始构造大根堆 ,然后顶端数与末尾数交换,固定最大值再构造大根堆,重复执行上面的操作,最终会得到有序数组

 

三 总结

到这里,大家应该对堆排序都有了自己的见解,我们对上面的流程总结下:

1、首先将无需数组构造成一个大根堆(新插入的数据与其父结点比较)

2、固定一个最大值,将剩余的数重新构造成一个大根堆,重复这样的过程

四 代码

代码中主要两个方法:

1、将待排序数组构造成一个大根堆(元素上升)

2、固定一个最大值,将剩余的数再构造成一个大根堆(元素下降)

    //堆排序
    public static void heapSort(int[] arr) {
        //构造大根堆
        heapInsert(arr);
        int size = arr.length;
        while (size > 1) {
            //固定最大值
            swap(arr, 0, size - 1);
            size--;
            //构造大根堆
            heapify(arr, 0, size);

        }

    }

    //构造大根堆(通过新插入的数上升)
    public static void heapInsert(int[] arr) {
        for (int i = 0; i < arr.length; i++) {
            //当前插入的索引
            int currentIndex = i;
            //父结点索引
            int fatherIndex = (currentIndex - 1) / 2;
            //如果当前插入的值大于其父结点的值,则交换值,并且将索引指向父结点
            //然后继续和上面的父结点值比较,直到不大于父结点,则退出循环
            while (arr[currentIndex] > arr[fatherIndex]) {
                //交换当前结点与父结点的值
                swap(arr, currentIndex, fatherIndex);
                //将当前索引指向父索引
                currentIndex = fatherIndex;
                //重新计算当前索引的父索引
                fatherIndex = (currentIndex - 1) / 2;
            }
        }
    }
    //将剩余的数构造成大根堆(通过顶端的数下降)
    public static void heapify(int[] arr, int index, int size) {
        int left = 2 * index + 1;
        int right = 2 * index + 2;
        while (left < size) {
            int largestIndex;
            //判断孩子中较大的值的索引(要确保右孩子在size范围之内)
            if (arr[left] < arr[right] && right < size) {
                largestIndex = right;
            } else {
                largestIndex = left;
            }
            //比较父结点的值与孩子中较大的值,并确定最大值的索引
            if (arr[index] > arr[largestIndex]) {
                largestIndex = index;
            }
            //如果父结点索引是最大值的索引,那已经是大根堆了,则退出循环
            if (index == largestIndex) {
                break;
            }
            //父结点不是最大值,与孩子中较大的值交换
            swap(arr, largestIndex, index);
            //将索引指向孩子中较大的值的索引
            index = largestIndex;
            //重新计算交换之后的孩子的索引
            left = 2 * index + 1;
            right = 2 * index + 2;
        }

    }
    //交换数组中两个元素的值
    public static void swap(int[] arr, int i, int j) {
        int temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

                                                              友情提示:手机观看,可以左右滑动 

 

 

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010452388/article/details/81283998

智能推荐

Material Design控件使用(二)_material design 对接菜单控件 menu.horizontaldisplay-程序员宅基地

文章浏览阅读404次。Material Design控件使用(二)_material design 对接菜单控件 menu.horizontaldisplay

Ubuntu11.04安装完全指南_ubuntu11.04安装qq-程序员宅基地

文章浏览阅读2.2k次。出处:http://apps.hi.baidu.com/share/detail/46047864Ubuntu11.04安装完全指南李顺亮2011年5月3日00:44:09现在的世界变化快,早已不再年少的我们,似乎很难适应ubuntu更新的速度。每半年推出一个新版本,是一路跟进,还是抱残守缺,的确让人有些蛋疼。_ubuntu11.04安装qq

Defy刷魔趣2.3.7包~_跃传app下载-程序员宅基地

文章浏览阅读2.4k次。由于用的defy手机一直都是刚入手的时候装的原生态系统,这么久了,系统垃圾文件太多,用起来非常不爽,今天决定把它做了,据说魔趣的系统做的挺不错,决定换成它了。网上的刷机教程挺多的,但这毕竟是自己第一次刷机,多多少少写点啥纪念一下~话说下面就是所做的步骤了: 1.下载几个包,分别是; (1) http://115.com/file/be2uv75t# 这是_跃传app下载

c语言中a lt b a b是什么意思,C语言中c=a<b?a:b是什么意思-程序员宅基地

文章浏览阅读1.6k次。问: ^ 在C语言中是什么意思?答:这是C语言的逻辑运算符:异或这个网站讲的非常详细,我摘抄了一些,详细的你看以看看。有很例子,好懂!逻辑运算符把各个运算的变量(或常量)连接起来组成一个逻辑表达式。逻辑运算符有4个,它们分别是: !(逻辑非)、 ||(逻辑或)、&&(逻辑与) ^(异或)。在位运算里面还有 &(位与)、|(位或)的运算。 什么是逻辑运算--逻辑运算用来判断一件事情是“对”的还是“错..._c语言中c=a

IE浏览器常见的9个css Bug以及解决办法_在ie9上写css的一些bug-程序员宅基地

文章浏览阅读507次。我们在浏览网页的时候经常看见这样的现象:某个网页在IE6浏览器中打开很正常,但是在IE8里面打开可能完全变形了。或者也有可能出现完全相反的现象。这让Web程序员及设计师往往为了其CSS在各个IE版本下表现怪异而痛苦不已,有时候需要通过专为IE6或者IE8设计单独的定义。IE浏览器则因此被公认为Web程序员的毒药,虽然在微软官网上并没有提供相关的解决方案,但是IE浏览器的兼容性存在的问题却是Web程_在ie9上写css的一些bug

求java用人民币来转换美元,NJUPT JAVA语言 综合图形界面程序设计-程序员宅基地

文章浏览阅读873次。一、实验目的和要求学习和理解JAVASWING中的容器,部件,布局管理器和部件事件处理方法。通过编写和调试程序,掌握JAVA图形界面程序设计的基本方法。实验内容:设计和编写一个用于将人民币转换为等值的美元的程序,界面要求可以输入人民币的金额并可以得到转换后的结果。附:程序使用的人民币外汇牌价参考每100元美元等值买入人民币数:619.72(2015/5/23数据)二、实验代码package..._java jframe做货币转换

随便推点

性能调整相关-程序员宅基地

文章浏览阅读108次。Performance Considerations of Data Types本文大意: 主要介绍数据类型的选择,选择尽量小的数据类型,减少表宽就以为这减少磁盘占用空间,以为这减少读入内存后占用的内存,就以为这减少不必要的cpu来读入数据,处理数据,char和varchar最佳实践小于5使用char大于10使用varchar。关于聚集索引的..._磁盘队列长度很长说明什么

一起学开车救命宝典:遭遇爆胎怎么办?_汽车后轮爆胎如何处理驾校宝典-程序员宅基地

文章浏览阅读901次。遭遇爆胎怎么办?行驶中突然爆胎,这种突发状况对于驾驶员肯定会被弄得措手不及,由于爆胎导致车辆行驶轨迹偏差、操控难度加大会让人想尽快与补救,但对于爆胎事故,很多补救措施往往都是百害而无一利,所以如果在行驶时突然爆胎了,一定不要用下意识的方式去补救,比如急转方向盘、大力刹车都是绝对不能采取的措施!在高速公路上,这样做等于是自杀,因为爆胎后采取这类行为的后果就是100%的失控,甚至翻车。爆胎_汽车后轮爆胎如何处理驾校宝典

Java2HTML改造手记(7)-程序员宅基地

文章浏览阅读227次。<!--google_ad_client = "pub-2947489232296736";/* 728x15, 创建于 08-4-23MSDN */google_ad_slot = "3624277373";google_ad_width = 728;google_ad_height = 15;//--><script type="text/javascript"

组件管理工具Bit_bit js 组件管理-程序员宅基地

文章浏览阅读3.2k次,点赞3次,收藏5次。对比Git你就知道Bit是什么了‘Bit loves Git’对,这是官方文档的原话。Git大家再熟悉不过了,世界上最先进的分布式版本控制系统,没有之一,‘近朱者赤’,大概这就是Bit喜欢Git的原因了。开个玩笑,其实是因为Bit的工作流和Git很相似,也是一个分布式工具。Git是管理源文件、源代码的,Bit也是用来管理代码,但不同的是Git不管你代码的语义结构,而Bit是将代码分..._bit js 组件管理

史上最全的机器学习资料(下)-程序员宅基地

文章浏览阅读165次。推荐:史上最全的机器学习资料(上)机器学习(Machine Learning, ML)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。机器学习牵涉的编程语言十分之广,包括了MATLAB、Julia、R、P..._stft stms

SpringBoot实战之增删改查_springboot实现增删改查-程序员宅基地

文章浏览阅读6.4w次,点赞13次,收藏189次。首先我们需要使用IDEA新建一个javaweb项目,步骤图示如下选择File菜单中的Project子菜单,弹出如下图所示窗口在右侧菜单中选择Spring Initial,如上图所示选择JDK的版本,此处为JDK1.8。弹出如下如所示的窗口 如上图所示输入包名、选择Maven构建项目,选择java语言,项目打包方式,选择Java JDK的版本,输入项目名称。点击Next弹出..._springboot实现增删改查

推荐文章

热门文章

相关标签