1. 程式人生 > >《演算法導論》——最壞時間為線性時間的選擇演算法

《演算法導論》——最壞時間為線性時間的選擇演算法

《演算法導論》——最壞時間為線性時間的選擇演算法

該演算法與期望時間為線性時間的選擇演算法的最大區別就是該演算法的partition中不是隨機的以某一個數作為基準,而是將中位數的中位數傳入作為基準,返回中位數的中位數在序列中的位置。
步驟如下

1: 將輸入陣列的n個元素劃分為 n/5 組,每組5個元素,且至多隻有一組由剩下的 n%5 個元素組成。

2: 尋找 n/5 組中每一組的中位數:首先對每組元素(至多為5個)進行插入排序,然後確定每組有序元素的中位數。

3: 對第2步中找出的 n/5 箇中位數,遞迴呼叫 select 以找出其中位數 num(如果有偶數箇中位數,為了方便,約定 num 是較小的中位數)

4: 利用修改過的partition版本,按中位數的中位數 num 對輸入陣列進行劃分。讓 count 比劃分的低區中的元素數目多1,因此 num 是第 count 小的元素,並且有 n - count 個元素在劃分的高區。

5: 如果 k == count,則返回 num。如果 k < count,則在低區遞迴呼叫 select 以找出第 k 小的元素。如果 k > count,則在高區遞迴查詢第 k - count 小的元素。
在這裡插入圖片描述

#include "iostream"
#include "algorithm"
using namespace std;

void insert_sort(int* a, int l, int h)//插入排序
{
	
	for (int i = l + 1; i <= h; i++)
	{
		int key = a[i];
		int j = i - 1;
		while (j >= l && a[j] > key)
		{
			a[j + 1] = a[j];
			j--;
		}
		a[j + 1] = key;
	}
}

int partition(int* a, int l, int h, int q)//q為中位數的中位數
{
	int j = 0;
	for (int i = l; i <= h; i++)//找到中位數的中位數在陣列中的位置
	{
		if (a[i] == q)
		{
			j = i;
			break;
		}	
	}
	swap(a[j], a[h]);//放大索引最大處

	int n = l - 1;
	for (int m = l; m < h; m++)
	{
		if (a[m] < a[h])
		{
			n++;
			swap(a[m], a[n]);
		}
	}
	swap(a[n + 1], a[h]);
	return n + 1; //返回中位數的中位數的基準(左邊小於它,右邊大於它)
}
int select(int* a, int l, int h, int k)//l,h為陣列的邊界,k為輸入
{
	if (h - l+1 <= 5 )
	{
		insert_sort(a, l, h);
		return a[l + k - 1];
	}
	int group = (h - l + 5) / 5; //每5個數為一組,group為組數
	for (int i = 0; i < group; i++)
	{
		int left = l + 5 * i;
		int right = l + 5 * i + 4 > h ? h : l + 5 * i + 4;//如果邊界大於了陣列上邊界,則用陣列上邊界作為邊界
		insert_sort(a, left, right);
		int mid = (left+right) / 2;
		swap(a[mid], a[l + i]);//把中位數放在前面,方便後面的計算中位數的中位數		

	}
	int q = select(a, l, l + group - 1, (1 + group)/2);//遞迴找到中位數的中位數
	int m = partition(a, l, h, q);//返回中位數的中位數在當前劃分數組裡的位置
	int s = m - l + 1;//中位數的中位數是第幾大數
	if (s == k)
	{
		//return a[l+k-1];//一定記得要加上L(在執行select(a, m + 1, h, k - s);時,傳的k值是減過的)
		return a[m];
	}
	if (s > k)
	{
		select(a, l, m - 1, k);
	}
	else
	{
		select(a, m + 1, h, k - s);
	}
}
  int main(void)
{
	
	int a[10] = { 25,31,89,12,67,53,44,59,71,19 };
	for(int i =1;i<11;i++)
		cout<<"第"<<i<<"大數為:"<< select(a, 0, 9, i)<<endl;
		system("pause");
 }