堆排序的学习

几句废话先:



本人最近在学习研究数据结构和算法,之所以为什么学习它,第一:是我看到到了现在学习的瓶颈,一直停留在使用别人的什么什么框架或者算法之类的东西,没有对程序更深层次的学习探究。第二:想在IT行业走得更加的长远,数据结构是必须要迈的一道坎。

另外本人从上一篇博客开始已经步入javascript,不在或者很少探讨.NET平台相关内容。

主题:



arr.heap-size

至于什么事堆,我在这里也不再的啰嗦,阅读本博文,已经默认你认识它,如果您是大牛,请忽视本博文或者找我的错,我很欢迎  :)

在堆排序之前我们要先知道一个非常基础的一个东西就是arr.heap-size。我刚开始看的时候,对这个不怎么重视,觉得,它不就是等于arr.length吗?并且在学习之初的建堆上,也没有多大的用处,但是在后面的排序中,优化性能方面它发挥了很大的作用。它的定义说白了就是在一个数组arr中有效的元素个数,换句话说:arr[1,arr.length]中可能有数据,但是在arr[1,arr.heap-size]中存放的是有效的数据。

最大堆

在有些书上,对于最大堆的定义有些啰嗦(个人觉得),其实说白了就是除了根节点外,任何一个孩子的根节点相对其孩子来说是最大的。至于最小堆就是它的反向。最小堆是用于构造优先队列的,这里暂时不说,毕竟现在都23点了,明儿还要早起,扯远了。。在以后的博文中会陆续讲解。最大堆的性质也就是它的定义所说的,维护最大堆的性质的时间复杂度是大Ο(logN) ,这里的N不是方!!

我们先来看看怎么样去维护一个堆的性质:

下面用JS吧,毕竟现在日常就是用JS,啥语言都一样:

var arr = [4,1,3,2,16,9,10,14,8,7],
    heapLen = arr.length,
    halfLen = heapLen / 2,
    laster;

function buidHeap(i){
	var left = 2 * i + 1,
		right = 2 * i + 2;
	if(left < heapLen && arr[left] > arr[i]){
		laster = left;
	}else{
		laster = i;
	}

	if(right < heapLen && arr[right] > arr[laster]){
		laster = right;
	}

	if(laster !== i){
		var temp;
		temp = arr[i];
		arr[i] = arr[laster];
		arr[laster] = temp;
		buidHeap(laster);
	}
}

 维护最大堆的一个最主要的思想就是始终坚持它的性质,我们在arr[i]、arr[left]和arr[right]中选出最大的,并且将它的下标保存在laster中,但是在交换了之后考虑到还要继续维护它的性质,我们可以进行递归的调用。维护一个最大堆的代价也就是两个:第一:arr[i]、arr[left]和arr[right]的调换是常数时间,任意一个孩子的子树是≤2/3 * n的

所以它的时间T(n) = T(2/3 *N);

在上面我们只是在维护最大堆的性质,那么我们怎么去建堆那?

var arr = [4,1,3,2,16,9,10,14,8,7],
    heapLen = arr.length,
    halfLen = heapLen / 2,
    laster;

function buidHeap(i){
	var left = 2 * i + 1,
		right = 2 * i + 2;
	if(left < heapLen && arr[left] > arr[i]){
		laster = left;
	}else{
		laster = i;
	}

	if(right < heapLen && arr[right] > arr[laster]){
		laster = right;
	}

	if(laster !== i){
		var temp;
		temp = arr[i];
		arr[i] = arr[laster];
		arr[laster] = temp;
		buidHeap(laster);
	}
}

function exe(cb){
	for(var i = heapLen;i >= 0;i--){
		buidHeap(i);
	}
	cb();
}

  最下面的exe()函数就是一个建堆的过程。

到这我们的堆就已经建好了,我们的堆排序怎么实现?

实现堆排序:



