堆排序和優先級隊列priority_queue

       堆是完全二叉樹,便於用array來儲存堆的所有節點;堆存儲在下標爲0開始計數的數組中,因此在堆中給定下標爲i的結點時:

        ① 如果i=0: 結點i是根節點,沒有雙親節點;否則結點i的雙親結點爲結點(i-1)/2。
        ② 如果2*i+1>n-1: 則結點i無左孩子,否則結點i的左孩子爲結點2*i+1。
        ③ 如果2*i+2>n-1: 則結點i無右孩子,否則結點i的右孩子爲結點2*i+2。

大根堆與小根堆priority_queue

       根據元素排列方式,heap可分爲max-heap和min-heap兩種,前者每個節點的鍵值(key)都大於或等於其子節點鍵值,後者的每個節點鍵值(key)都小於或等於其子節點鍵值。因此,max-heap的最大值在根節點,並總是處於底層array或vector的頭部。min-heap的最小值在根節點,總是位於底層array或vector的起頭處。

堆排序

       小根堆實現降序排列,大根堆實現升序排列。

       小根堆實現堆排序(降序)基本思想:

       (1) 初始化堆:將數列a[0,...,n-1]構成小根堆;

       (2) 交換數據:將a[0]與a[n-1]交換,使a[n-1]成爲a[0,...,n-1]中的最小值;然後將a[0,...,n-2]重新調爲小根堆。然後,將a[0]與a[n-2]交換,使a[n-2]成爲a[0,...,n-2]中的最小值;然後將a[0,...,n-3]重新調爲小根堆。依次類對,直到整個數列a[0,...,n-1]有序(降序)。

       代碼如下:

       (1) 遞歸調整下沉

//  遞歸調整下沉
void minHeapAdjust(int *arr, int i, int size)           //  i爲父節點索引
{
	int left = 2 * i + 1;                           //  left爲左孩子節點索引
	int right = 2 * i + 2;                          //  right爲右孩子節點索引
	int min = i;                                    //  min爲父節點、左節點、右節點中最小值的索引
	if (i < size / 2)                               //  葉子節點不需調整,i>=size/2均是葉子節點
	{
		if (left < size&&arr[min] > arr[left])
			min = left;
		if (right < size&&arr[min] > arr[right])
			min = right;
		if (min != i)                   //  遞歸返回條件:父節點爲最小值則直接返回;
		{                               //  否則交換父節點與最小值節點,然後再繼續以min爲父節點索引,繼續遞歸調整;
			swap(arr[min], arr[i]);
			minHeapAdjust(arr, min, size);
		}
	}
}

//  建堆
void buildMinHeap(int *arr, int size)
{
	for (int i = size / 2 - 1; i >= 0; i--)//  從第一次非葉子節點開始調整堆(索引爲size/2-1的節點爲第一個非葉子節點)
	{
		minHeapAdjust(arr, i, size);
	}
}

//  堆排
void minHeapSort(int *arr, int size)
{
	assert(arr != nullptr&&size > 0);  //  檢查參數有效性
	buildMinHeap(arr, size);              //  建堆
	for (int i = size - 1; i > 0; i--) //  將arr[0]與arr[n-1]交換,使arr[n-1]成爲arr[0,...,n-1]中的最小值;
	{                                  //  然後將arr[0,...,n-2]重新調爲小根堆。
		swap(arr[0], arr[i]);      //  再將a[0]與a[n - 2]交換,使a[n - 2]成爲a[0, ..., n - 2]中的最小值;
		minHeapAdjust(arr, 0, i);  //  然後將a[0, ..., n - 3]重新調爲小根堆。依次類對,直到整個數列有序。
	}
}

        (2) 迭代調整下沉

//  迭代調整下沉
void adjustDown(int *arr, int i, int size)
{
	int child = 2 * i + 1;
	int min = i;
	while (min < size / 2) //  當min>=size/2時,就到達非葉子節點了,非葉子節點有序,結束循環;
	{
		if (child + 1 < size&&arr[child] > arr[child + 1])
			child++;
		if (child<size&&arr[min]>arr[child])
		{
			swap(arr[min], arr[child]);
			min = child;
			child = 2 * min + 1;
		}
		else
			break;  //  父節點本身是min(父,左,右)時,跳出循環;
	}
}

//  建立小根堆
void buildMinHeap(int *arr, int size)
{
	for (int i = size / 2 - 1; i >= 0; i--)
		adjustDown(arr, i, size);
}

//  堆排降序
void minHeapSort(int *arr, int size)
{
	assert(arr != nullptr&&size > 0);
	buildMinHeap(arr, size);                          
	for (int i = size - 1; i > 0; i--)
	{
		swap(arr[0], arr[i]);
		adjustDown(arr, 0, i);
	}
}

優先級隊列priority_queue

       priority_queue本質是一個堆。

       1. 頭文件是#include<queue>

       2. 關於priority_queue中元素的比較

  模板申明帶3個參數:priority_queue<Type, Container, Functional>,其中Type 爲數據類型Container爲保存數據的容器Functional 爲元素比較方式

  Container必須是用數組實現的容器,比如vector,deque等等,但不能用 list。STL裏面默認用的是vector。

       比較方式默認用operator<,所以如果把後面2個參數缺省的話,優先隊列就是大頂堆,隊頭元素最大。特別注意pair的比較函數:先按照pair的first元素降序,first元素相等時,再按照second元素降序。

       如果要用到小頂堆,則一般要把模板的3個參數都帶進去。STL裏面定義了一個仿函數greater<>,基本類型可以用這個仿函數聲明小頂堆。如: priority_queue<int, vector<int>, greater<int> > q;

發佈了55 篇原創文章 · 獲贊 25 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章