Hark的数据结构与算法练习之堆排序

前言

堆排序我是看了好半天别人的博客才有了理解,然后又费了九牛二虎之力才把代码写出来,我发现我的基础真的很差劲啊……不过自己选的路一定要坚持走下去。
我试着把我的理解描述出来,如有不妥之处希望大家可以指点出来

算法说明

堆排序,是基于堆的排序。 堆也就是二叉树的一种(完全二叉树),首先要确定堆的定义,才可以学会堆算法的逻辑;

OK,我们知道堆的定义前得先确定啥是完全二叉树。

二叉树就是树状结构是这样的,如图:

通常二叉树都会存放在数组中,那么将上图的完全二叉树放在数组中就是int[] arrayData = {1,2,3,17,19,36,7,25,100}

然后还有一个前置知识要知道,就是如何计算左子孩子的索引和右子孩子的索引。

还是以上图来举例子,3,36,7这三个数中, 3就是父节点,36 和 7就是子节点。  然后左子节点和右子节点的计算公式是i*2+1 和 i*2+2  。 替换到我举的例子中就是,数字3在数组中的索引是2,那么根据公式所述,左子节点的数组索引是2*2+1=5,右子节点的数组索引是2*2+2=6。 大家可以对一下数字36和数字7的数组索引和我们计算出来的是否一致。

还有,父节点的索引公式是 (i-1)/2。 大家可以代入一下上面的数组求一下值

OK,下面我们可以看一下堆的定义了。

堆的定义是这样的:

如图,堆分两种,一种是“小顶堆”,另一种是“大顶堆“。

小顶堆:就是顶点是最小值,并且父节点小于等于左子节点和右子节点(子节点公式上边有说)

大顶堆:就是顶点是最大值,并且父节点大于等于左子节点呼右子节点(子节点公式上边有说)

OK,现在说一下堆排序的逻辑。 堆排序是有两步:

第一步:建立堆。 我们需要把无序数组调整成堆结构(也就是让无序数组满足上图的条件)

大概过程,如图:

如图如所,我们要建立的是小顶堆,所以父节点一定是大小子节点的。

那么从97这个数字开始,是才有子节点的。  如果按公式来算,就是索引第(i/2)-1个数字是第一个有子节点的数字。

比较完数字97,把小的数字与97对换,然后再比较数字65与65子节点大小,然后再比较数字38与38子节点大小,最终比较最顶节点与子节点。

OK,有一个槽点要注意,请大家看第四图和第五图,当比较顶节点和子节点时,13与49换了位置,这时发现换位置后的49与49子节点是不满足堆的关联,所以49与27也要做一下互换的。   大家有个印象就行,具体逻辑还是请看下边的代码(17行至20行)

第二步:进行堆排序。

因为堆其实就是一个数组,所以如果我们的堆是完全从小到大,或者从大到小存储数据,那么数组同样也就是升序,或者降序了。

以下代码第24行至31行是进行堆排序。

我们可以看到我们之前代码建立好的堆是小顶堆,也就是最小值是在顶点。

第26行到28行,做的就是把第一个节点与最后一个节点数字互换,这时,最小值就在最后边了。

然后执行第30行的代码,因为上边把节点数字做了互换,这时堆就变成了无序数组,那么需要重新建立堆啦,需要把第一个节点至(最后一个节点-1)进行重建堆。

重建堆后,最小的数字又会放到第一他节点啦

接着执行第26行到28行,把第一个节点与(最后一个节点-1)数字互换,这时,最小值就在(最后一个节点-1)了。

然后执行第30行代码,继续重新建立堆。。。。如此类推,数字将会进行降序。

代码

使用JAVA

/*
 * 堆排序
 */
public class HeadSort {
	public static void main(String[] args) {
		int[] arrayData = { 5, 9, 6, 7, 4, 1, 2, 3, 8 };
		HeadSortMethod(arrayData);
		for (int integer : arrayData) {
			System.out.print(integer);
			System.out.print(" ");
		}
	}

