排序算法(5)--Selection Sorting--选择排序[2]--Heap Sort--堆排序
1.基本思想
具有n个元素的序列 (h1,h2,...,hn),当且仅当满足(hi>=h2i,hi>=2i+1)或(hi<=h2i,hi<=2i+1) (i=1,2,...,n/2)时称之为堆。在这里只讨论满足前者条件的堆。由堆的定义可以看出,堆顶元素(即第一个元素)必为最大项(大顶堆)。完全二 叉树可以很直观地表示堆的结构。堆顶为根,其它为左子树、右子树。
2.实现原理
初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大。然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆。依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列。从算法描述来看,堆排序需要两个过程,一是建立堆,二是堆顶与堆的最后一个元素交换位置。所以堆排序有两个函数组成。一是建堆的渗透函数,二是反复调用渗透函数实现排序的函数。

一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+1和2*i+2。
(注:如果根结点是从1开始,则左右孩子结点分别是2i和2i+1。)
如第0个结点左右子结点下标分别为1和2。
3.代码实例
(1)代码:
//使树成为一个大顶堆:顶部最大
public void HeapAdjust(int[] array, int parent, int length) {
int temp = array[parent]; // temp保存当前父节点
int child = 2 * parent + 1; // 先获得左孩子
while (child < length) {
// 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
if (child + 1 < length && array[child] < array[child + 1]) {
child++;
}
// 如果父结点的值已经大于孩子结点的值,则直接结束
if (temp >= array[child])
break;
// 把孩子结点的值赋给父结点
array[parent] = array[child];
// 选取孩子结点的左孩子结点,继续向下筛选
parent = child;
child = 2 * child + 1;
}
array[parent] = temp;
} public void heapSort(int[] list) {
// 循环建立初始堆
for (int i = list.length / 2; i >= 0; i--) {
HeapAdjust(list, i, list.length - 1);
}
// 进行n-1次循环,完成排序
for (int i = list.length - 1; i > 0; i--) {
// 最后一个元素和第一元素进行交换
int temp = list[i];
list[i] = list[0];
list[0] = temp; // 筛选 R[0] 结点,得到i-1个结点的堆
HeapAdjust(list, 0, i);
System.out.format("第 %d 趟: \t", list.length - i);
printPart(list, 0, list.length - 1);
}
} // 打印序列
public void printPart(int[] list, int begin, int end) {
for (int i = 0; i < begin; i++) {
System.out.print("\t");
}
for (int i = begin; i <= end; i++) {
System.out.print(list[i] + "\t");
}
System.out.println();
} public static void main(String[] args) {
// 初始化一个序列
int[] array = {
70, 60, 12, 40, 30, 8, 10
}; // 调用快速排序方法
HeapSort heap = new HeapSort();
System.out.print("排序前:\t");
heap.printPart(array, 0, array.length - 1);
heap.heapSort(array);
System.out.print("排序后:\t");
heap.printPart(array, 0, array.length - 1);
}
(2)结果:
排序前: 70 60 12 40 30 8 10
第 1 趟: 60 40 12 10 30 8 70
第 2 趟: 40 30 12 10 8 60 70
第 3 趟: 30 10 12 8 40 60 70
第 4 趟: 12 10 8 30 40 60 70
第 5 趟: 10 8 12 30 40 60 70
第 6 趟: 8 10 12 30 40 60 70
排序后: 8 10 12 30 40 60 70
4.算法分析
堆排序也是一种不稳定的排序算法。
堆排序优于简单选择排序的原因:
简单选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行了这些比较操作。
堆排序可通过树形结构保存部分比较结果,可减少比较次数。
堆排序的最坏时间复杂度为O(nlogn)。堆序的平均性能较接近于最坏性能。由于建初始堆所需的比较次数较多,所以堆排序不适宜于记录数较少的文件。
5.排序特点
堆排序是一种树形选择排序,是对简单选择排序的有效改进。
排序算法(5)--Selection Sorting--选择排序[2]--Heap Sort--堆排序的更多相关文章
- 排序算法Java实现(选择排序)
算法描述:对于给定的一组记录,经过第一轮比较后得到最小的记录,然后将该记录与第一个记录的位置进行交换:接着对不包括第一个记录以外的其他记录进行第二轮比较,得到最小的记录并与第二个记录进行位置交换:重复 ...
- 排序算法c语言描述---选择排序
排序算法系列学习,主要描述冒泡排序,选择排序,直接插入排序,希尔排序,堆排序,归并排序,快速排序等排序进行分析. 文章规划: 一.通过自己对排序算法本身的理解,对每个方法写个小测试程序. 具体思路分析 ...
- 常见排序算法总结分析之选择排序与归并排序-C#实现
本篇文章对选择排序中的简单选择排序与堆排序,以及常用的归并排序做一个总结分析. 常见排序算法总结分析之交换排序与插入排序-C#实现是排序算法总结系列的首篇文章,包含了一些概念的介绍以及交换排序(冒泡与 ...
- 排序算法(4)--Selection Sorting--选择排序[1]--Simple Selection Sort--简单(直接)选择排序
1.基本思想 在要排序的一组数中,选出最小的一个数与第一个位置的数交换:然后在剩下的数当中再找最小的与第二个位置的数交换,如此循环到倒数第二个数和最后一个数比较为止. 2.实现原理 每趟从待排序的 ...
- Java常用的经典排序算法:冒泡排序与选择排序
一.冒泡排序 冒泡排序(Bubble Sort)是一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为 ...
- Java排序算法(二):简单选择排序
[基本思想] 在要排序的一组数中.选出最小的一个数与第一个位置的数交换:然后在剩下的数中再找出最小的与第二个位置的数交换,如此循环至倒数第二个数和最后一个数比較为止. 算法关键:找到最小的那个数.并用 ...
- 【排序算法】——冒泡排序、选择排序、插入排序、Shell排序等排序原理及Java实现
排序 1.定义: 所谓排序,即是整理文件中的内容,使其按照关键字递增或递减的顺序进行排列. 输入:n个记录,n1,n2--,其对应1的关键字为k1,k2-- 输出:n(i1),n(i2)--,使得k( ...
- 排序算法lowb三人组-选择排序
def get_min_pos(li): min_pos = 0 for i in range(1, len(li)): if li[i] < li[min_pos]: min_pos = i ...
- Java排序算法(二)选择排序
一.测试类SortTest import java.util.Arrays; public class SortTest { private static final int L = 20; publ ...
随机推荐
- sudoer命令各参数含义及设置
对于普通用户sudo加权的时限制可以执行的命令 https://segmentfault.com/a/1190000007394449 需要修改/etc/sudoers 1. 字段说明如下 授权用户/ ...
- 关于axios及其在vue中的配置
什么是axios?官方解释:axios 是一个基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中. 具有以下特点: 从浏览器中创建 XMLHttpRequests 从 nod ...
- sync.WaitGroup和sync.Once
sync.WaitGroup,顾名思义,等待一组goroutinue运行完毕.sync.WaitGroup声明后即可使用,它有如下方法: func (wg *WaitGroup) Add(delta ...
- win10关机之后自动重启(系统更新之后出现这个问题)
最近更新了一把win10之后出现无法关机,关机之后直接又开机,无限循环状态.最近几天没空处理一直是强关笔记本下班的. 今天打了一把命令: shutdown /s /t 0 发现关机正常,本来打算整个脚 ...
- python获取动态网站上面的动态加载的数据(初级)
我们在处理一些网站数据的时候,有时候我们需要的数据很多都是动态加载的,而不都是静态的,以下以一个实例来介绍简单的获取动态数据,首先申明本人小白,还在学习python中,这个方法还是比较笨拙的,但是对于 ...
- JavaScript与CSS相对路径引用的差异
转自:http://blog.csdn.net/luohuidong01/article/details/74938652 JS跟CSS相对路径引用的差异在于他们的参考点不一样,下面举个例子说明一下. ...
- Docker概念学习系列之Docker核心概念之镜像Image
不多说,直接上干货! 说明: Docker 运行容器之前需要本地存在对应的镜像,如果镜像不存在,Docker 会尝试先从默认镜像仓库下载(默认使用Docker Hub公共注册服务器中的仓库),用户 ...
- mybatis随笔一之SqlSessionFactoryBuilder
SqlSessionFactoryBuilder是构建sqlSessionFactory的入口类 从该类的方法可知,它是通过不同的入参来构造SqlSessionFactory,除了最后一个config ...
- MyCAT 源码解析 —— 分片结果合并(使用unsaferow)
1. 概述 2. 多分片执行 SQL 3. 合并多分片结果 3.1 记录头(header) 3.2 记录行(row) 3.1 AbstractDataNodeMerge 3.2 DataNodeMer ...
- ABP框架服务层的接口与实现(增删改查)
public interface ITaskAppService : IApplicationService { IList<TaskDto> GetAllTasks();//所有 Get ...