算法學習筆記:27.堆排序(生日限定版)——從原理到實戰,涵蓋 LeetCode 與考研 408 例題

堆排序(Heap Sort)是一種基于二叉堆數據結構的高效排序算法,由計算機科學家 J. W. J. Williams 于 1964 年提出。它結合了選擇排序的思想和二叉堆的特性,具有時間復雜度穩定(O (nlogn))、原地排序(空間復雜度 O (1)) 等優點,在大規模數據排序場景中應用廣泛。

堆的基本概念與性質 🎂

二叉堆的定義?

二叉堆是一種完全二叉樹(除最后一層外,每層節點均滿,最后一層節點靠左排列),分為兩種類型:?

最大堆:每個父節點的值大于等于其左右子節點的值(parent.val ≥ left.val 且 parent.val ≥ right.val)。?
最小堆:每個父節點的值小于等于其左右子節點的值(parent.val ≤ left.val 且 parent.val ≤ right.val)。? 堆排序中通常使用最大堆,本文以最大堆為例講解。

?

堆的存儲結構 🎂

堆通常用數組實現,利用完全二叉樹的性質映射節點索引(假設數組索引從 0 開始):?

對于節點 i:?
左子節點索引:2i + 1
?右子節點索引:2i + 2?
父節點索引:(i - 1) / 2(整數除法)

在這里插入圖片描述

構建最大堆?

構建最大堆需從最后一個非葉子節點開始,依次向前執行堆調整操作。最后一個非葉子節點的索引為 (n / 2) - 1(n 為數組長度)。

構建過程代碼:

private void buildMaxHeap(int[] arr) {int n = arr.length;// 從最后一個非葉子節點開始調整for (int i = (n / 2) - 1; i >= 0; i--) {maxHeapify(arr, n, i);}
}

堆排序完整實現? 🎂

算法流程?

調用 buildMaxHeap 將數組轉為最大堆。?
初始化堆大小 heapSize = n。?
循環 n - 1 次:?
交換堆頂(arr[0])與堆尾(arr[heapSize - 1])元素。?
減小堆大小(heapSize–)。?
調用 maxHeapify 調整堆頂元素,維護剩余元素的堆性質。

堆排序圖示

堆排序圖示

程序代碼:

