【數據結構】用堆解決TOPK問題

設計一個算法,找出數組中最小的k個數。以任意順序返回這k個數均可。

示例:

輸入: arr = [1,3,5,7,2,4,6,8], k = 4 輸出: [1,2,3,4]

比較替換堆頂的數時,不需要讓堆頂與數組的每一個數再進行比較,比較數組減去k個數之后剩下的數就行。

1.堆排序方法

// 選出最小的K個數,要建一個大堆// 大堆的向下調整算法
void HeapDown(int* heap, int k, int parent) {// 1.根據父親結點得到左孩子節點下標childint child = parent * 2 + 1;// 2.進入while循環進行向下調整(while的循環條件是child<k)while (child < k) {// 2.1比較左孩子和右孩子的值,如果右孩子更大,child++,注意要避免越界child+1<kif (child + 1 < k && heap[child] < heap[child + 1]) {child++;}// 2.2如果父親結點的值小于孩子結點,進行交換,交換后將child結點的下標賦值給父親結點,//    根據此時的父親結點計算下一個child的下標if (heap[parent] < heap[child]) {int temp = heap[parent];heap[parent] = heap[child];heap[child] = temp;parent = child;child = parent * 2 + 1;}// 2.3如果父親結點的值已經大于孩子結點,說明不需要再調整,break跳出循環elsebreak;}
}int* smallestK(int* arr, int arrSize, int k, int* returnSize) {// 0.首先處理兩種特殊情況K<=0,K>=arrSzieif (k <= 0) {*returnSize = 0;return NULL;}if (k >= arrSize) {int* result = (int*)malloc(sizeof(int) * arrSize);memcpy(result, arr, sizeof(int) * arrSize);*returnSize = arrSize;return result;}// 1.首先開辟一個空間存我們新建的大堆數據int* heap = (int*)malloc(sizeof(int) * arrSize);// 2.將傳入的數組的數據memcpy到我們新建的空間memcpy(heap, arr, sizeof(int) * arrSize);// 3.從下往上進行向下調整構建大堆for (int i = (arrSize - 1 - 1) / 2; i >= 0; i--) {HeapDown(heap, arrSize, i);}int end = arrSize - 1;while (end > 0) {int tem = heap[0];heap[0] = heap[end];heap[end] = tem;end--;HeapDown(heap, end, 0);}int* arrtemp = (int*)malloc(sizeof(int) * k);memcpy(arrtemp, heap, sizeof(int) * k);*returnSize = k;return arrtemp;
}
  1. 邊界條件的嚴謹性
    代碼開頭對k的特殊情況(k<=0k>=數組長度)做了單獨處理:

    • k<=0時,返回空數組并設置returnSize=0
    • k大于等于數組長度時,直接返回原數組。
      這種處理避免了后續無效的堆操作,也防止了數組訪問越界,體現了對 “異常輸入” 的考慮,是健壯性代碼的常見做法。
  2. 堆操作的標準化實現

    • 向下調整算法(HeapDown):這是堆操作的核心,代碼正確實現了大堆的向下調整邏輯:
      ① 先比較左右孩子,選擇更大的子節點(保證大堆特性);
      ② 若父節點小于子節點,則交換兩者,并繼續向下調整;
      ③ 若父節點已大于子節點,說明堆已平衡,直接退出。
      其中對child+1 < k的越界檢查(避免右孩子不存在時的訪問錯誤),體現了細節處理的嚴謹性。

    • 堆的構建方式:從最后一個非葉子節點((arrSize-1-1)/2)開始,向上依次調用HeapDown,這是構建堆的標準高效方法(時間復雜度O(n)),比從根節點逐個插入(O(n log n))更優。

  3. 接口設計的規范性
    函數通過returnSize參數返回結果數組的長度,符合 C 語言中 “動態數組需明確長度” 的設計習慣(調用者可通過returnSize正確遍歷結果),避免了因長度未知導致的越界訪問。

最優解:

// 選出最小的K個數,要建一個大堆// 大堆的向下調整算法
void HeapDown(int* heap, int k, int parent) {// 1.根據父親結點得到左孩子節點下標childint child = parent * 2 + 1;// 2.進入while循環進行向下調整(while的循環條件是child<k)while (child < k) {// 2.1比較左孩子和右孩子的值,如果右孩子更大,child++,注意要避免越界child+1<kif (child + 1 < k && heap[child] < heap[child + 1]) {child++;}// 2.2如果父親結點的值小于孩子結點,進行交換,交換后將child結點的下標賦值給父親結點,//    根據此時的父親結點計算下一個child的下標if (heap[parent] < heap[child]) {int temp = heap[parent];heap[parent] = heap[child];heap[child] = temp;parent = child;child = parent * 2 + 1;}// 2.3如果父親結點的值已經大于孩子結點,說明不需要再調整,break跳出循環elsebreak;}
}int* smallestK(int* arr, int arrSize, int k, int* returnSize) {// 0.首先處理兩種特殊情況K<=0,K>=arrSzieif (k <= 0) {*returnSize = 0;return NULL;}if (k >= arrSize) {int* result = (int*)malloc(sizeof(int) * arrSize);memcpy(result, arr, sizeof(int) * arrSize);*returnSize = arrSize;return result;}// 1.首先開辟一個空間存我們新建的大堆數據int* heap = (int*)malloc(sizeof(int) * k);// 2.將傳入的數組的數據memcpy到我們新建的空間memcpy(heap, arr, sizeof(int) * k);// 3.從下往上進行向下調整構建大堆for (int i = (k - 1 - 1) / 2; i >= 0; i--) {HeapDown(heap, k, i);}// 4.將新建大堆的堆頂值與傳入數組剩下的arrSize-k個數據進行比較,如果堆頂的數更大,替換堆頂的數并進行向下調整//j從k開始即可for (int j = k; j < arrSize; j++) {if (heap[0] > arr[j]) {//直接替換即可heap[0]=arr[j];HeapDown(heap, k, 0);}}*returnSize = k;return heap;
}
  1. 大堆的精準應用
    選出 “最小的 K 個數” 時,使用 “大堆” 是非常巧妙的選擇:

    • 大堆的堆頂始終是當前 K 個元素中的最大值,便于快速判斷新元素是否有資格進入 “最小 K 個” 的集合(只需比較新元素與堆頂)。
    • 若新元素更小,則替換堆頂并調整,保證堆中始終是當前最小的 K 個元素。
      這種思路體現了 “用合適的數據結構解決特定問題” 的設計思想。
  2. 堆構建的高效實現

    • 構建堆時,從最后一個非葉子節點((k-1-1)/2)開始向上調用HeapDown,這是堆初始化的標準高效方法(時間復雜度O(k)),而非從根節點逐個插入(O(k log k))。
    • HeapDown函數的實現嚴謹:先比較左右孩子取較大值,再與父節點比較交換,確保大堆特性,且對右孩子的越界檢查(child+1 < k)避免了數組訪問錯誤。
  3. 內存使用的合理性

    • 堆空間直接分配為k大小(malloc(sizeof(int)*k)),精準匹配需求,不浪費內存。
    • 最終直接返回堆空間作為結果,避免了額外的內存拷貝(上一版中arrtemp的拷貝操作)。

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

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

相關文章

【深度長文】Anthropic發布Prompt Engineering全新指南

目錄 1.什么時候適合用提示工程? 2.如何進行提示工程 2.1 使用提示模板 2.1.1 使用提示模板和變量 2.1.2 何時使用提示模板和變量 2.1.3 提示模板示例 2.2 保持清晰和直接 2.2.1 如何保持清晰、具有上下文和具體 2.2.2 示例 ?2.3 使用示例&#xff08;多示例提示…

【基礎-判斷】HarmonyOS提供了基礎的應用加固安全能力,包括混淆、加密和代碼簽名能力

