數據結構-如果將堆結構應用到TOP-K問題上會怎樣?

數據結構的應用-如何用堆解決TOP-K問題

  • 前言
  • 一、TOP-K問題是什么?
  • 二、如何用堆解決TOP-K問題
    • 1.怎么建堆,建大堆還是小堆?
    • 2.代碼實現
  • 總結


前言

本篇文章進行如何用堆結構解決TOP-K問題的講解


一、TOP-K問題是什么?

TOP-k問題:即求一些數據中的前k個最大的數字或最小的數字,并且一般這些數據規模都很大

生活中也有許多常見的topks問題,比如在打游戲時的全服前幾名玩家,世界五百強,專業前十名等等,這些都屬于TOP-K問題

而當數據規模很大的時候,想要找出前k個最大值或最小值,普通排序是難以滿足要求的,因為時間復雜度太高,比如冒泡排序,這就讓我們聯想到了一種排序方式——堆排序

因為我們想要找到前k個最大值或最小值,必然離不開排序,并且當數據規模極大的時候,我們不可能全部等待它加載出來,這時候我們常常借助堆這個結構來解決

舉個例子,當我們需要找出100億個整數的前k個最大值的時候,這100億個整數我們不可能全部申請到內存中,我們計算一下,100億個整數,相當于400億個字節

1G = 1024MB = 1024 * 1024KB = 1024 * 1024 * 1024Byte,1個G,大約是10億個字節,那么400億個字節,就相當于40G的內存,注意,這是臨時申請的內存,是運行內存,我們想想,一個普通的筆記本電腦運行內存才有多少,這一下子就占據了40G,除去電腦本身的操作系統等也需要使用占據的運行內存,我們的電腦內存能承受得住嗎,而且我們申請完空間之后,還要借助cpu進行排序等操作,換句話說,cpu內心可能在哀嚎:皇上,臣妾做不到啊!

而這時候,就輪到了我們的同學出場了

二、如何用堆解決TOP-K問題

1.怎么建堆,建大堆還是小堆?

當我們想要取N個數據的前k個最大元素時,我們要建小堆

當取前k個最小元素時,我們要建大堆

可能有些同學會想到上一篇剛剛講到的堆排序問題,堆排序,升序建大堆,降序建小堆,為什么到TOP-K問題就變了呢?

因為我們要解決的TOP-K問題,通常數據規模較大,并且它只要求我們找到前k個最大數據,并不要求對數據整體進行排序,因此我們并不一定要按照堆排序的方式進行,只需要進行類似于“排序”的操作,找出前k個最大值即可,對整體數據進行排序反而會讓問題變得更加復雜,有些冗余

如果我們按照堆排序的想法進行,在N個數據中找到前k個最大元素,那么就要建N個大堆,然后再pop(刪除)K次最后得到前k個最大元素,在這個過程中,整個數據的順序也排成了升序,這未免有些冗雜,因此我們要采取另一種方式

我們以N個數據中取前K個最大元素為例,此時我們要建小堆,并且只用數據中的前k個元素進行建堆,當我們對數據中前k個元素建小堆之后,堆頂的元素則是前k個元素中最小的那一個元素剩下的N - K個元素逐個與堆頂元素進行比較如果剩下的元素比堆頂元素大的話,就將堆頂元素進行替換替換為更大的那個元素替換后將前k個數據建成的小堆進行向下調整,調整之后繼續將堆頂元素和后面的元素進行比較,重復進行

這種方式會使得最開始的前k個元素中的最小值“沉底”,而替換上來的是一個大一點的元素,向下調整之后,堆頂的數據又是前k個元素中最小的那一個,再與剩下的元素進行比較,這樣我們就不必擔心大的數據會遺漏,因為我們建的是小堆,小堆的堆頂元素必然是最小的,大一些的元素都是堆頂元素的子結點,不必擔心小的數據會進入導致出錯

有的同學可能會問,既然這樣,建大堆不行嗎?

答案是不行!

