堆得简单介绍以及堆排序

首先看一下堆的定义:

对于n个元素的序列{k1,k2,k3,……,kn},当且仅当满足下列关系时,称之为堆:

K(i) <= K(2*i) && K(i) <= K(2*i+1)      此时的堆为小顶堆

K(i) >= K(2*i) && K(i) >= K(2*i+1)      此时的堆为大顶堆

(i = 1,2,……,n/2(下取整))

注意:堆得存储是用一维数组来存储的。

若将堆对应的序列看成是一个完全二叉树,则堆得含义表明:

完全二叉树中所有非终端结点的值均不大于(或不小于)其左右孩子结点的值。

因此,若序列{K1,K2,……,Kn} 是大顶堆,则堆顶元素必为序列中n个元素的最大值;反之,若序列是小顶堆,则堆顶元素必为序列中n个元素的最小值。

堆排序就是利用的这个性质。

堆排序的过程如下:

假设要从小到大排序,我们构建一个大顶堆,则堆顶元素是最大值。将堆顶元素和最后一个元素互换,则最后一个元素变成了n个元素中的最大值。之后再将剩下的 n-1 个元素调整成为大顶堆,将堆顶元素和第n-1 个元素互换,则第n-1 个元素变成了n个元素中的次大值……循环这个过程,不断调整堆,最后得到一个有序的序列。

在上面堆排序的过程中,有两个问题需要解决:

(1)如何将一个初始的序列构建成一个大顶堆?

(2)再得到最大元素后,剩下的n-1个元素如何再次调整成为一个大顶堆?

实际上,初始序列构建大顶堆也是一个不断调整堆得过程。因此,只要解决第二个问题就可以。

下图是一个大顶堆:

当把堆顶元素20和最后一个元素互换之后,最后一个元素变成了序列中的最大值。如下图:

但是,此时堆顶元素违反了大顶堆的性质,堆顶元素的左右孩子仍旧满足大顶堆的性质。因此,此时需要对堆进行调整。因为左子树的值大于右子树的值,所以将3和17互换,如下图:

此时,左子树又违反了大顶堆得性质,所以需要调整左子树,如下图:

至此,一次调整完毕,堆顶元素成为了次大元素。

实际上,调整堆就是这样一个不断筛选比较的过程,不断的和左右子树比较,一直到不需要交换为止。

将一个无序序列构建成一个大顶堆的过程就是一个反复筛选的过程。将此序列看成是一个完全二叉树,则最后一个非叶子节点是第 n/2(下取整)个元素,因此,筛选只需从第 n/2(下取整)个元素开始。

假设有序列:{49,38,65,97,76,13,27},初始二叉树是:

从第3个元素,也就是65开始调整堆,65大于左右子树的值,因此不需要调整。

然后是第2个元素,也就是从38开始调整堆,38和左右子树比较,将97和38互换,调整后如下图:

然后是第1个元素,也就是从49开始调整堆,49和左右子树比较,将97和49互换,互换之后,因为49破坏了左子树大顶堆的性质,因此需要继续调整,将49和左右子树比较,然后将49和76互换,调整过程如下图:

至此,将一个无序的序列调整成为了一个大顶堆。

同理,堆排序也分为两个过程:(1)将初始化序列调整成为一个大顶堆(2)用最后一个元素和堆顶元素交换,然后不断调整剩下的元素成为一个新的大顶堆。

代码如下:

const int N = 8;
int num[N] = {-1,49,38,65,97,76,13,27};  //从第一个元素开始存储

//调整堆的函数
void heapAdjust(int pos,int total){
	int temp = num[pos];
	for(int j = 2 * pos; j <= total; j *= 2){
		if(j < total){  //说明还有右子树
			if(num[j] < num[j + 1]){   //筛选出左右子树中较大的值
				j += 1;
			}
		}
		if(temp >= num[j])  //不需要再继续向下调整了
			break;
		num[pos] = num[j];
		pos = j;
	}
	num[pos] = temp;
}

void heapSort(){
	//首先将数组构建成一个大顶堆
	for(int i = (N-1) / 2;i >=1;--i){
		heapAdjust(i,N - 1);
	}
	//开始堆排序
	for(int i = N-1; i > 1; --i){
		int temp = num[i];   //交换第一个元素和最后一个元素
		num[i] = num[1];
		num[1] = temp;
		heapAdjust(1,i - 1);  //交换完之后,重新调整堆
	}
}

  

