1. 程式人生 > >堆排序實現

堆排序實現

操作 ora right 堆排 word -i 堆排序 margin space

1、堆排序算法描寫敘述:

(1)定義

n個keyword序列Kl,K2,…,Kn稱為(Heap)。當且僅當該序列滿足例如以下性質(簡稱為堆性質): 1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2)。當然。這是小根堆。大根堆則換成>=號。//k(i)相當於二叉樹的非葉子結點,K(2i)則是左子節點。k(2i+1)是右子節點 2)若將此序列所存儲的向量R[1..n]看做是一棵全然二叉樹存儲結構,則堆實質上是滿足例如以下性質的全然二叉樹: 樹中任一非葉子結點的keyword均不大於(或不小於)其左右孩子(若存在)結點的keyword。

(2)用大根堆排序的基本思想

① 先將初始文件R[1..n]建成一個大根堆。此堆為初始的無序區 ② 再將keyword最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n]。且滿足R[1..n-1].keys≤R[n].key ③因為交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整為堆。

然後再次將R[1..n-1]中keyword最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換。由此得到新的無序區R[1..n-2]和有序區R[n-1..n]。且仍滿足關系R[1..n-2].keys≤R[n-1..n].keys,相同要將R[1..n-2]調整為堆。

…… 直到無序區僅僅有一個元素為止。 (3)大根堆排序算法的基本操作: ①建堆。建堆是不斷調整堆的過程,從len/2處開始調整,一直到第一個節點,此處len是堆中元素的個數。建堆的過程是線性的過程。從len/2到0處一直調用調整堆的過程。相當於o(h1)+o(h2)…+o(hlen/2) 當中h表示節點的深度。len/2表示節點的個數,這是一個求和的過程,結果是線性的O(n)。 ②調整堆:調整堆在構建堆的過程中會用到。並且在堆排序過程中也會用到。利用的思想是比較節點i和它的孩子節點left(i),right(i),選出三者最大(或者最小)者。假設最大(小)值不是節點i而是它的一個孩子節點。那邊交互節點i和該節點,然後再調用調整堆過程,這是一個遞歸的過程。調整堆的過程時間復雜度與堆的深度有關系,是lgn的操作,由於是沿著深度方向進行調整的。 ③堆排序:堆排序是利用上面的兩個過程來進行的。首先是依據元素構建堆。

然後將堆的根節點取出(通常是與最後一個節點進行交換)。將前面len-1個節點繼續進行堆調整的過程,然後再將根節點取出,這樣一直到全部節點都取出。堆排序過程的時間復雜度是O(nlgn)。由於建堆的時間復雜度是O(n)(調用一次)。調整堆的時間復雜度是lgn,調用了n-1次。所以堆排序的時間復雜度是O(nlgn)。 2、堆的存儲 一般用數組來表示堆,若根結點存在序號0處, i結點的父結點下標就為(i-1)/2。

i結點的左右子結點下標分別為2*i+12*i+2
3、堆排序算法實現(最大堆)

#include <iostream>

void printArray(int theArray[], int n)
{
	for(int i = 0; i < n; i++)
	{
		std::cout << theArray[i] << " ";
	}
	std::cout << std::endl;
}

void adjustHeap(int theArray[], int n, int start)  //從start索引處結點開始調整 
{
	if(start < 0 || start >= n)
		return;
	int fatherIndex;
	int leftIndex;
	int rightIndex;
	int tmp;
	
	fatherIndex = start;
	leftIndex = 2 * fatherIndex + 1;        //start索引處結點的左孩子的索引 
	rightIndex = leftIndex + 1;             //start索引處結點的右孩子的索引 
	
	if(rightIndex < n)          //start索引處結點存在右孩子結點. 
	{
		if(theArray[fatherIndex] < theArray[rightIndex])     //右孩子結點大於start索引處結點 。交換 
		{
			tmp = theArray[fatherIndex];
			theArray[fatherIndex] = theArray[rightIndex];
			theArray[rightIndex] = tmp; 
		}
	}
	if(leftIndex < n)       //start索引處結點存在左孩子結點,但不一定存在右孩子結點
	{
		if(theArray[fatherIndex] < theArray[leftIndex])     //右孩子結點大於左孩子結點樹,交換 
		{
			tmp = theArray[fatherIndex];
			theArray[fatherIndex] = theArray[leftIndex];
			theArray[leftIndex] = tmp;
		}
	} 
	adjustHeap(theArray, n, start-1);         //再次遞歸調整start索引處的上一個結點 
}

void constructHeap(int theArray[], int n)
{
	int start = n / 2; 
	adjustHeap(theArray, n, start);                 //從length/2處開始調整 
}

void heapSort(int theArray[], int n)
{
	if(n == 1)                                    //當未排序序列中僅僅剩一個元素時,直接跳出遞歸 
		return;
	int length = n;
	
	//printArray(theArray, length);              //打印出每次建立最大堆之後,數組排列情況 
	
	//將最大堆的堆頂元素與堆末尾元素交換,並取出該元素作為已排序數組元素 
	int tmp = theArray[0];                    
	theArray[0] = theArray[length-1];
	theArray[length-1] = tmp;
	 
	//再次調整交換後的堆,使得為滿足最大堆 
	--length;                 //未排序序列長度減一 
	int start = length / 2;   
	adjustHeap(theArray, length, start);
	heapSort(theArray, length);             //遞歸進行堆排序 
}

int main(int argc, char *argv[]) {
	int myArray[] = {5,90,28,4,88,58,38,18,19,20};
	int length = sizeof(myArray) / sizeof(myArray[0]);
	constructHeap(myArray, length);
	heapSort(myArray, length);
	printArray(myArray, length);
	return 0;
}

4、堆的時間復雜度和空間復雜度 堆的最好、最壞以及平均時間復雜度是O(nlogn). 堆的空間復雜度是O(1). 感想:剛開始實現建立堆時,以為須要建立一個二叉樹輔助幫助,事實上這個所謂堆(即全然二叉樹)僅僅是邏輯結構。真實的物理結構還是那個順序數組。在建立大堆或小堆的過程中,均是以順序數組為基礎,而邏輯上是操作一顆全然二叉樹,故全然不須要建立一個輔助全然二叉樹。

堆排序實現