八十四、堆排序解決TopK問題

2021-03-02 Python之王

「@Author:Runsen」

上次介紹了堆排序,這次介紹堆排序常見的應用場景TopK問題。

利用堆求TopK問題

TopK問題是一個堆排序典型的應用場景。

題目是這樣的:假設,我們想在大量的數據,如 100 億個整型數據中,找到值最大的 K 個元素,K 小於 10000。對此,你會怎麼做呢?

對標的是Leetcode第215題:「數組中的第K個最大元素。」

具體連結:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/

在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。

示例 1:

輸入: [3,2,1,5,6,4] 和 k = 2
輸出: 5
示例 2:

輸入: [3,2,3,1,2,4,5,5,6] 和 k = 4
輸出: 4

經典的TopK問題還有:最大(小) K 個數、前 K 個高頻元素、第 K 個最大(小)元素

對此TopK問題本質上是一個排序問題,排序算法一共有十個,這個還有很多排序算法沒有介紹過。

至於為什麼TopK問題最佳的答案是堆排序?其實在空間和時間的複雜度來考量,雖說快排是最好的排序算法,但是對於100億個元素從大到小排序,然後輸出前 K 個元素值。

可是,無論我們掌握的是快速排序算法還是堆排序算法,在排序的時候,都需要將全部的元素讀入到內存中。也就是說,100億個整型元素大約需要佔用40GB的內存空間,這聽起來就不像是普通民用電腦能幹的事情,(一般的民用電腦內存比這個小,比如我寫文章用的電腦內存是 32GB)。

眾所周知,快速排序和堆排序的時間複雜度都可以達到

但是快速排序是新建數組,空間複雜度是

如果使用heapq內置模塊,尋找數組中的第K個最大元素就是一行代碼,heapq中的nlargest接口封裝好了,返回的是一個數組,需要切片取值。

import heapq
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        return heapq.nlargest(k,nums)[-1]

當然,一般都是手寫堆排序,尋找數組中的第K個最大元素建立最小堆,尋找數組中的第K個最小元素建立最大堆,

思路:「取nums前K個元素建立大小為K的最小堆,後面就是維護一個容量為k的小頂堆,堆中的k個節點代表著當前最大的k個元素,而堆頂顯然是這k個元素中的最小值。」

因此只要遍歷整個數組,當二叉堆大小等於K後,當遇見大於堆頂數值的元素時彈出堆頂,並壓入該元素,持續維護最大的K個元素。遍歷結束後,堆頂元素即為第K個最大元素。時間複雜度