正確 解釋如下: 應用加固: 這是指對應用程序進行保護,使其更難被逆向工程、篡改或盜版。HarmonyOS 作為現代操作系統,確實提供了這樣的基礎安全能力。 混淆: HarmonyOS 的 SDK 提供了代碼混淆工具(通常基于 ProGuard 或類似技術)。開發者在構建應用時啟用混淆,可以將類…

HTML 框架:構建網頁布局的基石

HTML 框架&#xff1a;構建網頁布局的基石 引言 HTML 框架是網頁設計中不可或缺的一部分&#xff0c;它為網頁內容的布局提供了強大的支持。本文將深入探討 HTML 框架的概念、種類、應用以及如何有效地使用它們來構建網頁布局。 什么是 HTML 框架&#xff1f; HTML 框架是一種網…

[Linux]學習筆記系列 -- [mm][memblock]

文章目錄mm/memblock.c: Linux內核的“拓荒時代”內存管理器一、 核心問題&#xff1a;為什么需要 memblock&#xff1f;二、 核心原理與設計三、 在內核啟動流程中的角色四、 關鍵 API五、 總結include/linux/memblock.hmm/memblock.cmemblock_reserve 預留內存塊for_each_mem…

Java 面試八股文匯總(1000 道附答案解析)

在過 2 個月即將進入金九銀十了&#xff0c;然而面對今年的大環境而言&#xff0c;跳槽成功的難度比往年高了很多&#xff0c;很明顯的感受就是&#xff1a;對于今年的 java 開發朋友跳槽面試&#xff0c;無論一面還是二面&#xff0c;都開始考驗一個 Java 程序員的技術功底和基…

給純小白的Python操作 PDF 筆記

一、文件基礎打開與關閉 推薦用 with open(path, mode, encodingutf-8) as f:&#xff0c;自動完成 close()&#xff0c;避免泄露文件句柄。常見模式&#xff1a;r 讀&#xff0c;w 寫覆蓋&#xff0c;a 追加&#xff0c;rb/wb 二進制。Windows 默認編碼為 GBK&#xff0c;Linu…

vue使用vue-cropper實現圖片裁剪之單圖裁剪

vue制作的pc系統中(如若依系統)&#xff0c;需要實現按照固定尺寸進行裁剪后再進行圖片上傳&#xff0c;以下代碼講述的是實現單張圖片裁剪上傳。1.第一步需要安裝vue-croppernpm install vue-cropper2.第二步在需要的頁面進入代碼引入import {VueCropper} from "vue-crop…

后臺管理系統-5-vue3之子路由渲染首頁及卡片容器和表格容器實現

文章目錄 1 子路由的實現 1.1 router/index.js 1.2 views/Home.vue(首頁) 1.3 Main.vue 2 左上方的卡片 2.1 分欄間隔(Layout布局) 2.2 卡片容器(el-card) 2.3 整體代碼Home.vue 3 左下方的table(靜態實現) 3.1 準備數據 3.2 渲染表格(el-table) 3.3 整體代碼Home.vue 4 附錄 子…

在CentOS系統中查詢已刪除但仍占用磁盤空間的文件

在CentOS系統中查詢已刪除但仍占用磁盤空間的文件在CentOS系統中查詢已刪除但仍占用磁盤空間的文件1. 檢查磁盤整體使用情況2. 查找被刪除但仍被進程占用的文件3. 釋放磁盤空間4. 替代方案&#xff08;不終止進程&#xff09;注意事項補充工具在CentOS系統中查詢已刪除但仍占用…

正點原子【第四期】Linux之驅動開發學習筆記-1.1 Linux驅動開發與裸機開發的區別

前言&#xff1a; 本文是根據嗶哩嗶哩網站上“正點原子【第四期】手把手教你學Linux系列課程之 Linux驅動開發篇”視頻的學習筆記&#xff0c;該課程配套開發板為正點原子alpha/mini Linux開發板。在這里會記錄下正點原子 I.MX6ULL 開發板的配套視頻教程所作的實驗和學習筆記內…

Android SystemServer 中 Service 的創建和啟動方式

