C++分治策略實現線性時間選擇

問題描述:ios

給定線性序集中n個元素和一個整數k,1≤k≤n,要求找出這n個元素中第k小的元素,即若是將這n個元素依其線性序排列時,排在第k個的元素即爲要找到元素。算法

細節須知:(與以前的隨筆相比)windows

(1)設置了對於程序運行次數的手動輸入設定數組

(2)取消了文件的讀入,直接生成隨機數進行排序查找ui

(3)擴大了隨機數的範圍、數組的可申請大小spa

(4)時間統計精確到了微秒級設計

(5)運行結束後一次性寫入提高了程序穩定性,寫入的數據可用於數據分析圖表code

算法原理:orm

將n個輸入元素劃分紅⌈n/5⌉個組,每組5個元素,只可能有一個組不是5個元素。用任意一種排序算法,將每組中的元素排好序,並取出每組的中位數,共⌈n/5⌉個。遞歸調用算法Select來找出⌈n/5⌉個元素的中位數。若是⌈n/5⌉是偶數,就找它的兩個中位數中較大的一個。以這個元素做爲劃分基準。以此遞歸排序找到所需的第k項。blog

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cstring>
  5 #include<fstream>
  6 #include<algorithm>
  7 #include<windows.h>
  8 #include<ctime>
  9 using namespace std;
 10 LARGE_INTEGER nFreq;//LARGE_INTEGER在64位系統中是LONGLONG,在32位系統中是高低兩個32位的LONG,在windows.h中經過預編譯宏做定義
 11 LARGE_INTEGER nBeginTime;//記錄開始時的計數器的值
 12 LARGE_INTEGER nEndTime;//記錄中止時的計數器的值
 13 
 14 //一次快排
 15 int Partition(int nums[],int p,int r,int x)
 16 {
 17     if(p>r) return -1;
 18     //找出基準x的位置並與第一位交換
 19     for(int i=p;i<=r;i++)
 20     {
 21         if(nums[i]==x)
 22         {
 23             swap(x,nums[p]);
 24             break;
 25         }
 26     }
 27     int left=p,right=r;
 28     while(left<right)
 29     {
 30         while(left<right && nums[right]>=x) right--;
 31         nums[left]=nums[right];
 32         while(left<right && nums[left]<x) left++;
 33         nums[right]=nums[left];
 34     }
 35     nums[left]=x;
 36     return left;
 37 }
 38 
 39 //快速排序
 40 void QuickSort(int nums[],int low,int high)
 41 {
 42     if(low>high) return;
 43     int key=nums[low];
 44     int left=low,right=high;
 45     while(left<right)
 46     {
 47         while(left<right && nums[right]>=key) right--;
 48         nums[left]=nums[right];
 49         while(left<right && nums[left]<key) left++;
 50         nums[right]=nums[left];
 51     }
 52     nums[left]=key;
 53     QuickSort(nums,low,left-1);
 54     QuickSort(nums,left+1,high);
 55 }
 56 
 57 int Select(int nums[],int p,int r,int k)
 58 {
 59     if(r-p<75)
 60     {
 61         QuickSort(nums,p,r);
 62         return nums[p+k-1];
 63     }
 64     //每5個爲一組,找到各組的中位數,並存儲在前(r-p-4)/5個位置裏
 65     for(int i=0;i<=(r-p-4)/5;i++)
 66     {
 67         QuickSort(nums,p+5*i,p+5*i+4);
 68         swap(nums[p+i],nums[p+5*i+2]);
 69     }
 70     //找全部中位數的中位數
 71     int x=Select(nums,p,p+(r-p-4)/5,(r-p-4)/10);
 72     //以x爲基準作一次快排
 73     int i=Partition(nums,p,r,x);
 74     int j=i-p+1;
 75     //判斷k屬於那個部分
 76     if(k<=j)
 77         return Select(nums,p,i,k);
 78     else
 79         return Select(nums,i+1,r,k-j);
 80 }
 81 int main(){
 82     ofstream fout;
 83     double cost;
 84     int i = 0,m = 0,n = 0,key = 0;
 85     cout<<"Please enter the number of times you want to run the program:";        //輸入程序運行次數
 86     cin>>m;
 87     int data_amount[m];
 88     double runtime[m];
 89     srand((unsigned int)time(NULL));      //設置隨機數種子
 90     for(i=0;i<m;i++)
 91     {
 92         n=10000+RAND_MAX*(rand()%300)+rand();           //RAND_MAX=32767,隨機生成數據量
 93         data_amount[i]=n;                               //限定數據規模爲10000~9872867
 94         cout<<"☆The "<<i+1<<"th test Data amount is:"<<n<<endl;
 95         int j=0;
 96         int *a=(int *)malloc(n*sizeof(int));
 97         for(j=0;j<n;j++){
 98             a[j]=RAND_MAX*(rand()%400)+rand();          //隨機生成0~13139567的隨機數
 99         }
100         key=rand()%10000+1;   //隨機生成1~10000做爲所要選擇的次序
101         QueryPerformanceFrequency(&nFreq);//獲取系統時鐘頻率
102         QueryPerformanceCounter(&nBeginTime);//獲取開始時刻計數值
103         int t=Select(a,0,n-1,key);
104         QueryPerformanceCounter(&nEndTime);//獲取中止時刻計數值
105         cost=(double)(nEndTime.QuadPart - nBeginTime.QuadPart) / (double)nFreq.QuadPart;
106         runtime[i]=cost;
107         cout<<"The "<<key<<"th number is:"<<t<<endl;
108         cout<<"The running time is:"<<cost<<" s"<<endl;
109         free(a);
110     }
111     fout.open("data.txt");
112     if(!fout){
113         cerr<<"Can not open file 'data.txt' "<<endl;
114         return -1;
115     }
116     for(i=0;i<m;i++){
117            fout<<data_amount[i]<<","<<runtime[i]<<endl;
118     }
119     fout.close();
120     cout<<"Success!"<<endl;
121     return 0;
122 }

