排序算法串烧——堆排序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a247027417/article/details/82682608

堆排序准备知识讲解

什么是堆:

堆是一种具有特定特性的完全二叉树

满足两种特定的性质:

1. 堆的每一个父亲节点都大于(或小于)其子节点(分别称为:大顶堆、小顶堆)。

堆排序的建立过程:

我们使用一组数据来进行堆排序,以直观的理解堆排序的过程。我们实现的是大顶堆。

使用数据: 33, 25, 47,58,22, 17, 99 将其升序排列。

第一步:建堆(升序建小堆,降序建大堆)。首先,将数据直接按顺序填充到完全二叉树中,此时尚未形成堆。然后,从最后一个非叶子节点开始进行调整(根据堆的定义,是考虑父节点大于或者小于子节点,因此,我们总是考虑非叶子节点即父节点),因此我们首先考虑3号节点,其数值为47,而后观察以其为父节点的左右子节点,将三者中最大值放到父节点位置。

继续遍历剩下的非叶子节点,从后往前, 因此接下来将选择2号节点,与其左右节点相比较,如果子节点大于父亲节点,则交换位置。再接下来就是1号节点,示意图如下。

第二步:堆建立好之后,进行排序。排序过程:使用第一个节点和第N个节点交换数据,并且将最后一个节点脱离出堆,最为有序数据,剩下的N-1个节点通过调整再次形成堆。继续使用第一个节点和无序的最后一个节点,即N-1个节点,交换,再进行调整。一直循环执行到所有节点均有序,排序完成。示意图如下:

大顶堆节点交换

交换了之后,对无序的1-6号节点进行堆调节,使之再成为符合要求的大顶堆,之后再次交换1号节点和最后一个无序节点

继续进行调整和交换的过程,直到所有的节点有序

相信到这一步,大家都能明白堆排序的原理,其实很好理解,每次大顶堆排序可以使得,最大数据出现在root节点,再将最大数据放至最后的节点,重复过程可以使得数据按照节点的序号实现升值排序。

下面讲解一下代码过程:

由上可知,整个排序就三个过程:1.建立堆;2.交换无序首尾数据;3.调整堆结构。

使用Array[N] 来表示一个完全二叉树,和上述介绍的编号有一些差异,编程的编号是从0开始的,所以我们容易知道,当i为父亲节点时,其左子节点为2*i+1,其右子节点为2*i+2;

建堆过程,从最后一个非叶子节点,开始往前遍历,将父节点、左子节点、右子节点中的最大值放在父节点上。

/*
	 * 建堆,通过比较父节点,左右节点,将最大或者最小值放在堆顶
	 * 从最后一个非叶子节点开始一直打到root
	 */
	public int[] buildHeap(int[] data)
	{
		for (int i = (data.length-2)/2;i>=0; i--)
			AdjustHeap(data, i, data.length);
		
		return data;
	}
	
	public void AdjustHeap(int[] data, int i, int length)
	{
		//传入的i为父节点值,因此需要判断子节点是否超出了index
		//左子节点为 2i+1  右子节点为 2i+2
		if((2*i+2)>length-1)   //只有左节点
		{
		   if(data[i] < data[2*i+1])
			   ExchangeData(data, i, 2*i+1);
		}
		else {
			if(data[2*i+1]>data[i]&&data[2*i+1]>data[2*i+2])
				ExchangeData(data, i, 2*i+1);
			else if(data[2*i+2]>data[i]&&data[2*i+2]>data[2*i+1])
				ExchangeData(data, i, 2*i+2);
		}
	}

使用带参数的AdjustHeap是因为在排序的时候,只需要对无序的节点进行调整堆结构,已经有序的节点不需要再变动。因此排序过程,就是重复的交换0号节点和最后一个无序节点的位置再进行调整。当执行到只剩下两个节点时,只需要进行交换即可,不需要再进行调整。代码如下:

/*
 * 堆排序
 */
	
	public int[] Sort(int[] data){
		//disp("建堆前:", data);
		//第一步,建堆
	    data = buildHeap(data);
	   // disp("建堆后:", data);
	    //将第一个与最后一个交换,然后调整结构
		for(int i = data.length-1; i>0; i--)
		{
			ExchangeData(data, 0, i);  // 将data[0] 放置在data[N]
			//disp("交换后:", data);
			// 调整堆结构
			for (int j = (i-2)/2 ; j>=0; j--)
				AdjustHeap(data, j, i);
			//disp("调整后:", data);
		}
		ExchangeData(data, 0, 1);
		return data;
	}

 程序输入如下:

建堆前:33 25 47 58 22 17 99  
建堆后:99 58 33 25 22 17 47  
交换后:47 58 33 25 22 17 99  
调整后:58 47 33 25 22 17 99  
交换后:17 47 33 25 22 58 99  
调整后:47 17 33 25 22 58 99  
交换后:22 17 33 25 47 58 99  
调整后:33 25 22 17 47 58 99  
交换后:17 25 22 33 47 58 99  
调整后:25 17 22 33 47 58 99  
交换后:22 17 25 33 47 58 99  
调整后:22 17 25 33 47 58 99  
交换后:17 22 25 33 47 58 99  
结果:17 22 25 33 47 58 99  

代码已经上传,同时附上了堆排序的高清理解图,

点击下载

猜你喜欢

转载自blog.csdn.net/a247027417/article/details/82682608