亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Java?詳細講解用堆解決Top-k問題

 更新時間:2022年04月14日 09:28:44   作者:Scintillator.?/  
TopK問題即在N個數(shù)中找出最大的前K個,這篇文章將詳細講解如何利用小根堆的方法解決TopK問題,文中代碼具有一定參考價值,快跟隨小編一起學(xué)習(xí)一下吧

要解決 top-k 問題,我們應(yīng)該先熟悉一種數(shù)據(jù)結(jié)構(gòu) - 堆(優(yōu)先級隊列),已經(jīng)了解的朋友可以跳過哦。

1、什么是堆?

堆結(jié)構(gòu)

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

我們假設(shè)一顆普通的二叉樹可以用數(shù)組存儲,那么就可以得到如下結(jié)構(gòu):

在這里插入圖片描述

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

在這里插入圖片描述

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

例如下圖:

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

他的左孩子下標(biāo)是:2 * 2 + 1 = 3

他的右孩子下標(biāo)是:2 * 2 + 2 = 4

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

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

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

在這里插入圖片描述

大根堆 VS 小根堆

大根堆(最大堆)

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

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

在這里插入圖片描述

具體調(diào)整過程如下:

在這里插入圖片描述

在這里插入圖片描述

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

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

在這里插入圖片描述

// 將數(shù)組變成大根堆結(jié)構(gòu)
public void createHeap(int[] arr){
    for (int i = 0; i < arr.length; i++) {
        elem[i] = arr[i];// 放入elem[],假設(shè)不需要擴容
        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;// 如果不需要調(diào)整了,說明當(dāng)前子樹已經(jīng)是大根堆了,直接 break
        swap(elem,parent,child);
        parent = child;// 繼續(xù)向下檢測,看是否要調(diào)整
        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é)點

調(diào)整過程同上。

在這里插入圖片描述

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

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

// 默認得到一個小根堆
PriorityQueue<Integer> smallHeap = new PriorityQueue<>();
smallHeap.offer(23);
smallHeap.offer(2);
smallHeap.offer(11);
System.out.println(smallHeap.poll());// 彈出2,剩余最小的元素就是11,會被調(diào)整到堆頂,下一次彈出
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問題解決思路

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

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

思路二: 將元素全部放入一個堆結(jié)構(gòu)中,然后彈出三個元素,每次彈出的元素都是當(dāng)前堆最小的,那么彈出的三個元素就是前最小的三個元素。

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

思路三:

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

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

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

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

// 找前 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;
 }

以上就是top-k問題的基本思路,其他的類似問題也是這樣解。

總結(jié):

1、如果求前K個最大的元素,要建一個小根堆。

2、如果求前K個最小的元素,要建一個大根堆。

3、如果求第K大的元素,要建一個小根堆 ( 堆頂元素就是 )。

4、如果求第K小的元素,要建一個大根堆 ( 堆頂元素就是 )。

到此這篇關(guān)于Java 詳細講解用堆解決Top-k問題的文章就介紹到這了,更多相關(guān)Java Top-k問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

您可能感興趣的文章:

相關(guān)文章

  • SpringBoot對接小程序微信支付的實現(xiàn)

    SpringBoot對接小程序微信支付的實現(xiàn)

    本文主要介紹了SpringBoot對接小程序微信支付的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧<BR>
    2023-09-09
  • SpringBoot 如何使用RestTemplate發(fā)送Post請求

    SpringBoot 如何使用RestTemplate發(fā)送Post請求

    這篇文章主要介紹了SpringBoot 如何使用RestTemplate發(fā)送Post請求的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • 更簡單更高效的Mybatis?Plus最新代碼生成器AutoGenerator

    更簡單更高效的Mybatis?Plus最新代碼生成器AutoGenerator

    這篇文章主要為大家介紹了更簡單更高效的Mybatis?Plus最新代碼生成器AutoGenerator使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 完美解決idea moudle沒有藍色的小方塊的問題

    完美解決idea moudle沒有藍色的小方塊的問題

    這篇文章主要介紹了完美解決idea moudle沒有藍色的小方塊的問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • futuretask源碼分析(推薦)

    futuretask源碼分析(推薦)

    這篇文章主要介紹了futuretask源碼分析(推薦),小編覺得還是挺不錯的,這里給大家分享下,供各位參考。
    2017-10-10
  • 基于Java中字符串內(nèi)存位置詳解

    基于Java中字符串內(nèi)存位置詳解

    下面小編就為大家?guī)硪黄贘ava中字符串內(nèi)存位置詳解。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • Spring profile通過多種方法實現(xiàn)多環(huán)境支持

    Spring profile通過多種方法實現(xiàn)多環(huán)境支持

    這篇文章主要介紹了Spring profile通過多種方法實現(xiàn)多環(huán)境支持,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-10-10
  • mybatis-spring:@MapperScan注解的使用

    mybatis-spring:@MapperScan注解的使用

    這篇文章主要介紹了mybatis-spring:@MapperScan注解的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java 定時器的多種實現(xiàn)方式

    Java 定時器的多種實現(xiàn)方式

    本文介紹了Java中定時器的多種實現(xiàn)方式,有此需求的朋友可以根據(jù)實際選擇適合自己的方式
    2021-06-06
  • Java環(huán)境配置原理全面解析

    Java環(huán)境配置原理全面解析

    下面小編就為大家?guī)硪黄狫ava環(huán)境配置原理全面解析。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-09-09

最新評論