1. 程式人生 > >分治演算法求n個元素序列中第k個大的元素

分治演算法求n個元素序列中第k個大的元素

     首先,我們應該設定產生隨機數的序列儲存在陣列中,然後我們應該最容易想到的是排序對吧,做一個降序排序,就很容易找到第k個大的元素。但是用排序演算法的話,時間複雜度最快的也是快速排序O(logn),如果我們使用分治演算法求得話,會得到一個線性的時間複雜度O(n)。分治演算法是將一個問題分成若干個子問題去實現。分治演算法是離不開遞迴演算法的。

怎麼實現?

我們可以在陣列中產生一個隨機的下標,讓他作為一個標誌,然後我們可以先將比改下標對應的元素大的放在該下標所對應的元素放在左邊區間,小的放在右邊區間。

然後我們可以模仿二分查詢來尋找第k個大的元素,首先我們在將大的元素放在左邊區間的迴圈中加入一個計數器,我們可以統計在標誌下標左邊區間的元素數量是多少,既然我們要找的是第k個大的元素,我們可以將k與計數器比較,如果小於左邊區間的元素個數,那麼第k個大的元素必然在左區間,我們就可以使用遞迴繼續找,反之,如果大於計數器說明在右邊區間,我們就遞迴右邊區間即可。

演算法原始碼實現:

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<algorithm>
using namespace std;
int num[1000];

int random(int a,int b) 
{

	 return (rand()%(b-a+1)+a);
}

int  find(int a[],int left,int right,int k)
{
	int index = random(left,right - left + 1);    //隨機選取一個下標的元素作為定值來進行比較 
	swap(a[left],a[index]);
	int middle = left;
	int count = 1;                     //記錄比選定元素大的元素的個數 
	int i,j;
	for(i = left + 1;i<=right;i++)     //計算所選定的元素左邊的元素的個數 
	{
		if(a[i]>a[left])  			 	//將大的元素排在所選定點得左邊 
		{
			count++;
			middle++; 
			swap(a[middle],a[i]);      
		}
	} 

	swap(a[middle],a[left]);   

	if(k<count){ 					//如果需要找的第k個大的元素小於左邊的元素數量,那麼第k個大的元素一定在這個區間,在遞迴呼叫去找 
		find(a,left,middle-1,k);
	}
	else if(k>count){			//相反的,如果大於左邊元素數量,則在右邊的區間找 
		find(a,middle+1,right,k-count);
	}
	else
		return a[middle];   //如果k==count說明要找的元素是在middle 
	
}


int main()
{
	srand((unsigned)time(0));
	int n,k;
	cout<<"請輸入產生多少個元素:"<<endl;
	cin>>n;
	cout<<"尋找第k個最大元素"<<endl;
	cin>>k;
	for(int i = 0;i<n;i++)
	{
		num[i] = random(1,100);
	}
	
	for(int i = 0;i<n;i++)
	{
		cout<<num[i]<<",";
	}
	cout<<endl;
	cout<<find(num,0,n-1,k);
	
	return 0;
}