1. 程式人生 > >經典排序演算法----堆與堆排序(不穩定)

經典排序演算法----堆與堆排序(不穩定)

堆與堆排序(不穩定

堆排序

穩定性分析

我們知道堆的結構是節點i的孩子為2 * i和2 * i + 1節點,大頂堆要求父節點大於等於其2個子節點,小頂堆要求父節點小於等於其2個子節點。在一個長為n 的序列,堆排序的過程是從第n / 2開始和其子節點共3個值選擇最大(大頂堆)或者最小(小頂堆),這3個元素之間的選擇當然不會破壞穩定性。但當為n / 2 - 1, n / 2 - 2, ... 1這些個父節點選擇元素時,就會破壞穩定性。有可能第n / 2個父節點交換把後面一個元素交換過去了,而第n / 2 - 1個父節點把後面一個相同的元素沒 有交換,那麼這2個相同的元素之間的穩定性就被破壞了。所以,堆排序不是穩定的排序演算法。
複雜度O(nlogn)

二叉堆

二叉堆的定義

二叉堆是完全二叉樹或者是近似完全二叉樹。

二叉堆滿足二個特性:
  • 父結點的鍵值總是大於或等於(小於或等於)任何一個子節點的鍵值。
  • 每個結點的左子樹和右子樹都是一個二叉堆(都是最大堆或最小堆)。
當父結點的鍵值總是大於或等於任何一個子節點的鍵值時為最大堆。當父結點的鍵值總是小於或等於任何一個子節點的鍵值時為最小堆。下圖展示一個最小堆:

堆的儲存

一般都用陣列來表示堆,i結點的父結點下標就為(i – 1) / 2。它的左右子結點下標分別為2 * i + 1和2 * i + 2。如第0個結點左右子結點下標分別為1和2。

堆的插入與刪除


堆的插入

每次插入都是將新資料放在陣列最後。可以發現從這個新資料的父結點到根結點必然為一個有序的數列,現在的任務是將這個新資料插入到這個有序資料中——這就類似於直接插入排序中將一個數據併入到有序區間中,不難寫出插入一個新資料時堆的調整程式碼:
  1. //  新加入i結點  其父結點為(i - 1) / 2
  2. void MinHeapFixup(int a[], int i)  
  3. {  
  4.     int j, temp;  
  5.     temp = a[i];  
  6.     j = (i - 1) / 2;      //父結點
  7.     while (j >= 0 && i != 0)  
  8.     {  
  9.         if (a[j] <= temp)  
  10.             break;  
  11.         a[i] = a[j];     //把較大的子結點往下移動,替換它的子結點
  12.         i = j;  
  13.         j = (i - 1) / 2;  
  14.     }  
  15.     a[i] = temp;  
  16. }  
優化之:
  1. void MinHeapFixup(int a[], int i)  
  2. {  
  3.     for (int j = (i - 1) / 2; (j >= 0 && i != 0)&& a[i] > a[j]; i = j, j = (i - 1) / 2)  
  4.         Swap(a[i], a[j]);  
  5. }  
插入時:
  1. //在最小堆中加入新的資料nNum
  2. void MinHeapAddNumber(int a[], int n, int nNum)  
  3. {  
  4.     a[n] = nNum;  
  5.     MinHeapFixup(a, n);  
  6. }  

堆的刪除

按定義,堆中每次都只能刪除第0個數據。為了便於重建堆,實際的操作是將最後一個數據的值賦給根結點,然後再從根結點開始進行一次從上向下的調整。調整時先在左右兒子結點中找最小的,如果父結點比這個最小的子結點還小說明不需要調整了,反之將父結點和它交換後再考慮後面的結點。相當於從根結點將一個數據的“下沉”過程。下面給出程式碼:
  1. //  從i節點開始調整,n為節點總數 從0開始計算 i節點的子節點為 2*i+1, 2*i+2
  2. void MinHeapFixdown(int a[], int i, int n)  
  3. {  
  4.     int j, temp;  
  5.     temp = a[i];  
  6.     j = 2 * i + 1;  
  7.     while (j < n)  
  8.     {  
  9.         if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
  10.             j++;  
  11.         if (a[j] >= temp)  
  12.             break;  
  13.         a[i] = a[j];     //把較小的子結點往上移動,替換它的父結點
  14.         i = j;  
  15.         j = 2 * i + 1;  
  16.     }  
  17.     a[i] = temp;  
  18. }  
  19. //在最小堆中刪除數
  20. void MinHeapDeleteNumber(int a[], int n)  
  21. {  
  22.     Swap(a[0], a[n - 1]);  
  23.     MinHeapFixdown(a, 0, n - 1);  
  24. }  

堆化陣列

有了堆的插入和刪除後,再考慮下如何對一個數據進行堆化操作。要一個一個的從陣列中取出資料來建立堆吧,不用!先看一個數組,如下圖:

很明顯,對葉子結點來說,可以認為它已經是一個合法的堆了即20,60, 65, 4, 49都分別是一個合法的堆。只要從A[4]=50開始向下調整就可以了。然後再取A[3]=30,A[2] = 17,A[1] = 12,A[0] = 9分別作一次向下調整操作就可以了。下圖展示了這些步驟:
  1. //建立最小堆
  2. void MakeMinHeap(int a[], int n)  
  3. {  
  4.     for (int i = n / 2 - 1; i >= 0; i--)  
  5.         MinHeapFixdown(a, i, n);  
  6. }  

堆排序

首先可以看到堆建好之後堆中第0個數據是堆中最小的資料。取出這個資料再執行下堆的刪除操作。這樣堆中第0個數據又是堆中最小的資料,重複上述步驟直至堆中只有一個數據時就直接取出這個資料。

由於堆也是用陣列模擬的,故堆化陣列後,第一次將A[0]與A[n - 1]交換,再對A[0…n-2]重新恢復堆。第二次將A[0]與A[n – 2]交換,再對A[0…n - 3]重新恢復堆,重複這樣的操作直到A[0]與A[1]交換。由於每次都是將最小的資料併入到後面的有序區間,故操作完成後整個陣列就有序了。有點類似於直接選擇排序。
  1. void MinheapsortTodescendarray(int a[], int n)  
  2. {  
  3.     for (int i = n - 1; i >= 1; i--)  
  4.     {  
  5.         Swap(a[i], a[0]);  
  6.         MinHeapFixdown(a, 0, i);  
  7.     }  
  8. }  
注意使用最小堆排序後是遞減陣列,要得到遞增陣列,可以使用最大堆。

由於每次重新恢復堆的時間複雜度為O(logN),共N - 1次重新恢復堆操作,再加上前面建立堆時N / 2次向下調整,每次調整時間複雜度也為O(logN)。二次操作時間相加還是O(N * logN)。故堆排序的時間複雜度為O(N * logN)。

相關推薦

經典排序演算法----排序穩定

堆與堆排序(不穩定)穩定性分析我們知道堆的結構是節點i的孩子為2 * i和2 * i + 1節點,大頂堆要求父節點大於等於其2個子節點,小頂堆要求父節點小於等於其2個子節點。在一個長為n 的序列,堆排序的過程是從第n / 2開始和其子節點共3個值選擇最大(大頂堆)或者最小(小

簡單選擇排序演算法原理及java實現超詳細

選擇排序是一種非常簡單的排序演算法,就是在序列中依次選擇最大(或者最小)的數,並將其放到待排序的數列的起始位置。 簡單選擇排序的原理 簡單選擇排序的原理非常簡單,即在待排序的數列中尋找最大(或者最小)的一個數,與第 1 個元素進行交換,接著在剩餘的待排序的數列中繼續找最大(最小)的一個數,與第 2 個元素交

各種排序演算法的實現及其比較c++實現

轉載自: http://lib.csdn.net/article/datastructure/9028     CSDN資料結構與演算法 排序演算法是筆試和麵試中最喜歡考到的內容,今晚花了好幾個小時的時間把之前接觸過的排序演算法都重新實現了一遍。 主要是作為複習用。當

數字影象處理,經典濾波演算法去噪對比實驗Matlab實現

一,經典濾波演算法的基本原理 1,中值濾波和均值濾波的基本原理 參考以前轉載的部落格:http://blog.csdn.net/ebowtang/article/details/38960271

人工智慧之演算法知識實戰篇附資源

寫在前頭 1、課程的選擇: 選擇一門完整的演算法課程,並以此課程為主,系統性學習;公眾號或者其他材料都是輔助,裡面主要是零碎的知識。推薦業內經典課程。 2、本號不涉及技術層面的內容: 沒有那個水平;前人已經有非常完善的課程體系 機器學習篇 1、知識內容 2、

演算法分析設計基礎 清華版

Taken from "Introduction to The Design and Analysis of Algorithms" by Anany Levitin 節選自《演算法設計與分析基礎》潘彥 譯 蠻力法 就像寶劍不是撬棍一樣,科學也很少使用蠻力。 ——Edward Lytton (183

決策樹演算法——熵資訊增益Python3實現

1、熵、條件熵與資訊增益 (1)熵(entropy) (2)條件熵(conditional entropy) (3)資訊增益(information gain) 2、資訊增益演算法實現流程 2、資料集以及每個特徵資訊增益的計算

排序演算法(四):排序Heap Sort

堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進。 基本思想: 堆的定義如下:具有n個元素的序列(k1,k2,...,kn),當且僅當滿足 時稱之為堆。由堆的定義可以看出,堆頂元素(即第一個元素)必為最小項(小頂堆)。 若以一維陣列儲存一個堆,則堆對應一棵完全二叉樹,且所有

十大經典排序演算法動畫解析,看我就夠了!配程式碼完全版

GitHub Repo:Sort Article Follow: MisterBooo · GitHub 排序演算法是《資料結構與演算法》中最基本的演算法之一。 排序演算法可以分為內部排序和外部排序。 內部排序是資料記錄在記憶體中進行排序。 而外部排序是因排序的資料很大,一次不能容納全部的排

白話經典算法系列之七 排序

 堆排序與快速排序,歸併排序一樣都是時間複雜度為O(N*logN)的幾種常見排序方法。學習堆排序前,先講解下什麼是資料結構中的二叉堆。二叉堆的定義二叉堆是完全二叉樹或者是近似完全二叉樹。二叉堆滿足二個特

必須知道的八大種排序演算法【java實現】 歸併排序演算法排序演算法詳解

一、歸併排序演算法 基本思想:   歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分為若干個子序列,每個子序列是有序的。然後再把有序子序列合併為整體有序序列。 歸併排序示例:   合併方法: 設r[i…n]由兩個有序子表r[i…m]和r[m+1…n]組

排序演算法設計分析

堆排序(HeapSort)是指利用堆積樹(堆)這種資料結構所設計的一種排序演算法,它是選擇排序的一種。堆分為大根堆和小根堆,是完全二叉樹。大根堆要求父結點的值大於或等於子結點的值,小根堆相反。根據大根

演算法筆記》4. 排序、比較器詳解

[TOC] # 比較器與堆 ## 堆結構 ### 完全二叉樹結構 > 完全二叉樹結構:要麼本層是滿的,要麼先滿左邊的,以下都是完全二叉樹 1. ``` graph TD A-->B A-->C ``` 2. ``` graph TD A-->B A-->C B--&

排序

分別是 步驟 最大的 第一次 img 二叉 wrap kaa lang 堆排序與快速排序,歸並排序一樣都是時間復雜度為O(N*logN)的幾種常見排序方法。 堆排序是就地排序,輔助空間為O(1)。 它是不穩定的排序方法。(排序的穩定性是指如果在排序的序列中,存在前後相同的兩

排序演算法大雜燴之排序

排序演算法大雜燴主幹文章傳送門 堆排序 #include <iostream> #include <vector> /* 非降序排序 時間複雜度:o(nlgn) 空間複雜度:o(nlgn) 建堆:o(n) 維護堆:o(lgn) 不穩定 */ using n

排序演算法7】排序

此排序演算法完全是看著MoreWindows大神的部落格一邊看一邊體會一邊跟著敲出來的,在這裡還是非常感謝他。 部落格地址https://blog.csdn.net/MoreWindows/article/details/6709644 上述地址對堆排序講的很細緻,這裡就不講了。但是不知

排序演算法串燒——排序

堆排序準備知識講解 什麼是堆: 堆是一種具有特定特性的完全二叉樹。 滿足兩種特定的性質: 1. 堆的每一個父親節點都大於(或小於)其子節點(分別稱為:大頂堆、小頂堆)。 堆排序的建立過程: 我們使用一組資料來進行堆排序,以直觀的理解堆排序的過程。我們實現的是大

經典排序演算法:希爾排序Shell Sort

希爾排序 希爾排序是Shell在1959年提出的一種排序演算法,它出現的重要意義在於,之前的排序演算法基本都是O(n²),希爾演算法是突破這個時間複雜度的第一批演算法之一,所以它絕對值得我們瞭解掌握! 希爾排序的基本思想是: 把記錄按increment(增量)分

二叉排序

二叉堆的定義 二叉堆是完全二叉樹或者是近似完全二叉樹。 二叉堆滿足二個特性: 1.父結點的鍵值總是大於或等於(小於或等於)任何一個子節點的鍵值。 2.每個結點的左子樹和右子樹都是一個二叉堆(都是最大堆或最小堆)。 當父結點的鍵值總是大於或等於任何一個子節點的鍵值時為最大堆。當

排序演算法5——圖解排序及其實現

排序演算法1——圖解氣泡排序及其實現(三種方法,基於模板及函式指標) 排序演算法2——圖解簡單選擇排序及其實現 排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現 排序演算法4——圖解希爾排序及其實現 排序演算法5——圖解堆排序及其實現 排序演算法6——圖解歸併排序及其遞迴與非