	public static void HeadSortMethod(int[] arrayData) {
		// 第一步,将无序数组,建成一个堆。
		//System.out.println("开始建堆");
		for (int i = arrayData.length / 2 - 1; i >= 0; i--) {
			// 第一个有孩子节点的节点,索引是arrayData.length/2-1 。 这个就不证明了(暂时还不会),大家测试一下就知道结果了
			HeadAjust(arrayData, i, arrayData.length);
		}
		//System.out.println("开始排序");
		// 第二步,进行排序
		// 对堆进行递归,来建立排好序的堆。
		int temp;
		for (int i = arrayData.length-1; i > 0; i--) {
			temp = arrayData[0];
			arrayData[0] = arrayData[i];
			arrayData[i] = temp;

			HeadAjust(arrayData, 0, i - 1);
		}
	}

	/*
	 * 对于指定数组的序列,进行堆化调整 也就是将无序数组调整成堆。
	 */
	public static void HeadAjust(int[] arrayData, int beginIndex, int endIndex) {
		int minNum = arrayData[beginIndex];

		// i=beginIndex*2+1代表是左子节点
		// i=i*2+1代表下一个左子节点
		for (int i = beginIndex * 2 + 1; i <= endIndex; i = i * 2 + 1) {
			//System.out.println(i);
			// 先比较左子节点和右子节点, i存储小值的索引
			if (i < endIndex && arrayData[i] > arrayData[i + 1]) {
				i++;
			}

			if (arrayData[i] > minNum) {
				break;
			}

			arrayData[beginIndex] = arrayData[i];
			beginIndex = i;
		}

		arrayData[beginIndex] = minNum;
	}
}

执行结果:

9 8 7 6 5 4 3 2 1

  

时间复杂度:O(nlog2n)   参考这里http://blog.csdn.net/liliuteng/article/details/8496050, 说实在的,这个证明过程我还是看不太懂……数学基础不太好,过后需要再学习一下数学了。

空间复杂度:O(1)

学习资料

http://blog.csdn.net/clam_clam/article/details/6799763   本次总结大部分图是从这里来的…实在是懒着画了,抱歉抱歉

http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html

http://blog.csdn.net/morewindows/article/details/6709644/

时间: 2024-08-09 23:22:10

Hark的数据结构与算法练习之堆排序的相关文章

【数据结构和算法16】堆排序

