輸入n個整數,輸出其中最小的k個。
要求乙個序列中最小的k個數,按照慣有的思維方式,則是先對這個序列從小到大排序,然後輸出前面的最小的k個數。
至於選取什麼的排序方法,我想你可能會第一時間想到快速排序(我們知道,快速排序平均所費時間為n*logn
),然後再遍歷序列中前k個元素輸出即可。因此,總的時間複雜度:o(n * log n)+o(k)=o(n * log n)
。
咱們再進一步想想,題目沒有要求最小的k個數有序,也沒要求最後n-k個數有序。既然如此,就沒有必要對所有元素進行排序。這時,咱們想到了用選擇或交換排序,即:
1、遍歷n個數,把最先遍歷到的k個數存入到大小為k的陣列中,假設它們即是最小的k個數;
2、對這k個數,利用選擇或交換排序找到這k個元素中的最大值kmax(找最大值需要遍歷這k個數,時間複雜度為o(k)
);
3、繼續遍歷剩餘n-k個數。假設每一次遍歷到的新的元素的值為x,把x與kmax比較:如果x < kmax
,用x替換kmax,並回到第二步重新找出k個元素的陣列中最大元素kmax『;如果x >= kmax
,則繼續遍歷不更新陣列。
每次遍歷,更新或不更新陣列的所用的時間為o(k)
或o(0)
。故整趟下來,時間複雜度為n*o(k)=o(n*k)
。
void select_sortn(vector &vec, intn) }
swap(vec[k], vec[i]);
}}
更好的辦法是維護容量為k的最大堆,原理跟解法二的方法相似:
這樣下來,總的時間複雜度:o(k+(n-k)*logk)=o(n*logk)
。此方法得益於堆中進行查詢和更新的時間複雜度均為:o(logk)
(若使用解法二:在陣列中找出最大元素,時間複雜度:o(k))
。
void heap_sortn(vector &vec, vector &res, intn));
res.push_back(*vec.begin());
vec.erase(vec.begin());
}}
在《資料結構與演算法分析--c語言描述》一書,第7章第7.7.6節中,闡述了一種在平均情況下,時間複雜度為o(n)
的快速選擇演算法。如下述文字:
此演算法的平均執行時間為o(n)。
下面**實現了quickselect,前n個數就儲存在原陣列中.
void select_mink(vector &vec, int start, int end, intn)
else
}if(!flag)
else}}
vec[i] =temp;
if(i - start == n - 1
)
else
if(i - start < n - 1
)
else
}
這個快速選擇select演算法,類似快速排序的劃分方法。n個數儲存在陣列s中,再從陣列中選取「中位數的中位數」作為樞紐元x,把陣列劃分為sa和sb倆部分,sa<=x<=sb,如果要查詢的k個元素小於sa的元素個數,則返回sa中較小的k個元素,否則返回sa中所有元素+sb中小的k-|sa|個元素,這種解法在平均情況下能做到o(n)
的複雜度。
更進一步,《演算法導論》第9章第9.3節介紹了乙個最壞情況下亦為o(n)時間的select演算法,有興趣的讀者可以參看。
1、谷歌面試題:輸入是兩個整數陣列,他們任意兩個數的和又可以組成乙個陣列,求這個和中前k個數怎麼做?
分析:
「假設兩個整數陣列為a和b,各有n個元素,任意兩個數的和組成的陣列c有n^2個元素。
那麼可以把這些和看成n個有序數列:
a[1]+b[1] <= a[1]+b[2] <= a[1]+b[3] <=…
a[2]+b[1] <= a[2]+b[2] <= a[2]+b[3] <=…
…a[n]+b[1] <= a[n]+b[2] <= a[n]+b[3] <=…
問題轉變成,在這n^2個有序數列裡,找到前k小的元素」
2、有兩個序列a和b,a=(a1,a2,...,ak),b=(b1,b2,...,bk),a和b都按公升序排列。對於1<=i,j<=k,求k個最小的(ai+bj)。要求演算法盡量高效。
3、給定乙個數列a1,a2,a3,...,an和m個三元組表示的查詢,對於每個查詢(i,j,k),輸出ai,ai+1,...,aj的公升序排列中第k個數。
可以用類似於快排的思路去求解,**如下:
int select_num(vector &vec, int start, int end, intn)
else
}if(!flag)
else}}
vec[i] =temp;
if(i - start == n - 1
)
else
if(i - start < n - 1
)
else
}
這裡將start和end簡化為座標進行處理, 道理不變
尋找最小的k個數
尋找最小的k個數 在乙個長度為n的陣列中,尋找最小的k個數。最大的k個數解法類似 想法比較簡單,先對n個數排序,再輸入前面k個數,即可。這種方法的時間複雜度比較大。假設我們使用快排,需要o nlogn 然後輸出k個數需要o k 一共要o nlogn 略。這種方法比較好,演算法簡單,易於實現。先把陣列...
尋找最小的k個數
輸入n個整數,輸出其中最小的k個。要求乙個序列中最小的k個數,按照慣有的思維方式,則是先對這個序列從小到大排序,然後輸出前面的最小的k個數。至於選取什麼的排序方法,我想你可能會第一時間想到快速排序 我們知道,快速排序平均所費時間為n logn 然後再遍歷序列中前k個元素輸出即可。因此,總的時間複雜度...
尋找最小的 k 個數
輸入n個整數,輸出其中最小的k個。使用排序的方法來解決該問題。快速排序所費時間複雜度為o n logn 排序完成過後,只需要取前k個數值即是最小的k個數。int array const int length sizeof array sizeof array 0 void printarray in...