如果我們以這種方式進行并且建大堆的話,那么堆頂元素存儲的則是前k中最大的元素,我們無法確定堆頂元素的子結點與剩下的N - K個結點之間的大小,可能會造成大的元素無法進入,比如堆頂是100,子結點中有的數據是5、10、15等等,剩下的N-K個元素中如果有78,那么判定78 < 100,不替換,78就無法進入前k個元素建成的小堆,但是實際上78是大于5、10、15這些數的,這就會導致出錯,甚至可能會導致前k中只有最大的元素和最小的k - 1個元素,因此當我們取前k個最大元素時,建大堆并不可取。

建大堆適合于取前k個最小元素時,這時候小于堆頂元素的就進行替換,替換之后向下調整,再重復,最后前k個元素自然就是最小的前k個元素

并且當我們如此操作時,我們只需要維護建堆的那k個空間即可,因為我們進行的是替換操作,不符合條件的堆頂元素直接就被替換,被丟棄掉,不需要再維護了,而且我們這種方式,不必擔心被丟棄的元素會比后面的元素大,成為前k個最大元素之一,因為它既然是堆頂元素,必然是前k個元素建成的小堆中的最小的一個元素,被替換后,替換上的數據必然會比它要大,而堆中剩下的k -1 個元素也必然大于它,因此替換后的前k個元素都是比被替換的元素要大的,后面如果繼續替換,說明它一定大于之前被替換的元素,就像b>a,a被替換,后面再比較,c > b,那么b被替換,此時c一定是大于a的,之后再進行重復比較、替換、調整,不會出現錯誤

2.代碼實現

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//topk問題,找出最大的k個數,那么就要建小堆
void Swap(int* a, int* b)
{int tmp;tmp = *a;*a = *b;*b = tmp;
}
void AdjustDown(int*a ,int n,int parent)
{int child = parent * 2 + 1;while (child < n){if (child + 1 < n && a[child + 1] < a[child]){child += 1;}if (a[parent] > a[child]){Swap(&a[parent], &a[child]);parent = child;child = parent * 2 + 1;}else{break;}}
}
void Topk(int* a,int n,int k)
{assert(k > 0 && k <= n);for (int i = (k - 1 - 1) / 2; i >= 0; i--){AdjustDown(a, k, i);}for (int i = k; i < n; i++){if (a[i] > a[0]){Swap(&a[0], &a[i]);AdjustDown(a, k, 0);}}for (int i = 0; i < k; i++){printf("%d ", a[i]);}
}
int main()
{int n;int k;printf("請輸入你要輸入數字的個數:");scanf_s("%d", &n);printf("請輸入你要查找的前k個最大數的個數k");scanf_s("%d", &k);int* a = (int*)malloc(sizeof(int) * n);if (a == NULL){perror("malloc fail");return 0;}printf("請輸入n個數字");for (int i = 0; i < n; i++){scanf_s("%d", &a[i]);}Topk(a, n, k);free(a);a = NULL;return 0;
}

與堆排序類似,我們依舊是先進行向下調整模擬建堆(為什么采用向下調整模擬建堆詳見上篇文章),之后進行比較、交換、調整,重復操作,當然,當數據規模過大的時候,我們一般不采取圖中的主函數中依次輸入后讀取數據的方式,多采用造文件,從文件中讀取數據的方式進行測試或者其他操作,具體詳見文件操作篇

總結

本篇講解了如何使用堆結構去解決TOP-K問題,關于TOP-K還有許多解法,比如隨機選擇等,后續會繼續發布,敬請關注!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908989.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908989.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908989.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Elasticsearch的索引

正向索引和倒排索引 什么是正向索引&#xff1f; 傳統的數據庫采用正向索引&#xff0c;如MySQL將表中的id創建索引&#xff0c;正向索引在進行不是id為索引進行搜索的時候&#xff0c;會逐條進行查詢&#xff0c;比方說 上圖的表格&#xff0c;數據庫進行逐條查詢&#xff0c;…

分散電站,集中掌控,安科瑞光伏云平臺助力企業綠色轉型

