題目描述
在上一回裏我們知道Nettle在玩《艦これ》,Nettle的鎮守府有很多船位,但船位再多也是有限的。Nettle通過撈船又出了一艘稀有的船,但是已有的N(1≤N≤1,000,000)個船位都已經有船了。所以Nettle不得不把其中一艘船拆掉來讓位給新的船。Nettle思考了很久,決定隨機選擇一個k,然後拆掉稀有度第k小的船。 已知每一艘船都有自己的稀有度,Nettle現在把所有船的稀有度值告訴你,希望你能幫他找出目標船。
輸入
第1行:2個整數N,k。N表示數組長度,
第2行:N個整數,表示a[1..N],保證不會出現重複的數,1≤a[i]≤2,000,000,000。
輸出
第1行:一個整數t,表示t在數組中是第k小的數,若K不在數組中,輸出-1。
樣例輸入
10 4
1732 4176 2602 6176 1303 6207 3125 1 1011 6600
樣例輸出
1732
算法
無意間刷到這題,並且原題已經給出算法的提示。趁着這題讓我回顧了下快速排序,並且在這邊先記錄這題算法的核心思想。
快速排序每次都能確定一個元素的位置,也就是說,能知道這個元素是數組中第幾小的數。那麼將k與該元素所在的位置進行比較:
如果正好就是第k小的數,直接打印退出即可;
如果該位置比k大,說明第k小的數在該元素左邊的那堆數中,往左邊走,重複之前的步驟;
如果該位置比k小,說明第k小的數在該元素右邊的那堆數中,往右邊走,重複之前的步驟。
由於不是進行全局的交換位置,每次都交換需要尋找的區域,所以時間複雜度爲O(logn);但是當遇見一開始就完全有序的情況,或者是從大到小排序的情況就比較蛋疼,所以最後能放置一個標誌位,如果一輪下來不曾交換可以直接通過數學的方法計算出第k小的位置。當然,這是在本題中這麼說,如果真實的快排中,選擇主元是一門比較大的學問。
當然這題也可以用堆排序實現,維護一個N-k這麼大的最小堆,最後的堆頂就是需要找的元素,說實話藉助優先隊列的話,還是十分簡單的。
代碼
#include <iostream>
using namespace std;
void Swap(int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
// freopen("stdin.txt", "r", stdin);
int N, k;
scanf("%d %d", &N, &k);
int Arr[N];
for (int i = 0; i < N; i++)
{
scanf("%d", &Arr[i]);
}
if (k < 1 || k > N)
printf("-1\n");
// 算法主體
int left = 0, right = N-1;
while(left <= right)
{
int pivot = Arr[left];
int p = left, q = right;
while (1)
{
while (Arr[q] >= pivot && q > p)
q--;
while (Arr[p] <= pivot && p < q)
p++;
if (p < q)
swap(Arr[p], Arr[q]);
else
break;
}
Swap(Arr[left], Arr[p]);
// for (int f = 0; f < N; f++)
// cout << Arr[f] << ' ';
// cout << endl;
if(p == k-1)
{
printf("%d\n", Arr[p]);
break;
}
else if (p > k-1)
right = p - 1;
else
left = p + 1;
}
return 0;
}