这里有两个办法,第一个将已经刷选出的元素放在另外一个数组中,因为在最大堆中,根节点最大,所以我们每移除一次根节点都要在进行一次最大堆性质的维护。但是复制到新的一个数组这样就消耗了两倍的空间,这样做虽然可以实现最终的目的,但是还是不怎么好。第二个办法就是在本博文开始不是说到heap-size了吗,在这里就用上了,它表示的是有效元素,既然是有效的,我们每一次刷选后,把最大元素放在最后,同时heap-size - 1;这样不就节省了空间了吗?

代码:

var arr = [4,1,3,2,16,9,10,14,8,7],
    heapLen = arr.length,
    halfLen = heapLen / 2,
    laster;

function buidHeap(i){
	var left = 2 * i + 1,
		right = 2 * i + 2;
	if(left < heapLen && arr[left] > arr[i]){
		laster = left;
	}else{
		laster = i;
	}

	if(right < heapLen && arr[right] > arr[laster]){
		laster = right;
	}

	if(laster !== i){
		var temp;
		temp = arr[i];
		arr[i] = arr[laster];
		arr[laster] = temp;
		buidHeap(laster);
	}
}

function exe(cb){
	for(var i = heapLen;i >= 0;i--){
		buidHeap(i);
	}
	cb();
}

function buidMaxHeap(cb){
	exe(function(){
		console.log(‘------最大堆已建好------‘);
		for(var i = arr.length - 1;i>=1;i--){
			var temp;
			temp = arr[0];
			arr[0] = arr[i];
			arr[i] = temp;
			heapLen -= 1;
			buidHeap(0);
		}
	});
	cb();
};

(function heapsort(){
	buidMaxHeap(function(){
		console.log(‘------堆排序已派好------‘);
		for(var i = 0;i<arr.length;i++){
			console.log(arr[i]);
		}
	});
})();

最后想说:



本博文只是对堆排序的浅意义上的说明,没有更深次的探讨它的时间复杂度,第一个原因就是一些数学符号弄不出来,第二个就是:我个人语言表现力不足,你可以Google之。

本博文属于原创,允许转载,但请标明原始连接来源!!

堆排序的学习,布布扣,bubuko.com

时间: 2024-12-16 21:23:30

堆排序的学习的相关文章

堆排序算法学习小记