本項目位于香港全境共計52個分布式光伏站&#xff0c;總裝機容量8.6MW。發電模式自發自用&#xff0c;余電上網&#xff0c;逆變器采用陽光電源SG100CX、SG20RT等12種型號共計103臺&#xff0c;其余型號共計15臺。每個站點均配置氣象站。 項目采用AcrelCloud-1200分布式光伏運…

開發記錄:修復一些Bug,并實現兩個功能

開發記錄&#xff1a; &#x1f4cb; 工作概述 到今天主要完成了AI閱讀助手的兩大核心功能&#xff1a;前情提要和名詞解釋&#xff0c;并對相關交互體驗進行了優化。通過流式SSE技術實現了實時AI內容生成&#xff0c;大幅提升了用戶體驗。 &#x1f3af; 主要完成功能 1…

LLM基礎1_語言模型如何處理文本

基于GitHub項目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介紹 tiktoken&#xff1a;OpenAI開發的專業"分詞器" torch&#xff1a;Facebook開發的強力計算引擎&#xff0c;相當于超級計算器 理解詞嵌入&#xff1a;給詞語畫"…

【HarmonyOS 5.0】開發實戰:從UI到Native全解析

一、環境搭建與項目創建 ??跨平臺安裝?? DevEco Studio支持Windows/macOS系統&#xff0c;安裝包集成HarmonyOS SDK、Node.js和OHPM工具鏈。 Windows&#xff1a;雙擊.exe選擇非中文路徑macOS&#xff1a;拖拽.app至Applications目錄驗證&#xff1a;通過Help > Diagnos…

零知開源——STM32F103RBT6驅動 ICM20948 九軸傳感器及 vofa + 上位機可視化教程

STM32F1 本教程使用零知標準板&#xff08;STM32F103RBT6&#xff09;通過I2C驅動ICM20948九軸傳感器&#xff0c;實現姿態解算&#xff0c;并通過串口將數據實時發送至VOFA上位機進行3D可視化。代碼基于開源庫修改優化&#xff0c;適合嵌入式及物聯網開發者。在基礎驅動上新增…

華為OD最新機試真題-食堂供餐-OD統一考試(B卷)

題目描述 某公司員工食堂以盒飯方式供餐。 為將員工取餐排隊時間降低為0,食堂的供餐速度必須要足夠快,現在需要根據以往員工取餐的統計信息,計算出一個剛好能達成排隊時間為0的最低供餐速度。即,食堂在每個單位時間內必須至少做出 多少價盒飯才能滿足要求。 輸入描述 第1行…

【筆記】MSYS2 的 MINGW64 環境 全面工具鏈

#工作記錄 MSYS2 的 MINGW64 環境&#xff08;mingw64.exe&#xff09;&#xff0c;下面是為該環境準備的最全工具鏈安裝命令&#xff08;包括 C/C、Python、pip/wheel、GTK3/GTK4、PyGObject、Cairo、SDL2 等&#xff09;。 這一環境適用于構建原生 64 位 Windows 應用程序。…

基于 HTTP 的單向流式通信協議SSE詳解

SSE&#xff08;Server-Sent Events&#xff09;詳解 &#x1f9e0; 什么是 SSE&#xff1f; SSE&#xff08;Server-Sent Events&#xff09; 是 HTML5 標準中定義的一種通信機制&#xff0c;它允許服務器主動將事件推送給客戶端&#xff08;瀏覽器&#xff09;。與傳統的 H…

【react+antd+vite】優雅的引入svg和阿里巴巴圖標

1.安裝相關包 由于是vite項目&#xff0c;要安裝插件來幫助svg文件引入進來&#xff0c;否則會失敗 npm下載包 npm i vite-plugin-svgr vite.config.ts文件內&#xff1a; import svgr from "vite-plugin-svgr"; //... export default defineConfig({plugins: …

UI框架-通知組件