时间: 2024-10-11 23:16:53

堆得简单介绍以及堆排序的相关文章

算法——堆的简单介绍

一.什么是堆? 堆:一种特殊的完全二叉树结构. 大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大: 小根堆:一棵完全二叉树,满足任一节点都比其他孩子节点小. 原文地址:https://www.cnblogs.com/xiugeng/p/9645972.html

JVM内存各个区域分工简单介绍

JVM内存各个区域简单介绍: 程序计数器:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 在使用多线程时,为了线程切换后能恢复到正确的执行位置,每条线程都需要有个独立的程序计数器,各条线程之间计数器互不影响,独立存储,我们称这类内存区域为"线程私有"的内存.如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址:如果正在执行的是Native方法,这个计数器值则为空(Undefined). 此内存区域是唯一一个在Java

Android——DDMS简单介绍

DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务. 它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息.Logcat.广播状态信息.模拟电话呼叫.接收SMS.虚拟地理坐标等等. 一,切换DDMS视图 在左侧的Devices中,可以看到正在运行的虚拟设备. 二,DDMS各个菜单简单介绍 Debug Process 断点调试程序: 个人总结断点调试程序有2种情况.1.eclipse中的

内存数据网格IMDG简单介绍

1 简单介绍 将内存作为首要存储介质不是什么新奇事儿,我们身边有非常多主存数据库(IMDB或MMDB)的样例.在对主存的使用上.内存数据网格(In Memory Data Grid,IMDG)与IMDB相似,但二者在架构上全然不同. IMDG特性能够总结为下面几点: ?  数据是分布式存储在多台server上的. ?  每台server都是active模式. ?  数据模型一般是面向对象和非关系型的. ?  依据须要.常常会增减server. 此外,IMDG与普通缓存系统也是不同的. 相同地,在

OC基础(十二)内存简单介绍和OC的内存管理

一.内存简单介绍 内存结构 1.运行时分配 (1)栈:用户存放程序临时创建的局部变量(先进后出). (2)堆:动态分配内存段. 2.编译器分配 (1)BSS段:存放未初始化的全局变量和静态变量. (2)数据段:已初始化的全局变量和静态变量. (3)代码段:执行代码的一块区域. 地址由低到高:代码段 -> 数据段 -> BSS段-> 堆 -> 栈 内存分配方式 1.从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量

一个性能较好的jvm參数配置以及jvm的简单介绍

一个性能较好的webserverjvm參数配置: -server //服务器模式 -Xmx2g //JVM最大同意分配的堆内存,按需分配 -Xms2g //JVM初始分配的堆内存.一般和Xmx配置成一样以避免每次gc后JVM又一次分配内存. -Xmn256m //年轻代内存大小.整个JVM内存=年轻代 + 年老代 + 持久代 -XX:PermSize=128m //持久代内存大小 -Xss256k //设置每一个线程的堆栈大小 -XX:+DisableExplicitGC //忽略手动调用GC,

Android发展简单介绍

Android一词的本义指“机器人”,同一时候也是Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统.中间件.用户界面和应用软件组成,号称是首个为移动终端打造的真正开放和完整的移动软件.眼下最好的是Android2.0的摩托罗拉Droid Android公司介绍 国家:美国 业务:手机软件,操作系统 成立于:2003年 创办人:Andy Rubin,Andy McFadden.Richard Miner Chris White 中文名:安卓 眼下

java中equals以及==的用法(简单介绍)

简单介绍 equals方法是java.lang.Object类的方法 有两种用法说明: 一.对于字符串变量来说,使用"=="和"equals()"方法比较字符串时,其比较方法不同. 1."=="比较两个变量本身的值,即两个对象在内存中的首地址. (java中,对象的首地址是它在内存中存放的起始地址,它后面的地址是用来存放它所包含的各个属性的地址,所以内存中会用多个内存块来存放对象的各个参数,而通过这个首地址就可以找到该对象,进而可以找到该对象的各

python的列表,元组和字典简单介绍

引 入 java                                   python 存取多个值:数组或list集合 ------------------------> 列表,元组 key-value格式:    Map        ------------------------>    字典 自己学习发现,java跟python这两门面向对象语言在数据类型的定义上,很多思想都是互通的,这里不说java,简单介绍一下python的列表,元组和字典. 一.列表 List: 最通