堆排序

堆排序

排序算法
堆排序作為一種内排序算法,其特點是将待排序記錄R[1..n]看成一棵完全二叉樹的順序存儲結構,利用完全二叉樹中孩子結點和雙親結點之間的内在關系,在當前無序區中選擇關鍵字最小(或最大)的記錄輸出,依次得到一個有序序列。堆排序需要解決的兩個問題:一是如何将一個無序序列建成一個堆;二是在輸出堆頂元素之後,把剩餘元素調整成為一個新堆。堆排序對少量的記錄來說,其優點不明顯,但對大量記錄來說是很有效的。 [1]
    中文名:堆排序 外文名:Heapsort 别名: 類 别:排序算法 發明人:羅伯特·弗洛伊德 起源于:羅伯特·弗洛伊德

簡介

堆排序(英語:Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似完全二叉樹的結構,并同時滿足堆的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。

堆的操作

在堆的數據結構中,堆中的最大值總是位于根節點(在優先隊列中使用堆的話堆中的最小值位于根節點)。堆中定義以下幾種操作:

最大堆調整(Max Heapify):将堆的末端子節點作調整,使得子節點永遠小于父節點

創建最大堆(Build Max Heap):将堆中的所有數據重新排序

堆排序(HeapSort):移除位在第一個數據的根節點,并做最大堆調整的遞歸運算。

實現示例

C語言

#include 

#include 

 

void swap(int* a, int* b)

{

    int temp = *b;

    *b = *a;

    *a = temp;

}

 

void max_heapify(int arr[], int start, int end) 

{

    //建立父節點指标和子節點指标

    int dad = start;

    int son = dad * 2 + 1;

    while (son <= end)  //若子節點指标在範圍内才做比較

        {

            if (son + 1 <= end && arr[son] < arr[son + 1]) 

            //先比較兩個子節點大小,選擇最大的

            son++;

        if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數

            return;

        else  //否則交換父子内容再繼續子節點和孫節點比較

        {

            swap(&arr[dad], &arr[son]);

            dad = son;

            son = dad * 2 + 1;

        }

    }

}

 

void heap_sort(int arr[], int len) 

{

    int i;

    //初始化,i從最後一個父節點開始調整

    for (i = len / 2 - 1; i >= 0; i--)

        max_heapify(arr, i, len - 1);

    //先将第一個元素和已排好元素前一位做交換,再重新調整,直到排序完畢

    for (i = len - 1; i > 0; i--) 

    {

        swap(&arr[0], &arr[i]);

        max_heapify(arr, 0, i - 1);

    }

}

 

int main() {

    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };

    int len = (int) sizeof(arr) / sizeof(*arr);

    heap_sort(arr, len);

    int i;

    for (i = 0; i < len; i++)

        printf("%d ", arr[i]);

    printf("n");

    return 0;

}

C++ 

#include 

#include 

using namespace std;

 

void max_heapify(int arr[], int start, int end) 

{

    //建立父節點指标和子節點指标

    int dad = start;

    int son = dad * 2 + 1;

    while (son <= end)  //若子節點指标在範圍内才做比較

    {    

        if (son + 1 <= end && arr[son] < arr[son + 1]) //先比較兩個子節點大小,選擇最大的

            son++;

        if (arr[dad] > arr[son]) //如果父節點大於子節點代表調整完畢,直接跳出函數

            return;

        else  //否則交換父子内容再繼續子節點和孫節點比較

        {

            swap(arr[dad], arr[son]);

            dad = son;

            son = dad * 2 + 1;

        }

    }

}

 

void heap_sort(int arr[], int len) 

{

    //初始化,i從最後一個父節點開始調整

    for (int i = len / 2 - 1; i >= 0; i--)

        max_heapify(arr, i, len - 1);

    //先将第一個元素和已經排好的元素前一位做交換,再從新調整(剛調整的元素之前的元素),直到排序完畢

    for (int i = len - 1; i > 0; i--) 

    {

        swap(arr[0], arr[i]);

        max_heapify(arr, 0, i - 1);

    }

}

 

void main() 

{

    int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };

    int len = (int) sizeof(arr) / sizeof(*arr);

    heap_sort(arr, len);

    for (int i = 0; i < len; i++)

        cout << arr[i] << ' ';

    cout << endl;

    system("pause");

}