程序設計思路:

假設輸入的數據規模爲n,要查找的數據次序爲k。

(1)判斷數組長度,若小於75則直接進行快速排序,不然進行以後的算法。

(2)將n個輸入元素劃分紅⌈n/5⌉個組,每組五個元素。用快速排序將每組中的元素排好序,並肯定每組的中位數,共⌈n/5⌉個。

(3)遞歸調用算法Select來找出這⌈n/5⌉個元素的中位數。若是⌈n/5⌉是偶數,就找它的兩個中位數中較大的一個。以這個元素做爲劃分基準。

(4)以此遞歸調用進行排序,最終搜索獲得要找的第k項。

時間複雜性分析:

爲了分析算法Select的計算時間複雜性,設n=r-p+1,即n爲輸入數組的長度。算法的遞歸調用只有在n≥75時才執行。所以,當n<75是算法Select所用的計算時間不超過一個常數C1。找到中位數的中位數x後,算法Select以x爲劃分基準調用Partition對數組a[p:r]進行劃分,這須要O(n)時間。算法Select的for循環體行共執行n/5次,每一次須要O(1)時間。所以,執行for循環共需O(n)時間。

設對n個元素的數組調用算法Select須要T(n)時間,那麼找中位數的中位數x至多用了T(n/5)的時間。已經證實了,按照算法所選的基準x進行劃分所獲得的2個子數組分別至多有3n/4個元素。因此,不管對哪個子數組調用,Select都至多用了T(3n/4)的時間。

總之,能夠獲得關於T(n)的遞歸式

 

 

 

解此遞歸式可得T(n)=O(n)

通過5000次不一樣規模數據的實驗並統計運行時間獲得以下算法效率分析圖

相關文章
相關標籤/搜索