輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。
第一個思路:利用大根堆。也是解決top k海量數據的關鍵
延伸部分(重要)
大堆還是小堆的選擇很重要,不是尋找最小的k個元素就要選擇小堆,而且恰恰相反。
尋找最小的k個數,其實就是尋找第k個大的元素,即尋找k個數中最大的,
不斷調整堆,堆得元素個數是k,堆頂是最大值,遍歷完初始數組后,
堆中存在的元素即使我們所要尋找的k個最小元素。
題解:
1:首先選取前K個數建立最大堆(根結點值大於左右結點值)。
2:此后,每次從原數組中取一個元素與根進行比較,如果大於根結點的元素,忽視之,取下一個數組元素繼續該過程;
如果小於根結點的元素,則將其加入最大堆,並進行堆調整(和堆頂替換),將根元素移動到最后再刪除,
即保證最大堆中的元素仍然是排名前K的數,且根元素仍然最大。
make_heap函數用法
這里使用了make_heap構建堆也可以手動實現堆
/* 先后建一個大根堆,利用make_heap函數, 然后將容器剩余元素比較, * 小於堆頂的就調整,(入堆,堆頂元素在彈出), * 大於堆頂的跳過 * 最后對容器排序 */ class Solution { public: vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { vector<int> vec; int len =input.size(); if (len <= 0 || len < k || k <= 0 ) return vec; // 將k個數放入vec,然后構建成大根堆 for (int i = 0; i < k; i++) vec.push_back(input[i]); make_heap(vec.begin(), vec.end(), less<int>()); for(int i = k; i < len; i++) { if (input[i] > vec.front()) // 當前元素大於大根堆堆頂,跳過 continue; else { vec.push_back(input[i]); // 添加新元素,調整堆 push_heap(vec.begin(), vec.end()); // 將堆頂元素調整到最后 pop_heap(vec.begin(), vec.end()); // 將最大的放到最后,然后對容器最后一個刪除 vec.pop_back(); //刪除最后那個元素 } } // 上面是將最小的k個放入了vec容器中,但是是亂序的,但是這個題也可以不用排序 sort_heap(vec.begin(),vec.end()); return vec; } };
這里是手動實現堆的, 面試常考手寫堆排和快排哦
class Solution2 { public: // 首先構建一個大根堆(len 是數組長度,index是第一個非葉子節點下標) 構建長度為len,從index開始 void adjust(vector<int>&vec, int len, int index) { if (index > len) // 遞歸出口 return ; int left = 2*index + 1; // index的左孩子 int right = 2*index + 2; // index的右孩子 int maxIdx = index; // 將maxidx作為子樹的最大值, 在下標不越界的情況下 if (vec[maxIdx] < vec[left] && left < len) { maxIdx = left; } if(vec[maxIdx] < vec[right] && right < len) { maxIdx = right; } if (index != maxIdx) // 最大值不是在堆頂就交換 { swap(vec[index], vec[maxIdx]); adjust(vec, len, maxIdx); // 遞歸所有的子樹,構建 } } vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { int len = input.size(); if (len <= 0 || len < k || k <= 0 ) return vector<int>(); vector<int>max_heap; for(int i = 0; i < k; i++) max_heap.push_back(input[i]); // 對k個元素進行構建 小根堆, 循環結束后得到的是一個含有k個元素的大根堆 for(int i = k/2-1; i >= 0; i--) adjust(max_heap, k, i); // 對input容器中的剩余元素和堆頂比較 for(int i = k; i < len; i++) { if (input[i] < max_heap[0]) { max_heap[0] = input[i]; adjust(max_heap, k, 0); // 對堆頂元素進行調整 } } return max_heap; } };
第二個思路就是先進性排序,然后輸出,但是這並不是出題者要考察的,
但是我們也給出代碼(手寫快排)加深印象
// // Created by LK on 2020/3/15. // /* * 輸入n個整數,找出其中最小的K個數。例如輸入4,5,1,6,2,7,3,8這8個數字,則最小的4個數字是1,2,3,4,。 */ #include <iostream> #include <vector> using namespace std; // 思路一排序 class Solution { public: void quick_sort(vector<int>&arr, int left, int right) { int i = left; int j = right; int temp; if (left < right) { temp = arr[left]; while(i < j) { // 從后向前找個小的 while(i < j&& arr[j] >= temp) --j; if (i < j) arr[i++] = arr[j]; // 從前往后找個大的 while (i <j && temp > arr[i]) ++i; if (i < j) arr[j--] = arr[i]; } arr[i] = temp; quick_sort(arr, left, i-1); quick_sort(arr, i+1, right); } } vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { vector<int> result; int len = input.size(); if (k <= 0 || k > len) return result; quick_sort(input, 0, input.size()-1); for(int i = 0; i < k; i++) result.push_back(input[i]); return result; } }; int main() { vector<int> input = {4,5,1,6,2,7,3,8}; int k = 4; Solution s; input = s.GetLeastNumbers_Solution(input, k); for(int i = 0; i < k; i++) cout << input[i]<<" "; return 0; }