每日一練(21):最小的k個數

加班猿發表於2022-02-23

title: 每日一練(21):最小的k個數

categories:[劍指offer]

tags:[每日一練]

date: 2022/02/17


每日一練(21):最小的k個數

輸入整數陣列 arr ,找出其中最小的 k 個數。例如,輸入4、5、1、6、2、7、3、8這8個數字,則最小的4個數字是1、2、3、4。

示例 1:

輸入:arr = [3,2,1], k = 2

輸出:[1,2] 或者 [2,1]

示例 2:

輸入:arr = [0,1,2,1], k = 1

輸出:[0]

限制:

0 <= k <= arr.length <= 10000

0 <= arr[i] <= 10000

來源:力扣(LeetCode)

連結:https://leetcode-cn.com/probl...

方法一:sort排序

思路和演算法

對原陣列從小到大排序後取出前 k 個數即可。

複雜度分析

  • 時間複雜度:O(n logn),其中 n 是陣列 arr 的長度。演算法的時間複雜度即排序的時間複雜度。
  • 空間複雜度:O(logn),排序所需額外的空間複雜度為 O(logn)。
vector<int> getLeastNumbers(vector<int>& arr, int k) {
    vector<int> ans(k, 0);
    if (arr.size() == 0 || k == 0) {
        return ans;
    }
    sort(arr.begin(), arr.end());    //排序
    for (int i = 0; i < k; i++) {
        ans.push_back(arr[i]);        //新增到目標vector
    }
    return ans;
}

方法二:堆

思路和演算法

我們用一個大根堆實時維護陣列的前 k 小值。首先將前 k 個數插入大根堆中,隨後從第 k+1 個數開始遍歷,如果當前遍歷到的數比大根堆的堆頂的數要小,就把

堆頂的數彈出,再插入當前遍歷到的數。最後將大根堆裡的數存入陣列返回即可。在下面的程式碼中,由於 C++ 語言中的堆(即優先佇列)為大根堆,我們可以這

麼做。而 Python 語言中的堆為小根堆,因此我們要對陣列中所有的數取其相反數,才能使用小根堆維護前 k 小值。

複雜度分析

  • 時間複雜度:O(n logk),其中 nn 是陣列 arr 的長度。由於大根堆實時維護前 k 小值,所以插入刪除都是 O(logk) 的時間複雜度,最壞情況下陣列裡 n 個數都會插入,所以一共需要 O(nlogk) 的時間複雜度。
  • 空間複雜度:O(k),因為大根堆裡最多 k 個數。
vector<int> getLeastNumbers(vector<int>& arr, int k) {
    vector<int> ans(k, 0);
    if (arr.size() == 0 || k == 0) {
        return ans;
    }
    priority_queue<int> q;
    for (int i = 0; i < k; ++i) {    //將前k個數插入堆中
        q.push(arr[i]);
    }
    for (int i = 0; i < (int)arr.size(); ++i) {
        if (q.top() > arr[i]) {        //第 k+1 個數開始遍歷,如果當前遍歷到的數比大根堆的堆頂的數要小,就把                         
            q.pop();               //堆頂的數彈出,再插入當前遍歷到的數
            q.push(arr[i]);
        }
    }
    for (int i = 0; i < k; ++i) {
        ans[i] = q.top();            //把堆中的數存到目標vector中
        q.pop();
    }
    return ans;
}

相關文章