溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java怎么用堆解決Top-k問題

發(fā)布時間:2022-04-14 10:25:33 來源:億速云 閱讀:203 作者:iii 欄目:開發(fā)技術

本篇內容介紹了“Java怎么用堆解決Top-k問題”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

1、什么是堆?

堆結構

堆其實就是一種二叉樹,但是普通的二叉樹是以鏈式結構進行儲存數據的,而堆是以數組進行順序存儲數據的。那么什么樣的二叉樹才適合用順序存儲的方式呢?

我們假設一顆普通的二叉樹可以用數組存儲,那么就可以得到如下結構:

Java怎么用堆解決Top-k問題

我們可以看到,當二叉樹中間有空值時,數組的存儲空間會被浪費,那么什么情況下空間才不會被浪費呢? 那就是完全二叉樹。

Java怎么用堆解決Top-k問題

從以上結構中,我們不能用鏈式結構的指針來訪問孩子節(jié)點或者父親節(jié)點,只能通過對應下標來訪問,其實也比較簡單。

例如下圖:

已知 2 節(jié)點的下標是1,那么

他的左孩子下標是:2 * 2 + 1 = 3

他的右孩子下標是:2 * 2 + 2 = 4

相反,已知 1 節(jié)點的下標是3,3 節(jié)點的下標是4,那么

1 節(jié)點的父親節(jié)點下標是:(3 - 1) / 2 = 1

3 節(jié)點的父親節(jié)點下標是:(4 - 1) / 2 = 1

Java怎么用堆解決Top-k問題

大根堆 VS 小根堆

大根堆(最大堆)

大根堆保證,每顆二叉樹的根節(jié)點都 大于 左右孩子節(jié)點

從最后一棵子樹的根節(jié)點開始調整,來到每顆子樹的根節(jié)點,使得每棵子樹都向下調整為大根堆,最后再向下做最后調整,保證二叉樹整體是大根堆(這個調整主要是為了后面的堆排序)。

Java怎么用堆解決Top-k問題

具體調整過程如下:

Java怎么用堆解決Top-k問題

Java怎么用堆解決Top-k問題

怎么用代碼實現(xiàn)呢?

我們首先從最后一棵子樹調整,那么就要拿到最后一顆子樹的根節(jié)點 parent ,我們知道數組最后一個節(jié)點下標是 len - 1,而這個節(jié)點是最后一棵子樹的左孩子或者右孩子,根據孩子下標就可以拿到根節(jié)點下標( parent ) ,parent-- 就可以讓每顆子樹都進行調整,直到來到根節(jié)點,再向下調整最后一次,便可以得到大根堆。

Java怎么用堆解決Top-k問題

// 將數組變成大根堆結構
public void createHeap(int[] arr){
    for (int i = 0; i < arr.length; i++) {
        elem[i] = arr[i];// 放入elem[],假設不需要擴容
        usedSize++;
    }
    // 得到根節(jié)點parent, parent--依次來到每顆子樹的根節(jié)點,
    for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
        // 依次向下搜索,使得每顆子樹都變成大根堆
        shiftDown(parent,usedSize);
    }
}
// 向下搜索變成大根堆
public void shiftDown(int parent,int len){
    int child = parent*2+1;// 拿到左孩子
    while (child < len){
        // 如果有右孩子,比較左右孩子大小,得到較大的值和父節(jié)點比較 
        if (child+1 < len && (elem[child] < elem[child+1])){
            child++;
        }
        // 比較較大的孩子和父節(jié)點,看是否要交換
        int max = elem[parent] >= elem[child] ? parent : child;
        if (max == parent) break;// 如果不需要調整了,說明當前子樹已經是大根堆了,直接 break
        swap(elem,parent,child);
        parent = child;// 繼續(xù)向下檢測,看是否要調整
        child = parent*2+1;
    }
}
public void swap(int[] arr,int i,int j){
  	int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
小根堆(最小堆)

小根堆保證,每顆二叉樹的根節(jié)點都 小于 左右孩子節(jié)點

調整過程同上。

Java怎么用堆解決Top-k問題

優(yōu)先級隊列(PriorityQueue)

在java中,提供了堆這種數據結構(PriorityQueue),也叫優(yōu)先級隊列,當我們創(chuàng)建一個這樣的對象時,就得到了一個沒有添加數據的 小根堆 ,我們可以向里面添加或者刪除元素,每向里面刪除或者添加一個元素,系統(tǒng)會整體進行一次調整,重新又調整為小根堆。

// 默認得到一個小根堆
PriorityQueue<Integer> smallHeap = new PriorityQueue<>();
smallHeap.offer(23);
smallHeap.offer(2);
smallHeap.offer(11);
System.out.println(smallHeap.poll());// 彈出2,剩余最小的元素就是11,會被調整到堆頂,下一次彈出
System.out.println(smallHeap.poll());// 彈出11

 // 如果需要得到大根堆,在里面?zhèn)饕粋€比較器
 PriorityQueue<Integer> BigHeap = new PriorityQueue<>(new Comparator<Integer>() {
     @Override
     public int compare(Integer o1, Integer o2) {
         return o2 - o1;
     }
 });

2、top-k問題解決思路

例:有一堆元素,讓你找出前三個最小的元素。

思路一: 將數組從小到大排序,拿到數組前3個元素。但是可以發(fā)現(xiàn)這樣時間復雜度太高,不可取。

思路二: 將元素全部放入一個堆結構中,然后彈出三個元素,每次彈出的元素都是當前堆最小的,那么彈出的三個元素就是前最小的三個元素。

這種思路可以做,但是假設我有1000000個元素,只彈出前三個最小的元素,那么就要用到大小為1000000的堆。這么做空間復雜度太高,不建議用這種方法。

思路三:

我們需要得到三個最小的元素,那么就建一個大小為3的堆,假設目前的堆結構中剛好放滿了3個元素,那么這三個元素就是當前最小的三個元素。假設第四個元素是我們想要的元素之一,那么前三個至少有一個元素不是我們想要的,就需要彈出,那么彈出誰呢?

我們要得到的是前三個最小的元素,所以當前堆結構中最大的元素一定不是我們想要的,所以這里我們建一個大根堆。彈出該元素,然后放入第四個元素,直到遍歷完整個數組。

Java怎么用堆解決Top-k問題

Java怎么用堆解決Top-k問題

Java怎么用堆解決Top-k問題

這樣我們就得到了只含有前三個最小元素的堆,并且可以看到堆的大小一直都是3,而不是有多少數據就建多大的堆,然后再依次彈出元素就行了。

// 找前 k個最小的元素
public static int[] topK(int[] arr,int k){
     // 創(chuàng)建一個大小為 k的大根堆
     PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k,new Comparator<Integer>() {
         @Override
         public int compare(Integer o1, Integer o2) {
             return o2 - o1;
         }
     });
     for (int i = 0; i < arr.length; i++) {
         if (i < k){
             // 放入前 k 個元素
             maxHeap.offer(arr[i]);
         }else{
             // 從第 k+1個元素開始進行判斷是否要入堆
             if (maxHeap.peek() > arr[i]){
                 maxHeap.poll();
                 maxHeap.offer(arr[i]);
             }
         }
     }
     int[] ret = new int[k];
     for (int i = 0; i < k; i++) {
         ret[i] = maxHeap.poll();
     }
     return ret;
 }

“Java怎么用堆解決Top-k問題”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI