首页 > 技术文章 > 纯手撸——堆排序

wangzheming35 2020-08-14 12:18 原文

阅读篇:

堆排序学习笔记——外婆的澎湖湾

前提:

堆用完全二叉树表示时,其表示方法不唯一,但可以确定的是树的根结点要么是无序表中的最小值,要么是最大值。

思想:

  • 把堆顶的元素与最后一个元素交换(这样破坏了堆)
  • 剩余元素再构成堆,再将堆顶元素与最后第二个元素交换
  • 以此类推,便会得到有序数组

图解步骤:

二叉堆在实现的时候,是采取数组的形式来存储的。

从二叉堆中删除一个元素,为了充分利用空间,其实我们是可以把删除的元素直接存放在二叉堆的最后一个元素那里的。例如:

删除堆顶,把删除的元素放在最后一个元素。

继续,把删除的元素放在最后第二个位置

继续删除

以此类推….

这样,对于一个含有n个元素的二叉堆,经过n-1(不用删除n次)次删除之后,这个数组就是一个有序数组了。

所以,给定一个无序的数组,我们需要把这个数组构建成二叉堆,然后在通过堆顶逐个删除的方式来实现堆排序。

其实,也不算是删除了,相当于是把堆顶的元素与堆尾部在交换位置,然后在通过下沉的方式,把二叉树恢复成二叉堆。

代码实现操作有难度,可以先模仿再屡思路,在代码中学习,此处重点学习在下沉操作:

//不妨就想象成一行数组吧!将parent->low,lenght->high
//并且这里的数组第一个元素下标姑且认为1,否则完全二叉树不好表示
int[] downAdjust(int a[],int low,int high)
{
	//临时保存下沉元素
	int tmp = a[low];
	//定位左孩子结点
	int child = 2*low;
	//开始下沉
	while(child<=high)
	{
		//如果右孩子结点>左孩子结点,则定位到右孩子
		if(child<high&&a[child]<a[child+1])
		{
			child++;
		}
		//如果根节点<最大孩子节点
		if(tmp<=a[child])
		{
			//赋值
			a[low]=a[child];
			low=child;
			child=2*low;
		}
		else
		{
			//根节点≥最大孩子结点
			break;
		}
		
	} 
	a[low]=tmp;
	return a;
}
int[] HeapSort(int a[],int n)
{
	//构建二叉堆
	int i;
	for(i=n/2;i>=1;i--)
	{
		a=downAdjust(a,i,n);
	}
	//排序
	for(i=n;i>=2;i--)
	{
		//把堆顶的元素与最后一个元素交换
		//swap
		int tmp = a[i];
		a[i] = a[1];
		a[1] = tmp;
		//下沉
		a = downAdjust(a,1,i-1);
	}
	return a;
} 

附个小视屏:

推荐阅读