堆排序,顾名思义就是利用堆这个数据结构对数据项进行排序.前面提到过.堆数据结构中.节点大于或等于自己的子节点.那么我们能够将待排序的数据项依次加入到堆中,然后再依次取出根节点就可以.从堆中取出的数据项是从大到小排列的.由于根节点永远是最大的.而堆中永远是取根节点.假设对堆这样的数据结构不太了解的话,能够先看这篇博文:数据结构和算法之 堆.这里不再赘述. 以下我们来看看堆排序的实现(假设程序有不清楚的地方.也能够參考上面那篇博文). public class HeapSort { private

Hark的数据结构与算法练习之鸡尾酒排序

算法说明 鸡尾酒排序又叫定向冒泡排序,鸡尾酒搅拌排序,搅拌排序,涟漪排序,回来排序,快乐小时排序. 鸡尾酒排序是交换排序的一种,它是冒泡排序的一个轻微的变种.冒泡是从低向高比较排序,鸡尾酒从低向高,从高向低交换着进行排序.大家看一下代码就知道了. 某些特殊有序数组情况下,鸡尾酒排序是效率略好于冒泡排序,例如: int[] arrayData = { 2, 3, 4, 5, 6, 7, 8, 9, 1 }; 鸡尾酒排序只排序一次就能出结果,而冒泡排序就需要8次才能出结果. 代码 使用的是java

Hark的数据结构与算法练习之地精(侏儒)排序

算法说明 地精排序是交换排序的一种,它是冒泡排序的一种改良,我感觉和鸡尾酒排序挺像的. 不同之处是鸡尾酒排序是从小到大,然后再从大到小切换着排序的.而地精排序是上来先从小到大排序,碰到交换到再从大到小,接着再从小到大进行排序. 举个例子: 对8,6,4,5,1进行升序排序 1.8与6交换,结果是 {6,8,4,5,1} 2.8与4交换,结果是 {6,4,8,5,1} 3.4与6交换,结果是 {4,6,8,5,1} 4.5与8交换,结果是 {4,6,5,8,1} 5.6与5交换,结果是 {4,5,

Hark的数据结构与算法练习之奇偶排序

算法说明 奇偶排序又叫奇偶换位排序,砖排序.它是一种交换排序,也是冒泡的一个变种 顾名思义,奇偶排序,其实就是先循环奇数位,然后将奇数位与偶数位比较计算. 然后再循环偶数位,再和奇数位比较运算.看一下代码大家就明白了. 据wiki所述,这种算法是一种并行算法,个人对这块现在不太理解,没明白这块所谓的并行是什么意思,现在只是完成了一个单机版,将来如果明白了再过来进行补充啦. 代码 使用的是java package hark.sort.exchangesort; /* * 奇偶排序 */ publi

Hark的数据结构与算法练习之Bogo排序

算法说明 Bogo排序是交换排序的一种,它是一种随机排序,也是一种没有使用意义的排序,同样也是一种我觉得很好玩的排序. 举个形象的例子,你手头有一副乱序的扑克牌,然后往天上不停的扔,那么有一定机率会变成有序的. 哈哈,就是这样. 看一下代码大家就知道了. 代码 使用的是java package hark.sort.exchangesort; import java.util.Random; /* * Bogo排序 */ public class BogoSort { public static

Hark的数据结构与算法练习之煎饼排序

算法说明 假设煎锅里边有N个煎饼摞在了一起,它们大小不一并且顺序不一致,我们需要通过拿铲子将它们不停的翻个,进行排序,最终得到一个底下是大的煎饼,上边是小的煎饼的序列.这个排序的过程就是煎饼排序. 这个算法有两种解,一种是普通解,一种是最优解. 普通论证: 例如你的初始煎饼顺序是[2,4,3,1] 然后2与4交换位置,然后4与1交换位置,得出[1,3,2,4]. 然后3与1交换位置,接着3与2交换位置,得出[2,1,3,4]. 最后2与1交换位置,得出结果[1,2,3,4] 通过普通解的过程,我

Hark的数据结构与算法练习之图书馆排序

算法说明 图书馆排序是插入排序的变种,典型的以空间换时间的一种方法.我个人感觉这种思路可以学习借鉴,但直接使用的场景应该不大. 我们知道,真正的插入排序通常往前边插入元素后,我们要把后边所有的元素后移.而图书馆排序的思路就是将每个元素后边都预留N个空间(例如预留10个元素空间),这样往某个元素前插入时,在预留空间足够的前题下,只会移动少少几个的元素. 代码 因为4月要考试,所以代码暂不写,以后有时间时补上 参考 http://www.cnblogs.com/kkun/archive/2011/1

Hark的数据结构与算法练习之希尔排序

算法说明 希尔排序是插入排序的优化版. 插入排序的最坏时间复杂度是O(n2),但如果要排序的数组是一个几乎有序的数列,那么会降低有效的减低时间复杂度. 希尔排序的目的就是通过一个increment(增量)来对数列分组进行交换排序,最终使数列几乎有序,最后再执行插入排序,统计出结果. 通过increment=n/2, 也就是如果9个数的话,增量为4,2,1.   如果是20个数的话,增量就是10,5,2,1.  当increment为1时,其实对几乎有序的数列进行插入排序啦啦. 时间复杂度 O(n

Hark的数据结构与算法练习之桶排序

算法说明 桶排序的逻辑其实特别好理解,它是一种纯粹的分而治之的排序方法. 举个例子简单说一下大家就知道精髓了. 假如对11,4,2,13,22,24,20 进行排序. 那么,我们将4和2放在一起,将11,13放在一起,将22,24,20放在一起.  然后将这三部分分别排序(可以根据实现情况任意选择排序方式,我的代码中使用的是快排),将子数组排序后,再顺序输出就是最终排序结果了(大概应该明白了,我们是根据数字大小进行分组的,故而顺序输出即可) 怎么样,很简单吧. 具体实现大家看代码就行,我实现的其