UI框架-通知組件 介紹 一個基于 Vue 3 的輕量級通知組件庫&#xff0c;提供了豐富的消息通知功能。支持多種通知類型、自定義樣式、進度條顯示等特性。 特性 &#x1f3a8; 支持多種通知類型&#xff1a;信息、成功、警告、錯誤? 支持進度條顯示&#x1f504; 支持加載中狀…

WordZero:讓Markdown與Word文檔自由轉換的Golang利器

在日常工作中&#xff0c;我們經常需要在Markdown和Word文檔之間進行轉換。Markdown方便編寫和版本控制&#xff0c;而Word文檔更適合正式的商務環境。作為一名Golang開發者&#xff0c;我開發了WordZero這個庫&#xff0c;專門解決這個痛點。 項目背景 GitHub倉庫&#xff1…

計算機網絡面試匯總(完整版)

基礎 1.說下計算機網絡體系結構 計算機網絡體系結構&#xff0c;一般有三種&#xff1a;OSI 七層模型、TCP/IP 四層模型、五層結構。 簡單說&#xff0c;OSI是一個理論上的網絡通信模型&#xff0c;TCP/IP是實際上的網絡通信模型&#xff0c;五層結構就是為了介紹網絡原理而折…

動端React表格組件:支持合并

前言 在移動端開發中&#xff0c;表格組件是一個常見但復雜的需求。相比PC端&#xff0c;移動端表格面臨著屏幕空間有限、交互方式不同、性能要求更高等挑戰。本文將詳細介紹如何從零開始構建一個功能完整的移動端React表格組件&#xff0c;包含固定列、智能單元格合并、排序等…

廣告系統中后鏈路數據為什么要使用流批一體技術?流批一體技術是什么?

在大規模廣告系統的后鏈路(離線和實時特征計算、模型訓練與上線、效果監控等)中,往往既有對海量歷史數據的批量計算需求(離線特征、離線模型訓練、報表匯總),又有對在線請求的低延遲實時計算需求(實時特征、在線打分、實時監控/告警)。傳統將二者割裂、用 Lambda 架構…

6.10 - 常用 SQL 語句以及知識點

MySQL 技術 SQL 是結構化查詢語言&#xff0c;他是關系型數據庫的通用語言 SQL 可以分為分為以下三個類別 DDL (data definition languages) 語句 數據定義語言&#xff0c;定義了 不同的數據庫、表、索引等數據庫對象的定義。常用的的語句關鍵字包括 **create、drop、alter …

OpenCV CUDA 模塊光流計算------稀疏光流算法類SparsePyrLKOpticalFlow

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 OpenCV CUDA 模塊中實現的稀疏光流算法類&#xff0c;基于 Lucas-Kanade 方法&#xff0c;并支持圖像金字塔結構。適用于特征點跟蹤任務&#xf…

免費工具-微軟Bing Video Creator

目錄 引言 一、揭秘Bing Video Creator 二、輕松上手&#xff1a;三步玩轉Bing Video Creator 2.1 獲取與訪問&#xff1a; 2.2 創作流程&#xff1a; 2.3 提示詞撰寫技巧——釋放AI的想象力&#xff1a; 三、核心特性詳解&#xff1a;靈活滿足多樣化需求 3.1 雙重使用模…

MySQL技術內幕1:內容介紹+MySQL編譯使用介紹

文章目錄 1.整體內容介紹2.下載編譯流程2.1 安裝編譯工具和依賴庫2.2 下載編譯 3.配置MySQL3.1 數據庫初始化3.2 編輯配置文件3.3 啟動停止MySQL3.4 登錄并修改密碼 1.整體內容介紹 MySQL技術系列文章將從MySQL下載編譯&#xff0c;使用到MySQL各組件使用原理源碼分析&#xf…

MySQL 事務詳解

MySQL 事務詳解 一、事務是什么&#xff1f;為什么需要事務&#xff1f; 二、事務的四大特性&#xff08;ACID&#xff09;舉例說明&#xff1a;轉賬操作 三、MySQL 中事務的支持四、事務分類&#xff1a;隱式 vs 顯式1. 隱式事務&#xff08;自動提交&#xff09;2. 顯式事務&…