Java語言

 /**

    * 選擇排序-堆排序

    * @param array 待排序數組

    * @return 已排序數組

    */

    public static int[] heapSort(int[] array) {

        //這裡元素的索引是從0開始的,所以最後一個非葉子結點array.length/2 - 1

        for (int i = array.length / 2 - 1; i >= 0; i--) {  

            adjustHeap(array, i, array.length);  //調整堆

        }

  

        // 上述邏輯,建堆結束

        // 下面,開始排序邏輯

        for (int j = array.length - 1; j > 0; j--) {

            // 元素交換,作用是去掉大頂堆

            // 把大頂堆的根元素,放到數組的最後;換句話說,就是每一次的堆調整之後,都會有一個元素到達自己的最終位置

            swap(array, 0, j);

            // 元素交換之後,毫無疑問,最後一個元素無需再考慮排序問題了。

            // 接下來我們需要排序的,就是已經去掉了部分元素的堆了,這也是為什麼此方法放在循環裡的原因

            // 而這裡,實質上是自上而下,自左向右進行調整的

            adjustHeap(array, 0, j);

        }

        return array;

    }

  

    /**

    * 整個堆排序最關鍵的地方

    * @param array 待組堆

    * @param i 起始結點

    * @param length 堆的長度

    */

    public static void adjustHeap(int[] array, int i, int length) {

        // 先把當前元素取出來,因為當前元素可能要一直移動

        int temp = array[i];

        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1為左子樹i的左子樹(因為i是從0開始的),2*k+1為k的左子樹

            // 讓k先指向子節點中最大的節點

            if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子樹,并且右子樹大于左子樹

                k++;

            }

            //如果發現結點(左右子結點)大于根結點,則進行值的交換

            if (array[k] > temp) {

                swap(array, i, k);

                // 如果子節點更換了,那麼,以子節點為根的子樹會受到影響,所以,循環對子節點所在的樹繼續進行判斷

                    i  =  k;

                        } else {  //不用交換,直接終止循環

                break;

            }

        }

    }

  

    /**

    * 交換元素

    * @param arr

    * @param a 元素的下标

    * @param b 元素的下标

    */

    public static void swap(int[] arr, int a, int b) {

        int temp = arr[a];

        arr[a] = arr[b];

        arr[b] = temp;

    }

Python語言

def big_endian(arr,start,end):    

    root=start    

    child=root*2+1 #左孩子    

    while child<=end:

    #孩子比最後一個節點還大,也就意味着最後一個葉子節點了,就得跳出去一次循環,已經調整完畢     

        if child+1<=end and arr[child]

        #為了始終讓其跟子元素的較大值比較,如果右邊大就左換右,左邊大的話就默認           

            child+=1            

        if arr[root]

        #父節點小于子節點直接交換位置,同時坐标也得換,這樣下次循環可以準确判斷:是否為最底層,

        #是不是調整完畢                

            arr[root],arr[child]=arr[child],arr[root]                

            root=child                

            child=root*2+1            

        else:               

        break

         

def heap_sort(arr): #無序區大根堆排序    

    first=len(arr)//2 - 1    

    for start in range(first,-1,-1):

    #從下到上,從左到右對每個節點進行調整,循環得到非葉子節點        

        big_endian(arr,start,len(arr)-1) #去調整所有的節點    

    for end in range(len(arr)-1,0,-1):        

        arr[0],arr[end]=arr[end],arr[0] #頂部尾部互換位置        

        big_endian(arr,0,end-1) #重新調整子節點的順序,從頂開始調整    

    return arr

     

def main():    

    l=[3,1,4,9,6,7,5,8,2,10]    

    print(heap_sort(l))

 

if __name__=="__main__":    

    main()

PHP語言

function hsort(array &$arr, $len){

    $idx = $len - 1;

    //創建堆操作,并使得堆有序

    for($k=floor($len/2); $k>=0; $k--){

        sink($arr, $k, $idx);

    } 

 

    //排序操作

    while($idx>0){

        //獲取最大值操作

        list($arr[0], $arr[$idx]) = [$arr[$idx], $arr[0]];

        $idx--;

        //堆調整下沉

        sink($arr, 0, $idx);        

    }

 

}

 

//堆調整下沉,小數字下沉

function sink(array &$arr, $low, $high){

 

    //從$low開始找子節點

    while($low*2+1<=$high){

        //獲取low的直接左子節點

        $j = $low*2+1;

 

        //判斷$low位置節點子節點中的較大子節點

        if($j<$high && $arr[$j]<$arr[$j+1]){

            $j++;

        }

 

        if($arr[$low]>$arr[$j]){

            break;

        }

        //交換low節點和子節點的值

        list($arr[$low], $arr[$j]) = [$arr[$j], $arr[$low]];

 

        //繼續排查下沉後子節點是否需要進行下沉操作

        $low = $j;

    }

}

 

$a = [4,3,6,7,5,1,9,0,2];

hsort($a, count($a));

print_r($a);

上一篇:052B

下一篇:TFS

相關詞條

相關搜索

其它詞條