class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        heapsize=len(nums)
        def maxheap(a,i,length):
            l=2*i+1
            r=2*i+2
            large=i
            if l<length and a[l]>a[large]:
                large=l
            if r<length and a[r]>a[large]:
                large=r
            if large!=i:
                a[large],a[i]=a[i],a[large]
                maxheap(a,large,length)
            
        def buildheap(a,length):
            for i in range(heapsize//2,-1,-1):
                maxheap(a,i,length)

        buildheap(nums,heapsize)
        for i in range(heapsize-1,heapsize-k,-1):
            nums[0],nums[i]=nums[i],nums[0]
            heapsize-=1
            maxheap(nums,0,heapsize)
        return nums[0]  

相反如果是求前k個最小,那麼就用最大堆,因此面對TopK問題,最完美的解法是堆排序。因此,只有你看到數組的第K個……,馬上就是想到堆排序。

如果在數據規模小、對時間複雜度、空間複雜度要求不高的時候,真沒必要上 「高大上」 的算法,寫一個快排就很完美了。

TopK問題就像搜尋引擎每天會接收大量的用戶搜索請求,它會把這些用戶輸入的搜索關鍵詞記錄下來,然後再離線地統計分析,得到最熱門的Top10搜索關鍵詞,啥啥惹事就出來了。

本文已收錄 GitHub,傳送門~[1] ,裡面更有大廠面試完整考點,歡迎 Star。

參考資料[1]

傳送門~: https://github.com/MaoliRUNsen/runsenlearnpy100

相關焦點

  • 快速排序和堆排序
    龜哥今天和大家分享下快速排序,堆排序算法。首先快速排序的思想,給定一個無序的數組,首先用兩個指針left和right分別指向數組的頭部和尾部,將數組的第一個元素array[0]作為和left,right指針比較的元素。
  • 堆排序算法C語言詳解
    在學習堆排序之前,首先需要了解堆的含義:在含有 n 個元素的序列中,如果序列中的元素滿足下面其中一種關係時,此序列可以稱之為堆。
  • 詳解堆排序
    堆是一棵順序存儲的完全二叉樹。其中每個結點的關鍵字都不大於其孩子結點的關鍵字,這樣的堆稱為小根堆。其中每個結點的關鍵字都不小於其孩子結點的關鍵字,這樣的堆稱為大根堆。構造了初始堆後,我們來看一下完整的堆排序處理:還是針對前面提到的無序序列 { 1, 3, 4, 5, 2, 6, 9, 7, 8, 0 } 來加以說明。
  • 圖解:什麼是堆排序?
    堆排序 要學習今天的堆排序(Heap Sort),我們以一個數組  arr = [5、1、4、2、8、4] 開始(這個數組我們之前講排序算法常用的):我們首先以這個數組建立一個大頂堆,插入結點 5 作為根結點
  • 堆排序就這麼簡單
    一、堆排序介紹來源百度百科:堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉樹。前面我已經有二叉樹入門的文章了,當時講解的是二叉查找樹,那上面所說的完全二叉樹是怎麼樣的一種二叉樹呢??
  • 堆排序與拓撲排序(Java模板)
    1.堆排序思想(1) 以升序排序為例,我們需要構建一個小根堆。第一種方法:用數組模擬小根堆。忘記了的朋友可以看看之前的文章《堆模板(Java)》認真複習一下~第二種方法:使用 java.util.PriorityQueue ,相當於一個堆結構。
  • 第二篇,選擇排序算法:簡單選擇排序、堆排序
    此為第二篇,選擇排序算法:簡單選擇排序、堆排序。
  • 堆排序算法
    堆是一種數據結構,一種叫做完全二叉樹的數據結構。堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆。大頂堆:a[i] >=a[2i+1] && ar[i] >= a[2i+2] 小頂堆:a[i] <= a[2i+1] && a[i] <= a[2i+2] 堆排序的基本思想:
  • 動畫 | 什麼是堆排序?
    而堆排序因為二叉堆的性質,堆頂就是最大的元素,查找次數只有一次,但是將無序轉成有序中間還需要一個預處理過程:構造堆有序。 堆有序並不代表數組有序,堆有序是滿足 二叉堆 性質的: 1.父節點的鍵值總是優先於任何一個子節點的鍵值; 2.左右子樹都是一個二叉堆。
  • 堆排序原理及實現
    小白們也很熟悉的:冒泡,歸併,簡單選擇,歸併,堆排序。那它們區別是什麼呢?需要好好梳理一下了。今天咱們就先瞅瞅感覺比較抽象的堆算法吧。什麼是堆?堆是具有下列性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小於或等於其左右子結點的值,稱為小頂堆。
  • 優先級隊列與堆排序
    如下圖,以對S O R T E X A M P L E 排序為例,首先本地構造一個最大堆,即對節點進行Sink操作,使其符合二叉堆的性質。然後再重複刪除根節點,也就是最大的元素,操作方法與之前的二叉堆的刪除元素類似。
  • 到底什麼是堆排序 PHP
    每個節點的值都大於等於子樹中每個節點值的堆叫最大堆,反之為最小堆。堆排序的時間複雜度為O(nlogn)。堆是一種特殊的二叉樹,它滿足以下兩點:1. 堆是一個完全二叉樹2. 堆中每個節點都必須大於等於(或小於等於) 其子樹的每個節點
  • 一文讀懂堆排序
    (點擊上方公眾號,可快速關注)轉自:劉毅https://61mon.com/index.php/archives/202/好文投稿, 請點擊 → 這裡了解詳情堆排序是利用堆的性質進行的一種選擇排序下面先討論一下堆。堆實際上是一棵完全二叉樹,其滿足性質:任何一結點大於等於或者小於等於其左右子樹結點。堆分為大頂堆和小頂堆,滿足 「任何一結點大於等於其左右子樹結點」 的稱為大頂堆,滿足 「任何一結點小於等於其左右子樹結點」 的稱為小頂堆。由上述性質可知:大頂堆的堆頂肯定是最大的,小頂堆的堆頂是最小的。
  • ...選擇,插入,希爾,歸併,快排,堆排序,計數排序,桶排序,基數排序)
    最佳情況:O(nlogn)最壞情況:O(n*2)平均情況:O(nlogn)八、堆排序(heap sort)一種利用堆概念來排序的選擇排序,是一種不穩定的排序。思路:利用堆這種數據結構設計的排序算法。堆積是一個近似完全的二叉樹的結構,並且擁有堆積的性質:子節點的鍵值或者索引總小於(大於)它的父節點。描述:(1)將初始待排序序列(r1,r2,r3,...
  • 排序算法問題:穩定排序與不穩定排序
    正文這幾天筆試了好幾次了,連續碰到一個關於常見排序算法穩定性判別的問題,往往還是多選,對於我以及和我一樣拿不準的同學可不是一個能輕易下結論的題目,當然如果你筆試之前已經記住了數據結構書上哪些是穩定的,哪些不是穩定的,做起來應該可以輕鬆搞定。本文是針對老是記不住這個或者想真正明白到底為什麼是穩定或者不穩定的人準備的。
  • 用這種解題方法,我解決了大部分的 leetcode 貪心算法題
    「這是本人第40篇原創博文,每周兩篇更新,謝謝賞臉閱讀文章今天介紹一種解決常規的貪心策略或者字典排序的題目的通用解題方法。第一題,leetcode中等難度題目先來一道簡單的字典序排列的問題,這個題目我這裡不會用最優解來解決這個問題,這個是leetcode的中等難度的題目,最優解還是需要再思考一下的,這道題目作為文章開頭只是為了介紹我想要介紹的貪心的解題的一種思路而已,大佬請勿噴!!
  • 使用vlookup解決自定義排序的問題,原來自定義排序竟如此簡單
    Hello,大家好,今天跟大家分享下如何自定義排序,實現想怎麼排序就怎麼排序,工作中我們可能會遇到這樣的問題,就是要根據給定的數據位置進行排序,如果我們直接使用排序excel會根據默認的排序規則進行排序,而不能達到我們想要的結果,解決這樣的問題,跟大家分享2種方法,一種是使用自定義排序
  • 漫畫:美團面試題(TOPK:求第K個最大的元素)
    第215題:在未排序的數組中找到第 k 個最大的元素。請注意,你需要找的是數組排序後的第 k 個最大的元素,而不是第 k 個不同的元素。TopK 問題 (尤其是大數據處理)優先隊列利用堆求中位數這種題目,從個人來講,我一般是比較偏好使用堆來做的。
  • 徹底搞懂穩定排序與不穩定排序
    article知音專欄關於資源視頻下載的說明常用設計模式完整系列篇【強化編程功底】算法文摘這幾天筆試了好幾次了,連續碰到一個關於常見排序算法穩定性判別的問題i + 1節點,大頂堆要求父節點大於等於其2個子節點,小頂堆要求父節點小於等於其2個子節點。
  • 你所知道的十大排序算法的總結(冒泡,選擇,插入,希爾,歸併,快排,堆...
    最佳情況:O(nlogn)最壞情況:O(n*2)平均情況:O(nlogn)八、堆排序(heap sort)一種利用堆概念來排序的選擇排序,是一種不穩定的排序。思路:利用堆這種數據結構設計的排序算法。堆積是一個近似完全的二叉樹的結構,並且擁有堆積的性質:子節點的鍵值或者索引總小於(大於)它的父節點。