1.完全二叉树的概念 若设二叉树的深度为h,除第 h 层外,其它各层 (1-h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树. 完全二叉树是由满二叉树而引出来的.对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树. (1)所有的叶结点都出现在第k层或k-l层(层次最大的两层) (2)对任一结点,如果其右子树的最大层次为L,则其左子树的最大层次为L或L+l. 一棵二叉树至多只有最下面

算法学习之排序的那些事(一)

????????排序的目的就是对一组无序的元素按照一定的次序排列起来.那么总的来说排序要做到事情就只有两件,找到各个元素按照一定次序排列后的位置并把各个元素移动到其所对应的位置.由此看出决定一个排序算法效率的因素也就是这两个: 寻找元素位置所消耗的时间 移动元素到其对应位置所消耗的时间 下面是一些常用的排序算法: 冒泡排序: 算法描述:算法比较简单就不多描述了. 实现步骤: 对N个元素做N-1次循环(每次循环至少可以把一个元素移动的正确的位置,N-1次循环后便把N个元素排列好了) 每次循环进行N

【算法导论】学习笔记——第6章 堆排序

堆这个数据结构应用非常广泛,数字图像处理的算法里也见过.似乎记得以前老师上课说需要用树结构实现堆排序,看了一下算法导论才明白其精髓.堆虽然是一棵树,但显然没必要非得用树结构实现堆排序.堆排序的性质很好,算法时间复杂度为O(nlgn). 1. 堆排序的简要说明.二叉堆可以分为两种形式:最大堆和最小堆.在最大堆中,最大堆性质是指除了根以外的所有结点i都要满足: A[PARENT(i)] >= A[i]:在最小堆中,最小堆性质是指除了根以外的所有结点i都要满足: A[PARENT(i)] <= A[

[数据结构学习备忘录]堆及其堆排序

[数据结构学习备忘录] 堆 一种数据结构,物理存储方式:数组 逻辑存储方式:近似于完全二叉树,假定i为堆元素的序数[Index],那么i/2就是该元素的左子树,(i/2 + 1)就是该元素的右子树,分为两种堆:大根堆.小根堆:这两种堆的区别是:大根堆的根节点元素的值比左右子树的值都要大,小根堆则相反. 可用这种数据结构进行排序,称为堆排序. 与该数据结构相关的关键算法: ①   MaxHeaplfy 当堆的元素顺序出现错误时的调整函数 ②   BulidMax[min]Heap 建立大[小]根堆

排序算法学习之堆排序

一.堆与堆排序的产生及定义 在简单选择排序中,每次从n个元素中比较n-1次选取最小的元素,这很好理解,但是前面比较过的数据在之后还要重新比较,这将花费大量的运算时间.堆排序算法就很好的解决了这个问题,堆排序在每次选择到最小记录的同时会根据比较结果对其他数据进行调整,堆排序的时间复杂度为O(NlogN). 堆通常是指二叉堆,即堆是一颗完全二叉树,同时其满足一定性质:每个节点的值大于等于其左右孩子的值(大顶堆),或者每个节点的值小于等于其左右孩子的值(小顶堆).堆在本质上是一个数组,根节点即为a[0

堆排序学习以及模板

堆排序学习以及模板 堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法.堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点. 堆排序的平均时间复杂度为Ο(nlogn) . 算法步骤: 1.创建一个堆H[0..n-1](利用MaxHeapify函数从最后一个非叶子节点开始调整堆,如BuildMaxHeap函数调整为大根堆). 2.把堆首(最大值)和堆尾互换(取堆顶元素,因为其总是当前堆中最大或者最小的) 3.把堆的尺寸缩小1,

堆排序-学习笔记

在学习堆排序之前首先了解一下二叉堆的特性: 1.二叉堆的父节点的值总是大于等于(或小于等于)其左右孩子的值: 2.每个节点的左右子树都是一棵这样的二叉堆. 如果该二叉堆的父节点总是大于孩子节点,则叫做最大堆,如果父节点小于孩子节点,则叫做最小堆. 在堆排序的应用中,如果递增排序,则应该使用最大堆,反之,使用最小堆. 堆排序是不稳定的 堆排序主要有两个步骤来完成堆排序: 1.把一个无序数列构造成一个最大堆或最小堆: 2.去掉根节点(堆顶元素),把剩下的元素重新构造一个二叉堆. 二叉堆的存储结构为一

[算法学习笔记]排序算法——堆排序

堆排序 堆排序(heapsort)也是一种相对高效的排序方法,堆排序的时间复杂度为O(n lgn),同时堆排序使用了一种名为堆的数据结构进行管理. 二叉堆 二叉堆是一种特殊的堆,二叉堆是完全二叉树或者是近似完全二叉树.二叉堆满足堆特性:父节点的键值总是保持固定的序关系于任何一个子节点的键值,且每个节点的左子树和右子树都是一个二叉堆. 如上图显示,(a)是一个二叉堆(最大堆), (b)是这个二叉堆在数组中的存储形式. 通过给个一个节点的下标i, 很容易计算出其父节点,左右子节点的的下标,为了方便,

排序再学习 - 冒泡、快速、归并、堆排序

1. 冒泡排序 每次比较数组中的两个数,如果和你期望的顺序不一致,就交换这两个数,一次循环下来能将一个数摆在正确的位置上.外层循环共需要N-1次,因为N-1个数都已经摆在正确的位置上,那第N个数也已经是正确的了.内层循环也可以是N-1次,也可以每次都比上一次少循环一次,第一种情况会比较已经排好序的部分,第二种情况已经排序过的部分不需要再比较了.还是两层循环都是N-1次比较好写. void bubble_sort(int array[], int n) { int i, j, temp; for