title: 2019-8-26 最小的k個數
tags: 算法,每日一題,矩陣打印
最小的k個數
1. 問題描述
數組中有一個數字出現的次數超過數組長度的一半,請找出這個數字。例如輸入一個長度爲9的數組{1,2,3,2,2,2,5,4,2}。由於數字2在數組中出現了5次,超過數組長度的一半,因此輸出2。如果不存在則輸出0。
2. 題目解析
這道題目如果單單只是要得到正確結果的話還是很簡單的,關鍵是如果要使時間複雜度達到O(n),那麼難度就會大很多。另外還要對輸入參數進行判斷。如果 k
大於數組大小,直接返回空數組。另外還要判斷 k
是不是小於等於0,如果是也直接返回空數組。還有就是輸入數組爲空的話也直接返回空數組。其他情況正常處理即可。
2.1 具體思路
這裏提供了三種方法:
方法一:對數組進行排序,取前k個元素即可。
方法二:使用大根堆進行求解,大根堆的大小爲k,如果大根堆根節點的值大於當前遍歷到的input的值將大根堆根節點彈出,壓入當前input位置的值。最後輸出大根堆中的k個數即可。
方法三:第三種方法是最巧妙的方法,能夠是時間複雜度達到O(n)。所以重點說一下這種方法。這種方法要借鑑快速排序中的partition思想,partition做的一個事就是選定一個值我們稱爲哨兵,然後數組中小於等於哨兵值的數我們放在哨兵值的左邊,大於哨兵值的數字我們放在其右邊。具體的算法如下:
後面發現這種方法有點問題對於這個用例 5,4,4,4,4,5,4,4 k=7發現通不過。我現在固定選定的哨兵就是最後一個第一次partition之後的結果爲:
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> res;
if(input.size() < k || k <= 0 || input.size() <= 0) return res;//這裏要對輸入進行判斷,否則要發生錯誤
if(input.size() == k) return input;
GetLeastNumbers_Solution3(input, k, res);
return res;
}
void GetLeastNumbers_Solution1(vector<int> &input, int k, vector<int>& res){//方法一:排序後取前k個數,複雜度O(nlogn)
sort(input.begin(), input.end());
for(int i=0; i<k; ++i){
res.push_back(input[i]);
}
}
void GetLeastNumbers_Solution2(vector<int> &input, int k, vector<int>& res){//方法二:使用大根堆的方式,複雜度O(nlogk)
//以下兩種方法都可以
//使用優先隊列實現
priority_queue<int> p;
for (int i = 0; i < input.size(); ++i) {
if (p.size() < k) {
p.push(input[i]);
}
else {
if (p.top() > input[i]) {
p.pop();
p.push(input[i]);
}
}
}
while (!p.empty()) {
res.push_back(p.top());
p.pop();
}
//使用堆實現
/*res = vector<int>(input.begin(), input.begin() + k);
make_heap(res.begin(), res.end());
for (int i = k; i < input.size(); ++i)
{
if (input[i] < res[0])
{
//先pop,然後在容器中刪除
pop_heap(res.begin(), res.end());//pop_head並不會刪除堆頂元素只是將其放在最後
res.pop_back();
//先在容器中加入,再push
res.push_back(input[i]);
push_heap(res.begin(), res.end());
}
}*/
}
void swap(vector<int> &input, int a, int b){
int temp = input[a];
input[a] = input[b];
input[b] = temp;
}
void GetLeastNumbers_Solution3(vector<int> &input, int k, vector<int>& res){//方法三:使用partition的方式
int len = input.size();
int begin = 0, end = len-1;
int sentry = -1;
while(sentry != k){
int rand_num = input[end];
int less = begin; int great = end;
while(less < great){//partition過程
if(input[less] > input[end]){
swap(input, less, --great);
}else{
++less;
}
}
swap(input, great, end);
sentry = great;
if(sentry < k){//繼續往左邊走
begin = sentry;
}else{//往左邊走
end = sentry-1;
}
}
for(int i=0; i<sentry; ++i){
res.push_back(input[i]);
}
}
};
更多關於編程和機器學習資料請關注FlyAI公衆號。