今天導師給我將講了一些如何新建一個系統服務&#xff0c;以及如何去初始化。 Android SystemServer 中 Service 的創建和啟動方式 在 Android 系統中&#xff0c;SystemServer 是系統服務的核心進程&#xff0c;負責啟動和管理各種系統服務。以下是 SystemServer 中服務創建和…

SQL SERVER中位數

有11家門店數據&#xff0c;要求每天所有門店的各個指標的中位數1.第一種做法&#xff0c;使用PERCENTILE_CONT&#xff08;&#xff09; 函數 SQL SERVER 2012 版本及以上PERCENTILE_CONT 函數簡介PERCENTILE_CONT 是 SQL 中的窗口函數&#xff0c;用于計算連續百分位數&#…

【java中springboot引入geotool】

學習目標&#xff1a; 在Spring Boot項目中引入GeoTools庫&#xff0c;可以按照以下步驟進行&#xff1a;理解GeoTools庫的基本信息和用途 GeoTools是一個開源的Java庫&#xff0c;用于處理地理信息系統&#xff08;GIS&#xff09;數據。它提供了對空間數據的讀取、寫入、查詢…

多項目開發環境:如何使用update-alternatives管理多版本Java JDK?(Windows、Mac、Ubuntu)

如何使用update-alternatives管理多版本Java JDK&#xff1f;&#xff08;Windows、Mac、Ubuntu&#xff09; &#x1f4d6; 摘要 在實際開發中&#xff0c;往往會遇到既要維護老項目又要跟進新特性的場景&#xff0c;這就需要在一臺機器上同時安裝并切換多個Java JDK版本。本…

力扣57:插入區間

力扣57:插入區間題目思路代碼題目 給你一個 無重疊的 &#xff0c;按照區間起始端點排序的區間列表 intervals&#xff0c;其中 intervals[i] [starti, endi] 表示第 i 個區間的開始和結束&#xff0c;并且 intervals 按照 starti 升序排列。同樣給定一個區間 newInterval […

KVM虛擬化技術解析:從企業應用到個人創新的開源力量

1 .KVM&#xff1a;開源虛擬化的核心引擎 KVM&#xff08;Kernel-based Virtual Machine&#xff09;作為Linux內核原生集成的開源虛擬化模塊&#xff0c;徹底改變了現代數據中心的虛擬化格局。它通過將Linux內核轉變為Type-1型虛擬機監控器&#xff08;Hypervisor&#xff09;…

28.Linux :通過源代碼編譯安裝lamp

Linux &#xff1a;通過源代碼編譯安裝lamp 區別特性源代碼編譯安裝yum 安裝安裝方式從源代碼編譯構建預編譯的二進制包自定義程度高度可定制有限定制性能優化可針對特定硬件優化通用優化依賴管理手動解決依賴關系自動解決依賴安裝復雜度復雜&#xff0c;需技術經驗簡單&#x…

應用控制技術

一、 應用特征識別技術1.傳統行為檢測技術1.1 五元組檢測原理1.2 配置思路1.3 效果展示需求背景21.4 傳統行為檢測的缺陷無法識別應用層內容&#xff1a;若應用更換端口&#xff08;如QQ改用隨機端口&#xff09;或偽裝協議&#xff08;如HTTPS加密&#xff09;&#xff0c;傳統…

當MySQL的int不夠用了

關于int的長度很多時候看到int(8)這樣的定義&#xff0c;其實這是工具導出的不專業。int是范圍&#xff0c;不是長度。在開發有了共識&#xff08;知道這個長度不算數&#xff0c;要看范圍&#xff09;以后&#xff0c;上來就是所有的類型都是bigint。int的范圍int的取值范圍是…

讓AI學會“邊做邊想“:ReAct的實戰指南

小智的求職困境有個叫小智的AI助手&#xff0c;它剛從"大語言模型大學"畢業&#xff0c;滿懷信心地去應聘一家咨詢公司的智能助理職位。面試官問&#xff1a;"北京和上海哪個城市人口更多&#xff1f;"小智立刻回答&#xff1a;"根據我的知識&#xf…