1. 程式人生 > 實用技巧 >純手擼——堆排序

純手擼——堆排序

閱讀篇:

堆排序學習筆記——外婆的澎湖灣

前提:

堆用完全二叉樹表示時,其表示方法不唯一,但可以確定的是樹的根結點要麼是無序表中的最小值,要麼是最大值。

思想:

  • 把堆頂的元素與最後一個元素交換(這樣破壞了堆)
  • 剩餘元素再構成堆,再將堆頂元素與最後第二個元素交換
  • 以此類推,便會得到有序陣列

圖解步驟:

二叉堆在實現的時候,是採取陣列的形式來儲存的。

從二叉堆中刪除一個元素,為了充分利用空間,其實我們是可以把刪除的元素直接存放在二叉堆的最後一個元素那裡的。例如:

刪除堆頂,把刪除的元素放在最後一個元素。

繼續,把刪除的元素放在最後第二個位置

繼續刪除

以此類推….

這樣,對於一個含有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;
}