最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java?詳細(xì)講解用堆解決Top-k問題

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

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

1、什么是堆?

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

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

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

在這里插入圖片描述

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

在這里插入圖片描述

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

例如下圖:

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

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

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

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

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

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

在這里插入圖片描述

大根堆 VS 小根堆

大根堆(最大堆)

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

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

在這里插入圖片描述

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

在這里插入圖片描述

在這里插入圖片描述

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

我們首先從最后一棵子樹調(diào)整,那么就要拿到最后一顆子樹的根節(jié)點(diǎn) parent ,我們知道數(shù)組最后一個(gè)節(jié)點(diǎn)下標(biāo)是 len - 1,而這個(gè)節(jié)點(diǎn)是最后一棵子樹的左孩子或者右孩子,根據(jù)孩子下標(biāo)就可以拿到根節(jié)點(diǎn)下標(biāo)( parent ) ,parent-- 就可以讓每顆子樹都進(jìn)行調(diào)整,直到來到根節(jié)點(diǎn),再向下調(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è)不需要擴(kuò)容
        usedSize++;
    }
    // 得到根節(jié)點(diǎn)parent, parent--依次來到每顆子樹的根節(jié)點(diǎn),
    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é)點(diǎn)比較 
        if (child+1 < len && (elem[child] < elem[child+1])){
            child++;
        }
        // 比較較大的孩子和父節(jié)點(diǎn),看是否要交換
        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é)點(diǎn)都 小于 左右孩子節(jié)點(diǎn)

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

在這里插入圖片描述

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

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

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

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

2、top-k問題解決思路

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

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

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

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

思路三:

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

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

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

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

// 找前 k個(gè)最小的元素
public static int[] topK(int[] arr,int k){
     // 創(chuàng)建一個(gè)大小為 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 個(gè)元素
             maxHeap.offer(arr[i]);
         }else{
             // 從第 k+1個(gè)元素開始進(jìn)行判斷是否要入堆
             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個(gè)最大的元素,要建一個(gè)小根堆。

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

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

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

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

您可能感興趣的文章:

相關(guān)文章

  • Java中double數(shù)值保留兩位小數(shù)的4種實(shí)現(xiàn)方式舉例

    Java中double數(shù)值保留兩位小數(shù)的4種實(shí)現(xiàn)方式舉例

    在Java編程中,我們經(jīng)常遇到需要對double類型的浮點(diǎn)數(shù)進(jìn)行精確截?cái)嗷蛩纳嵛迦氡A魞晌恍?shù)的需求,這篇文章主要給大家介紹了關(guān)于Java中double數(shù)值保留兩位小數(shù)的4種實(shí)現(xiàn)方式,需要的朋友可以參考下
    2024-07-07
  • 一文詳解SpringBoot3如何自定義starter

    一文詳解SpringBoot3如何自定義starter

    在Spring Boot中,starter是一種特殊的依賴,它可以幫助開發(fā)人員快速引入和配置某個(gè)特定的功能模塊,我們在面試中通過會(huì)被問到SpringBoot3如何自定義starter,所以本文小編給大家詳細(xì)介紹了SpringBoot3自定義starter的步驟,需要的朋友可以參考下
    2024-09-09
  • MyBatis-Plus如何關(guān)閉SQL日志打印詳解

    MyBatis-Plus如何關(guān)閉SQL日志打印詳解

    在使用mybatisplus進(jìn)行開發(fā)時(shí),日志是一個(gè)非常有用的工具,它可以幫助我們更好地了解和調(diào)試我們的代碼,這篇文章主要給大家介紹了關(guān)于MyBatis-Plus如何關(guān)閉SQL日志打印的相關(guān)資料,需要的朋友可以參考下
    2024-03-03
  • Java?ThreadPoolExecutor線程池有關(guān)介紹

    Java?ThreadPoolExecutor線程池有關(guān)介紹

    這篇文章主要介紹了Java?ThreadPoolExecutor線程池有關(guān)介紹,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-09-09
  • Java中this關(guān)鍵字的用法詳解

    Java中this關(guān)鍵字的用法詳解

    我知道很多朋友都和我一樣,在JAVA程序中似乎經(jīng)常見到this,自己也偶爾用到它,但是到底this該怎么用,卻心中無數(shù),下面這篇文章主要給大家介紹了關(guān)于Java中this關(guān)鍵字用法的相關(guān)資料,需要的朋友可以參考下
    2023-05-05
  • java啟動(dòng)jar包設(shè)置啟動(dòng)參數(shù)的實(shí)現(xiàn)

    java啟動(dòng)jar包設(shè)置啟動(dòng)參數(shù)的實(shí)現(xiàn)

    本文主要介紹了java啟動(dòng)jar包設(shè)置啟動(dòng)參數(shù)的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 一文帶你解讀Java序列化和反序列化機(jī)制

    一文帶你解讀Java序列化和反序列化機(jī)制

    序列化是將對象轉(zhuǎn)換成二進(jìn)制字節(jié)流的過程;反序列化是從二進(jìn)制字節(jié)流中恢復(fù)對象的過程。本文將大家詳細(xì)講講二者的原理與實(shí)現(xiàn),希望對大家有所幫助
    2023-01-01
  • Spring Cloud之服務(wù)監(jiān)控turbine的示例

    Spring Cloud之服務(wù)監(jiān)控turbine的示例

    這篇文章主要介紹了Spring Cloud之服務(wù)監(jiān)控turbine的示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • SpringSecurity+OAuth2.0?搭建認(rèn)證中心和資源服務(wù)中心流程分析

    SpringSecurity+OAuth2.0?搭建認(rèn)證中心和資源服務(wù)中心流程分析

    OAuth?2.0?主要用于在互聯(lián)網(wǎng)上安全地委托授權(quán),廣泛應(yīng)用于身份驗(yàn)證和授權(quán)場景,這篇文章介紹SpringSecurity+OAuth2.0?搭建認(rèn)證中心和資源服務(wù)中心,感興趣的朋友一起看看吧
    2024-01-01
  • spring項(xiàng)目如何配置多數(shù)據(jù)源(已上生產(chǎn),親測有效)

    spring項(xiàng)目如何配置多數(shù)據(jù)源(已上生產(chǎn),親測有效)

    這篇文章主要介紹了spring項(xiàng)目如何配置多數(shù)據(jù)源(已上生產(chǎn),親測有效),具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12

最新評論

铜川市| 北宁市| 凤山县| 米脂县| 鹤岗市| 尤溪县| 徐汇区| 福建省| 松桃| 二连浩特市| 银川市| 原平市| 渭源县| 九龙县| 晋城| 平安县| 济南市| 贵州省| 论坛| 旺苍县| 马山县| 白水县| 故城县| 闸北区| 逊克县| 汝州市| 勃利县| 西乌珠穆沁旗| 西乌珠穆沁旗| 双峰县| 郎溪县| 富阳市| 甘谷县| 辽宁省| 玉龙| 合川市| 含山县| 丰顺县| 璧山县| 建瓯市| 延安市|