題目:
給出一個O(n)時間的演算法,在給定一個有n個不同數位集合S以及一個正整數k<=n後,它能確定出S中最接近其中位元的k個數
思考:
step1:求出數組的中位元的值O(n)
step2:計算數組每個數與中位元差的絕對值,存於另一個數組B中O(n)
step3:求出數組B中第k小的數ret O(n)
step4:計算數組S中與ret差的絕對值小於ret的數並輸出O(n)
其中,step4也可以通過劃分的方法找出數組S中與ret差的絕對值小於ret的數
代碼:
#include <iostream>using namespace std;int length_A;void Print(int *A){int i;for(i = 1; i <= length_A; i++)cout<<A[i]<<' ';cout<<endl;}/*************最壞情況線性時間的選擇**************************************************///已經出現很多次了,不解釋int Partition(int *A, int p, int r){int x = A[r], i = p-1, j;for(j = p; j < r; j++){if(A[j] <= x){i++;swap(A[i], A[j]);}}swap(A[i+1], A[r]);return i+1;}int Select(int *A, int p, int r, int i);//對每一組從start到end進行插入排序,並返回中值//插入排序很簡單,不解釋int Insert(int *A, int start, int end, int k){int i, j;for(i = 2; i <= end; i++){int t = A[i];for(j = i; j >= start; j--){if(j == start)A[j] = t;else if(A[j-1] > t)A[j] = A[j-1];else{A[j] = t;break;}}}return A[start+k-1];}//根據文中的演算法,找到中值的中值int Find(int *A, int p, int r){int i, j = 0;int start, end, len = r - p + 1;int *B = new int[len/5+1];//每5個元素一組,長度為start到end,對每一組進行插入排序,並返回中值for(i = 1; i <= len; i++){if(i % 5 == 1)start = i+p-1;if(i % 5 == 0 || i == len){j++;end = i+p-1;//對每一組從start到end進行插入排序,並返回中值,如果是最後一組,組中元素個數可能少於5int ret = Insert(A, start, end, (end-start)/2+1);//把每一組的中值挑出來形成一個新的數組B[j] = ret;}}//對這個數組以遞迴調用Select()的方式尋找中值int ret = Select(B, 1, j, (j+1)/2);//delete []B;return ret;}//以f為主元的劃分int Partition2(int *A, int p, int r, int f){int i;//找到f的位置並讓它與A[r]交換for(i = p; i < r; i++){if(A[i] == f){swap(A[i], A[r]);break;}}return Partition(A, p, r);}//尋找數組A[p..r]中的第i大的元素,i是從1開始計數,不是從p開始int Select(int *A, int p, int r, int i){//如果數組中只有一個元素,則直接返回if(p == r)return A[p];//根據文中的演算法,找到中值的中值int f = Find(A, p, r);//以這個中值為主元的劃分,返回中值在整個數組A[1..len]的位置//因為主元是數組中的某個元素,劃分好是這樣的,A[p..q-1] <= f < A[q+1..r]int q = Partition2(A, p, r, f);//轉換為中值在在數組A[p..r]中的位置int k = q - p + 1;//與所尋找的元素相比較if(i == k)return A[q];else if(i < k)return Select(A, p, q-1, i);else//如果主元是數組中的某個元素,後面一半要這樣寫return Select(A, q+1, r, i-k);//但是如果主元不是數組中的個某個元素,後面一半要改成Select(A, q, r, i-k+1)}int main(){int k, i;while(cin>>length_A>>k){if(k > length_A){cout<<"error:k > length_A"<<endl;continue;}//產生隨機資料int *A = new int[length_A+1];for(i = 1; i <= length_A; i++)A[i] = rand() % 100;Print(A);//計算中位元int mid = Select(A, 1, length_A, (length_A+1)/2);//計算每個數與中位元的距離int *B = new int[length_A+1];for(i = 1; i <= length_A; i++)B[i] = abs(A[i] - mid);//選擇第k小的數int ret = Select(B, 1, length_A, k);//求出數組是與中位元距離小於ret的數,並輸出for(i = 1; i <= length_A; i++)if(abs(A[i] - mid) <= ret)cout<<A[i]<<' ';cout<<endl;delete []A;}return 0;}