public class HeapSort {public void sort(int[] arr) {int n = arr.length;if (n <= 1) return;// 步驟1:構建最大堆buildMaxHeap(arr);// 步驟2:排序階段int heapSize = n;for (int i = n - 1; i > 0; i--) {// 交換堆頂與當前堆尾swap(arr, 0, i);heapSize--; // 堆大小減1// 調整剩余元素為最大堆maxHeapify(arr, heapSize, 0);}}private void buildMaxHeap(int[] arr) {int n = arr.length;for (int i = (n / 2) - 1; i >= 0; i--) {maxHeapify(arr, n, i);}}private void maxHeapify(int[] arr, int heapSize, int i) {int left = 2 * i + 1;int right = 2 * i + 2;int maxIndex = i;if (left < heapSize && arr[left] > arr[maxIndex]) {maxIndex = left;}if (right < heapSize && arr[right] > arr[maxIndex]) {maxIndex = right;}if (maxIndex != i) {swap(arr, i, maxIndex);maxHeapify(arr, heapSize, maxIndex);}}private void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}public static void main(String[] args) {int[] arr = {4, 1, 3, 2, 16, 9, 10, 14, 8, 7};HeapSort heapSort = new HeapSort();heapSort.sort(arr);System.out.println(Arrays.toString(arr)); // 輸出:[1, 2, 3, 4, 7, 8, 9, 10, 14, 16]}
}

復雜度分析?

時間復雜度:?
構建最大堆:O (n)(嚴格證明需用到堆的高度求和,結果為 O (n))。?
排序階段:共執行 n-1 次堆調整,每次調整為 O (logn),總時間 O (nlogn)。?
整體時間復雜度:O (nlogn),且最壞情況下仍為 O (nlogn),穩定性優于快速排序。?
空間復雜度:O (1),原地排序,僅需常數級額外空間。

LeetCode 例題實戰? 🎂

例題 1:912. 排序數組(中等)?

題目描述:給你一個整數數組 nums,請你將該數組升序排列。

?
示例:
輸入:nums = [5,2,3,1]?
輸出:[1,2,3,5]

代碼實現

class Solution {public int[] sortArray(int[] nums) {if (nums == null || nums.length <= 1) {return nums;}heapSort(nums);return nums;}private void heapSort(int[] arr) {int n = arr.length;// 構建最大堆for (int i = (n / 2) - 1; i >= 0; i--) {maxHeapify(arr, n, i);}// 排序階段for (int i = n - 1; i > 0; i--) {swap(arr, 0, i);maxHeapify(arr, i, 0);}}private void maxHeapify(int[] arr, int heapSize, int i) {int left = 2 * i + 1;int right = 2 * i + 2;int maxIndex = i;if (left < heapSize && arr[left] > arr[maxIndex]) {maxIndex = left;}if (right < heapSize && arr[right] > arr[maxIndex]) {maxIndex = right;}if (maxIndex != i) {swap(arr, i, maxIndex);maxHeapify(arr, heapSize, maxIndex);}}private void swap(int[] arr, int i, int j) {int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}
}

例題 2:215. 數組中的第 K 個最大元素(中等)?

題目描述:給定整數數組 nums 和整數 k,請返回數組中第 k 個最大的元素。?

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

解題思路?

利用最大堆的堆頂為最大值的特性,彈出k-1個最大值后,堆頂即為第 k 個最大元素。或使用最小堆(更高效),維護大小為 k 的最小堆,堆頂為第 k 個最大元素。
方法 2(最小堆)Java 代碼

class Solution {public int findKthLargest(int[] nums, int k) {// 最小堆,堆頂為第k大元素PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);for (int num : nums) {if (minHeap.size() < k) {minHeap.add(num); // 堆未滿,直接加入} else if (num > minHeap.peek()) {// 元素大于堆頂,替換堆頂并調整minHeap.poll();minHeap.add(num);}}return minHeap.peek(); // 堆頂為第k大元素}
}

復雜度分析?
時間復雜度:O (nlogk),插入 n 個元素,每次堆調整為 O (logk)。?
空間復雜度:O (k),堆存儲 k 個元素。
?

考研 408 例題解析? 🎂

例題 1:概念辨析題(選擇題)?

題目:下列關于堆的敘述中,正確的是( )。
?A. 最大堆中,從根節點到任意葉子節點的路徑上的元素是遞減的?
B. 堆排序是穩定的排序算法?
C. 構建最大堆的時間復雜度為 O (nlogn)?
D. 堆的調整操作(Heapify)的時間復雜度為 O (n)?

答案:A?
解析:?
A 正確:最大堆中,父節點的值大于等于子節點,因此根到葉子的路徑元素遞減。?
B 錯誤:堆排序中交換元素可能改變相等元素的相對順序(如 [2,2] 排序后可能顛倒),因此不穩定。?
C 錯誤:構建最大堆的時間復雜度為 O (n),而非 O (nlogn)。?
D 錯誤:堆的調整操作時間復雜度為 O (logn),與樹的高度相關。?

例題 2:算法設計題(408 高頻考點)?

題目:設計一個算法,利用堆實現一個優先級隊列,支持插入元素和提取最大元素操作,并分析兩種操作的時間復雜度。?

解題思路?
優先級隊列結構:用數組存儲堆,維護最大堆性質。?
插入操作:?
將新元素添加到堆尾。?
執行 “上浮” 操作:與父節點比較,若大于父節點則交換,直至根節點或小于父節點。?
提取最大元素操作:?
取出堆頂元素(最大值)。?
將堆尾元素移至堆頂。?
執行 “下沉” 操作(即堆調整),維護最大堆性質。

Java 代碼實現

public class MaxPriorityQueue {private int[] heap;private int size; // 當前元素個數private int capacity; // 隊列容量public MaxPriorityQueue(int capacity) {this.capacity = capacity;heap = new int[capacity + 1]; // 1-based索引,便于計算子節點size = 0;}// 插入元素public void insert(int val) {if (size == capacity) {throw new IllegalStateException("隊列已滿");}size++;heap[size] = val;swim(size); // 上浮調整}// 提取最大元素public int extractMax() {if (size == 0) {throw new NoSuchElementException("隊列為空");}int max = heap[1];swap(1, size); // 交換堆頂與堆尾size--;sink(1); // 下沉調整return max;}// 上浮操作(用于插入)private void swim(int k) {while (k > 1 && heap[k] > heap[k / 2]) { // 與父節點比較swap(k, k / 2);k = k / 2;}}// 下沉操作(用于提取最大元素)private void sink(int k) {while (2 * k <= size) { // 存在左子節點int j = 2 * k; // 左子節點if (j < size && heap[j] < heap[j + 1]) { // 右子節點更大j++;}if (heap[k] >= heap[j]) {break; // 已滿足最大堆性質}swap(k, j);k = j;}}private void swap(int i, int j) {int temp = heap[i];heap[i] = heap[j];heap[j] = temp;}
}

復雜度分析?

插入操作:時間復雜度 O (logn),上浮操作最多執行 logn 次(樹的高度)。?
提取最大元素:時間復雜度 O (logn),下沉操作最多執行 logn 次。?

堆排序的擴展與應用? 🎂

實際應用場景?

優先級隊列:操作系統的進程調度、任務隊列等,需頻繁獲取最大值 / 最小值。?
Top-K 問題:如獲取海量數據中前 k 個最大元素(如 LeetCode 215 題)。?
流數據處理:實時處理數據流,維護動態數據的 Top-K 元素。?
堆排序:適用于大規模數據排序,尤其是內存有限的場景(原地排序)。?

與其他排序算法的對比

排序算法平均時間復雜度最壞時間復雜度空間復雜度
堆排序O(nlogn)O(nlogn)O(1)
快速排序O(nlogn)O(n2)O(logn)
歸并排序O(nlogn)O(nlogn)O(n)

考研 408 備考要點? 🎂

核心考點:堆的性質、堆的構建與調整、堆排序的步驟與復雜度。?

重點掌握:?

堆的索引計算(父節點與子節點的關系)。?
堆調整(Heapify)的遞歸與非遞歸實現。?
堆排序與優先級隊列的關聯,以及 Top-K 問題的解法。?

常見錯誤:?

混淆最大堆與最小堆的調整邏輯。?
構建堆時從 0 開始而非最后一個非葉子節點。?
忽略堆排序的不穩定性,錯誤認為其穩定。?

總結? 🎂

堆排序作為一種高效的排序算法,憑借 O (nlogn) 的穩定時間復雜度和原地排序的特性,在大規模數據處理中有著不可替代的地位。本文從堆的基本概念出發,詳細講解了堆的構建、調整操作和堆排序的完整流程,結合 LeetCode 例題(排序數組、Top-K 問題)展示了堆的核心應用,通過考研 408 例題解析了概念辨析和算法設計思路,并穿插 SVG 圖示直觀呈現了堆的結構與操作過程。?
掌握堆排序的關鍵在于:?
深刻理解堆的性質及索引映射關系。?
熟練實現堆的調整(Heapify)、構建和排序過程。?
靈活運用堆解決優先級隊列、Top-K 等實際問題。?
在考研備考中,堆的性質、堆排序的復雜度分析以及優先級隊列的實現是重點,需結合具體例題深入練習,理解堆在數據結構中的核心作用。

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

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

相關文章

I/O 多路復用select,poll

目錄 I/O多路復用的介紹 多進程/多線程模型的弊端 網絡多路復用如何解決問題&#xff1f; 網絡多路復用的常見實現方式 常見的開源網絡庫 select詳細介紹 select函數介紹 套接字可讀事件,可寫事件,異常事件 fd_set類型介紹 select的兩次拷貝&#xff0c;兩次遍歷 se…

最終分配算法【論文材料】

文章目錄一、最終分配算法1.1 平衡的情況1.2 不平衡的情況1.3 TDM 約束一、最終分配算法 上一步合法化后&#xff0c;group 的 TDM 情況大致分為兩類&#xff0c;一類是平衡的&#xff0c;最大的一些 group 的 TDM 比較接近。另外一種情況就是不平衡的&#xff0c;最大的 group…

《大數據技術原理與應用》實驗報告七 熟悉 Spark 初級編程實踐

目 錄 一、實驗目的 二、實驗環境 三、實驗內容與完成情況 3.1 Spark讀取文件系統的數據。 3.2 編寫獨立應用程序實現數據去重。 3.3 編寫獨立應用程序實現求平局值問題。 四、問題和解決方法 五、心得體會 一、實驗目的 1. 掌握使用 Spark 訪問本地文件和 HDFS 文件的…

機器學習漫畫小抄 - 彩圖版

斯坦福機器學習漫畫小抄&#xff0c;中文版來啦&#xff01; 下載地址&#xff1a; 通過網盤分享的文件&#xff1a;機器學習知識點彩圖版.pdf 鏈接: https://pan.baidu.com/s/1-fH9OpC_u_OrTqWy6gVUCA 提取碼: 246r

1.初始化

業務模塊核心技術棧業務&#xff08;亮點&#xff09;解決方案課程安排01 認識Vue3為什么需要學Vue3?Vue3組合式API體驗Vue3更多的優勢2 使用create-vue搭建Vue3項目認識 create-vue使用create-vue創建項目3 熟悉項目目錄和關鍵文件項目目錄和關鍵文件4 組合式API - setup選項…

Milvus分布式數據庫工作職責

主導騰訊云Milvus服務化項目&#xff0c;設計多租戶隔離方案&#xff0c;支撐日均10億向量請求&#xff0c;延遲降低40%。優化IVF_PQ索引構建流程&#xff0c;通過量化編碼壓縮使內存占用減少60%&#xff0c;QPS提升35%。開發基于Kubernetes的Milvus Operator&#xff0c;實現自…

FMEA-CP-PFD三位一體數字化閉環:汽車部件質量管控的速效引擎

FMEA-CP-PFD三位一體數字化閉環&#xff1a;汽車部件質量管控的速效引擎 全星FMEA軟件系統通過??FMEA&#xff08;失效模式分析&#xff09;、CP&#xff08;控制計劃&#xff09;、PFD&#xff08;過程流程圖&#xff09;三大工具的一體化協同管理??&#xff0c;為汽車部件…

VUE2 學習筆記1

目錄 VUE特點 文檔tips 開發者工具 從一個Hello world開始 hello world Demo 容器和實例的對應關系 差值語法{{}} VUE特點 構建用戶界面&#xff1a;可以用來把數據構建成用戶界面。 漸進式&#xff1a;自底向上&#xff0c;可以先從一個非常輕量級的框架開始&#xf…

嵌入式學習系統編程(四)進程

目錄 一、進程 1.程序和進程 2.進程的八種狀態 3. 幾個狀態 4.關于進程常用命令 二、關于進程的函數 1.fork 2.面問 3.孤兒進程 后臺進程 2. exec函數族 (只保留父子關系&#xff0c;做新的事情) strtok函數 三、進程的結束 1.分類 exit和_exit的區別 wait函數…

Linux中添加重定向(Redirection)功能到minishell

前言&#xff1a;在談論添加minishell之前&#xff0c;我再重談一下重定向的具體實現等大概思想&#xff01;&#xff01;&#xff01;方便自己回顧&#xff01;&#xff01;&#xff01; 目錄 一、重定向&#xff08;Redirection&#xff09;原理詳解 1、文件描述符基礎 2、…

Django由于數據庫版本原因導致數據庫遷移失敗解決辦法

在django開發中&#xff0c;一般我們初始化一個項目之后&#xff0c;創建應用一般就會生成如下的目錄&#xff1a;django-admin startproject myproject python manage.py startapp blogmyproject/ ├── manage.py └── myproject/ | ├── __init__.py | ├── se…

C++STL系列之vector

前言 vector是變長數組&#xff0c;有點像數據結構中的順序表&#xff0c;它和list也是經常被拿出作對比的&#xff0c; vector使用動態分配數組來存儲它的元素。當新元素插入時候&#xff0c;這個數組需要被重新分配大小&#xff0c;如果擴容&#xff0c;因為要開一個新數組把…

Functional C++ for Fun Profit

Lambda Conf上有人講C函數式編程。在Functional Conf 2019上&#xff0c;就有主題為“Lambdas: The Functional Programming Companion of Modern C”的演講。演講者介紹了現代C中函數式編程相關內容&#xff0c;講解了如何使用Lambda表達式編寫符合函數式編程原則的C代碼&…

Python基礎理論與實踐:從零到爬蟲實戰

引言Python如輕舟&#xff0c;載你探尋數據寶藏&#xff01;本文從基礎理論&#xff08;變量、循環、函數、模塊&#xff09;啟航&#xff0c;結合requests和BeautifulSoup實戰爬取Quotes to Scrape&#xff0c;適合零基礎到進階者。文章聚焦Python基礎&#xff08;變量、循環、…

ThingJS開發從入門到精通:構建三維物聯網可視化應用的完整指南

文章目錄第一部分&#xff1a;ThingJS基礎入門第一章 ThingJS概述與技術架構1.1 ThingJS平臺簡介1.2 技術架構解析1.3 開發環境配置第二章 基礎概念與核心API2.1 核心對象模型2.2 場景創建與管理2.3 對象操作基礎第三章 基礎開發實戰3.1 第一個ThingJS應用3.2 事件系統詳解3.3 …

關于list

1、什么是listlist是一個帶頭結點的雙向循環鏈表模版容器&#xff0c;可以存放任意類型&#xff0c;需要顯式定義2、list的使用有了前面學習string和vector的基礎&#xff0c;學習和使用list會方便很多&#xff0c;因為大部分的內容依然是高度重合的。與順序表不同&#xff0c;…

Mysql 查看當前事務鎖

在 MySQL 中查看事務鎖&#xff08;鎖等待、鎖持有等&#xff09;&#xff0c;可以使用以下方法&#xff1a; 一、查看當前鎖等待情況&#xff08;推薦&#xff09; SELECTr.trx_id AS waiting_trx_id,r.trx_mysql_thread_id AS waiting_thread,r.trx_query AS waiting_query,b…

【Keil5-map文件】

Keil5-map文件■ map文件■ map文件

k8s 基本架構

基于Kubernetes(K8s)的核心設計&#xff0c;以下是其關鍵基本概念的詳細解析。這些概念構成了K8s容器編排系統的基石&#xff0c;用于自動化部署、擴展和管理容器化應用。### 一、K8s核心概念概覽 K8s的核心對象圍繞容器生命周期管理、資源調度和服務發現展開&#xff0c;主要包…

Bell不等式賦能機器學習:微算法科技MLGO一種基于量子糾纏的監督量子分類器訓練算法技術

近年來&#xff0c;量子計算&#xff08;Quantum Computing&#xff09; 和 機器學習&#xff08;Machine Learning&#xff09; 的融合成為人工智能和計算科學領域的重要研究方向。隨著經典計算機在某些復雜任務上接近計算極限&#xff0c;研究人員開始探索量子計算的獨特優勢…