堆排序(Java实现)

原创 2015年07月07日 15:42:11

今天看了一下堆排序的内容,写篇博客记录一下。

关于堆的定义和性质就不说了,有一点需要说明一下,在给堆中的元素进行编号的时候,为了便于描述堆的性质,采取的编号方式是从上到下、从左至右依次编号,且堆顶元素编号为1。当使用数组来存储堆的时候,因为数组元素的编号是从0开始的,这就要求我们注意数组元素编号和堆元素编号的对应问题。下面以最大堆为例来说明堆排序的过程。


1、维护堆的性质

    该方法接受一个将要被构造成堆的数组和一个下标,数组用于保存堆节点。对于堆中的任意一个节点,假设其左子树和右子树都是最大堆,如果此时该节点的值大于其左右孩子的值,那么以该节点为顶的堆就是最大堆;如果小于其中的一个或两个孩子,就需要把该节点和较大的孩子交换,交换之后以该节点为根的子树可能又不满足最大堆的性质了,此时就要对该子树递归的调用维护堆性质的方法了。


2、建堆

    该方法接受一个数组。对于堆中的每一个非叶子节点,都调用1中的维护堆性质的方法就能构造一个堆了。对于叶子节点,由于没有左子树和右子树,可以把叶子节点看做已经满足最大堆性质的特殊的子树(特殊在左右子树都为空),所以不必对叶子节点执行维护堆性质的方法。当对非叶子节点执行维护堆性质的方法的时候,要按照序号从大到小的顺序进行,这是由维护堆性质的方法决定的,当我们先对下面的节点执行维护堆性质的算法时,就能保证对上面的节点执行同样的算法的时候,该节点的左右子树都满足了最大堆性质。


3、堆排序原理

    根据最大堆的性质,堆顶元素(数组的首元素a[0])必定是最大的,因此每次将堆顶元素和堆最后的元素交换,就能实现把数组中的最大值移动到数组的最后(递增排序)。交换之后,可能又不满足最大堆的性质,此时需要将数组再次进行建堆,由于此时已经有一个元素在他最终的位置上了,因此没有必要最整个数组进行建堆,只需要对数组中的未排部分进行建堆就行了,当然你对整个数组进行建堆的话,你会发现程序在原地踏步。


下面是完整的代码:

package com.hubu.heapsort;

public class HeapUtils {
	private static int len; //用于保存堆中数组元素的个数
	
	//获得编号为i的节点的左右孩子的编号
	public int left(int i) {
		return 2 * i;
	}

	public int right(int i) {
		return 2 * i + 1;
	}

	// 维护堆的性质的方法
	public void max_heap(int a[], int i) {
		int l = left(i);
		int r = right(i);
		int largest;

		if (l <= len && a[l - 1] > a[i - 1])
			largest = l;
		else
			largest = i;
		if (r <= len && a[r - 1] > a[largest - 1])
			largest = r;
		if (largest != i) {
			//将值最大的节点换到根节点的位置
			int temp = a[i - 1];
			a[i - 1] = a[largest - 1];
			a[largest - 1] = temp;

			max_heap(a, largest);// 递归调用
		}
	}

	// 构造初始堆的方法,对除了叶子节点之外的所有节点调用维护堆的方法
	public void build_heap(int a[]) {
		for (int i = a.length / 2; i >= 1; i--)
			max_heap(a, i);
	}

	// 堆排序算法
	public void heap_sort(int a[]) {
		build_heap(a);
		
		for (int i = a.length; i >= 2; i--) {
			int temp = a[0];
			a[0] = a[i-1];
			a[i-1] = temp;

			//每次把堆顶元素拿出来之后,下次进行建堆时,就不能把最后一个元素(最大)包含进来
			len--;
			max_heap(a, 1);
		}
	}

	public static void main(String[] args) {
		int a[] = { 6, 5, 4, 3, 2, 1, 0};
		len = a.length;
		HeapUtils hu = new HeapUtils();
		hu.heap_sort(a);
		for (int i = 0; i < a.length; i++)
			System.out.print(a[i] + " ");
	}
}


时间复杂度和稳定性分析

    先来看看整个算法经历了哪几个过程。先是进行初始化的建堆操作,然后取出最大值、执行维护堆性质的算法两者交替进行。建堆操作执行Θ(n)次维护最大堆性质的方法,维护最大堆性质的方法,在堆顶节点的值最小的时候(最坏情况)需要交换lgn次(最小元素每次降一层),因此建堆操作的时间复杂度为Θ(nlgn)。取出最大值、执行维护最大堆性质算法也会执行Θ(n)次,因此整个过程的复杂度同样是Θ(nlgn),所以堆排序算法的复杂度就是Θ(nlgn)。

    从建堆的原理中我们不难发现,在建堆的过程中就有可能已经改变他们在数组中的相对顺序,看下面的这个堆


按照上面给出的算法,我们不难发现,排序完成之后两个值为3的节点的相对顺序发生了改变,这也说明堆排序不是稳定的。


相关文章推荐

堆排序之Java实现

  • 2017年07月27日 17:03
  • 4KB
  • 下载

堆排序 java实现

  • 2011年10月24日 15:11
  • 1KB
  • 下载

HeapSort堆排序Java实现图文代码详解

堆排序(Heapsort)堆积树设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。 排序图如下:(gif来自维基百科) 堆排序的过程就是首先构建大根堆,然后对顶元素(及最大元素)与最...

堆排序,和其他排序 java 实现

  • 2012年05月21日 13:41
  • 52KB
  • 下载

Java堆排序(HeapSort)算法实现

算法实现public class MaxHeap { public static int left(int i){ return 2*i; } public s...

java实现最小堆(通过构造函数构造最小堆,相当于堆排序)

最小堆 最小堆数据结构也是一棵完全二叉树(叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树)。 因为完全二叉树的性质,因此我们用数组来存储树的节...

Java实现堆排序

堆排序 堆排序是另一种选择排序方法,它是树型选择排序的改进,它使用的辅助空间较少,仅需要一个元素用于空间交换。 堆:根结点的关键码值或者大于左右子树或者都小于左右子树,而且左右子树也是堆。...

读《程序员编程艺术》之自造Java版本----最大堆排序实现最小K问题

在《程序员编程艺术》中有求最小k的最大堆排序算法,通过JAVA进行了实现,并输出结果。 在实现的过程中,尤其是最大堆中,自顶向下的更新是最容易出错的,本程序中巧妙的使用i和j的关系(父节点和子节点),...

堆排序(Java实现)

什么是堆排序,具体百度。 推荐一个讲得比较清楚的视频教程:什么是堆排序? 下面贴代码:// 名称:堆排序——对数组里的十个整数,按从小到大的顺序排序输出 // 学校:河北大学 // 作者:来智慧 ...

算法外功修炼之二 堆排序的java实现

从下往上,每个非叶子节点都比较一次,那么到最后所有节点都满足了堆的性质,这就是建堆。 堆排序,一种建立在虚拟堆,真实数组上的排序方式 建立堆和调整堆有什么区别呢,或者说为什么要建立堆? 我认为,建立堆...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:堆排序(Java实现)
举报原因:
原因补充:

(最多只允许输入30个字)