您好,登錄后才能下訂單哦!
這篇文章主要講解了“c++怎么實現堆排序”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“c++怎么實現堆排序”吧!
堆的結構可以分為大根堆和小根堆,是一個完全二叉樹,而堆排序是根據堆的這種數據結構設計的一種排序,下面先來看看什么是大根堆和小根堆
1.1 大根堆和小根堆
性質:每個結點的值都大于其左孩子和右孩子結點的值,稱之為大根堆;每個結點的值都小于其左孩子和右孩子結點的值,稱之為小根堆。如下圖
我們對上面的圖中每個數都進行了標記,上面的結構映射成數組就變成了下面這個樣子
還有一個基本概念:查找數組中某個數的父結點和左右孩子結點,比如已知索引為i的數,那么
1.父結點索引:(i-1)/2(這里計算機中的除以2,省略掉小數)
2.左孩子索引:2*i+1
3.右孩子索引:2*i+2
所以上面兩個數組可以腦補成堆結構,因為他們滿足堆的定義性質:
大根堆:arr(i)>arr(2i+1) && arr(i)>arr(2i+2)
小根堆:arr(i)<arr(2i+1) && arr(i)<arr(2i+2)
基本思想:
首先將待排序的數組構造成一個大根堆,此時,整個數組的最大值就是堆結構的頂端
將頂端的數與末尾的數交換,此時,末尾的數為最大值,剩余待排序數組個數為n-1
3.將剩余的n-1個數再構造成大根堆,再將頂端數與n-1位置的數交換,如此反復執行,便能得到有序數組
將無序數組構造成一個大根堆(升序用大根堆,降序就用小根堆)
假設存在以下數組
主要思路:第一次保證0~0位置大根堆結構(廢話),第二次保證0~1位置大根堆結構,第三次保證0~2位置大根堆結構...直到保證0~n-1位置大根堆結構(每次新插入的數據都與其父結點進行比較,如果插入的數比父結點大,則與父結點交換,否則一直向上交換,直到小于等于父結點,或者來到了頂端)
插入6的時候,6大于他的父結點3,即arr(1)>arr(0),則交換;此時,保證了0~1位置是大根堆結構,如下圖:
(友情提示:待交換的數為藍色,交換后的數為綠色)
插入8的時候,8大于其父結點6,即arr(2)>arr(0),則交換;此時,保證了0~2位置是大根堆結構,如下圖
插入5的時候,5大于其父結點3,則交換,交換之后,5又發現比8小,所以不交換;此時,保證了0~3位置大根堆結構,如下圖
插入7的時候,7大于其父結點5,則交換,交換之后,7又發現比8小,所以不交換;此時整個數組已經是大根堆結構
此時,我們已經得到一個大根堆,下面將頂端的數與最后一位數交換,然后將剩余的數再構造成一個大根堆
(友情提示:黑色的為固定好的數字,不再參與排序)
此時最大數8已經來到末尾,則固定不動,后面只需要對頂端的數據進行操作即可,拿頂端的數與其左右孩子較大的數進行比較,如果頂端的數大于其左右孩子較大的數,則停止,如果頂端的數小于其左右孩子較大的數,則交換,然后繼續與下面的孩子進行比較
下圖中,5的左右孩子中,左孩子7比右孩子6大,則5與7進行比較,發現5<7,則交換;交換后,發現5已經大于他的左孩子,說明剩余的數已經構成大根堆,后面就是重復固定最大值,然后構造大根堆
如下圖:頂端數7與末尾數3進行交換,固定好7,
剩余的數開始構造大根堆 ,然后頂端數與末尾數交換,固定最大值再構造大根堆,重復執行上面的操作,最終會得到有序數組
到這里,大家應該對堆排序都有了自己的見解,我們對上面的流程總結下:
1、首先將無需數組構造成一個大根堆(新插入的數據與其父結點比較)
2、固定一個最大值,將剩余的數重新構造成一個大根堆,重復這樣的過程
代碼中主要兩個方法:
1、將待排序數組構造成一個大根堆(元素上升)
2、固定一個最大值,將剩余的數再構造成一個大根堆(元素下降)
//堆排序 public static void heapSort(int[] arr) { //構造大根堆 heapInsert(arr); int size = arr.length; while (size > 1) { //固定最大值 swap(arr, 0, size - 1); size--; //構造大根堆 heapify(arr, 0, size); } } //構造大根堆(通過新插入的數上升) public static void heapInsert(int[] arr) { for (int i = 0; i < arr.length; i++) { //當前插入的索引 int currentIndex = i; //父結點索引 int fatherIndex = (currentIndex - 1) / 2; //如果當前插入的值大于其父結點的值,則交換值,并且將索引指向父結點 //然后繼續和上面的父結點值比較,直到不大于父結點,則退出循環 while (arr[currentIndex] > arr[fatherIndex]) { //交換當前結點與父結點的值 swap(arr, currentIndex, fatherIndex); //將當前索引指向父索引 currentIndex = fatherIndex; //重新計算當前索引的父索引 fatherIndex = (currentIndex - 1) / 2; } } } //將剩余的數構造成大根堆(通過頂端的數下降) public static void heapify(int[] arr, int index, int size) { int left = 2 * index + 1; int right = 2 * index + 2; while (left < size) { int largestIndex; //判斷孩子中較大的值的索引(要確保右孩子在size范圍之內) if (arr[left] < arr[right] && right < size) { largestIndex = right; } else { largestIndex = left; } //比較父結點的值與孩子中較大的值,并確定最大值的索引 if (arr[index] > arr[largestIndex]) { largestIndex = index; } //如果父結點索引是最大值的索引,那已經是大根堆了,則退出循環 if (index == largestIndex) { break; } //父結點不是最大值,與孩子中較大的值交換 swap(arr, largestIndex, index); //將索引指向孩子中較大的值的索引 index = largestIndex; //重新計算交換之后的孩子的索引 left = 2 * index + 1; right = 2 * index + 2; } } //交換數組中兩個元素的值 public static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }
感謝各位的閱讀,以上就是“c++怎么實現堆排序”的內容了,經過本文的學習后,相信大家對c++怎么實現堆排序這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。