劍指offer 40. 最小的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

方法1:大根堆

構建堆+堆排序可參考視頻

大根堆的性質:

1.完全二叉樹
2.父節點>子節點

堆排序:
1.對於一個大根堆,每次將堆頂和堆中最後一個元素交換,堆的大小-1
2.對堆頂調用heapfiy使其保持堆結構
3.重複該過程知道堆有序

堆排序參考鏈接
比較直觀的想法是使用堆數據結構來輔助得到最小的 k 個數。堆的性質是每次可以找出最大或最小的元素。我們可以使用一個大小爲 k 的最大堆(大頂堆),將數組中的元素依次入堆,當堆的大小超過 k 時,便將多出的元素從堆頂彈出。我們以數組
[5,4,1,3,6,2,9],k=3 爲例展示元素入堆的過程,如下面動圖所示:
在這裏插入圖片描述

這樣,由於每次從堆頂彈出的數都是堆中最大的,最小的 k 個元素一定會留在堆裏。這樣,把數組中的元素全部入堆之後,堆中剩下的 k 個元素就是最大的 k 個數了。

注意在動畫中,我們並沒有畫出堆的內部結構,因爲這部分內容並不重要。我們只需要知道堆每次會彈出最大的元素即可。在寫代碼的時候,我們使用的也是庫函數中的優先隊列數據結構,如 Java 中的 PriorityQueue。在面試中,我們不需要實現堆的內部結構,把數據結構使用好,會分析其複雜度即可。

代碼實現

//保持堆的大小爲k,然後遍歷數組中的數字,遍歷的時候作如下判斷:
//1.若目前堆的大小小於k,將當前數字放入k中
//2.否則判斷當前元素與大根堆堆頂元素的大小關係,若小於堆頂元素,先poll掉堆頂,再將該數字放入堆中
//3.若當前元素比大根堆堆頂小,先poll掉堆頂,再將該數字放入堆中

class Solution {
    public int[] getLeastNumbers(int[] arr, int k) {
        if(k == 0 || arr.length == 0){
            return new int[0];
        }
        //默認是小根堆,實現大根堆需要重寫一下比較器
        Queue<Integer> pq = new PriorityQueue<>((v1,v2) -> v2-v1);
        for(int num:arr){
            if(pq.size() < k){
                pq.offer(num);
            }else if(num<pq.peek()){
                pq.poll();
                pq.offer(num);
            }
        }

        //返回堆中的元素
        int[] res = new int[pq.size()];
        int idx = 0;
        for(int num :pq){
            res[idx++] = num;
        }
        return res;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章