基礎算法總結之堆排序算法

堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分爲大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即A[PARENT[i]] >= A[i]。在數組的非降序排序中,需要使用的就是大根堆,因爲根據大根堆的要求可知,最大的值一定在堆頂。

n個關鍵字序列Kl,K2,…,Kn稱爲(Heap),當且僅當該序列滿足如下性質(簡稱爲堆性質):
(1)ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),當然,這是小根堆,大根堆則換成>=號。//k(i)相當於二叉樹的非葉子結點,K(2i)則是左子節點,k(2i+1)是右子節點
若將此序列所存儲的向量R[1..n]看做是一棵完全二叉樹存儲結構,則堆實質上是滿足如下性質的完全二叉樹:
樹中任一非葉子結點的關鍵字均不大於(或不小於)其左右孩子(若存在)結點的關鍵字。
【例】關鍵字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分別滿足堆性質(1)和(2),故它們均是堆,其對應的完全二叉樹分別如小根堆示例和大根堆示例所示。
大根堆和小根堆:根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最小者的堆稱爲小根堆,又稱最小堆。根結點(亦稱爲堆頂)的關鍵字是堆裏所有結點關鍵字中最大者,稱爲大根堆,又稱最大堆。注意:①堆中任一子樹亦是堆。②以上討論的堆實際上是二叉堆(Binary Heap),類似地可定義k叉堆。

高度

堆可以被看成是一棵樹,結點在堆中的高度可以被定義爲從本結點到葉子結點的最長簡單下降路徑上邊的數目;定義堆的高度爲樹根的高度。我們將看到,堆結構上的一些基本操作的運行時間至多是與樹的高度成正比,爲O(lgn)。

C語言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <stdio.h>
//array是待調整的堆數組,i是待調整的數組元素的位置,nlength是數組的長度
//本函數功能是:根據數組array構建大根堆
void HeapAdjust(int array[],int i,int nLength)
{
    int nChild;
    int nTemp;
    for(;2*i+1<nLength;i=nChild)
    {
        //子結點的位置=2*(父結點位置)+1
        nChild=2*i+1;
        //得到子結點中較大的結點
        if(nChild<nLength-1&&array[nChild+1]>array[nChild])++nChild;
        //如果較大的子結點大於父結點那麼把較大的子結點往上移動,替換它的父結點
        if(array[i]<array[nChild])
        {
            nTemp=array[i];
            array[i]=array[nChild];
            array[nChild]=nTemp; 
        }
        else break//否則退出循環
    }
}
//堆排序算法
void HeapSort(int array[],int length)
{
    int i;
    //調整序列的前半部分元素,調整完之後第一個元素是序列的最大的元素
    //length/2-1是最後一個非葉節點,此處"/"爲整除
    for(i=length/2-1;i>=0;--i)
    HeapAdjust(array,i,length);
    //從最後一個元素開始對序列進行調整,不斷的縮小調整的範圍直到第一個元素
    for(i=length-1;i>0;--i)
    {
        //把第一個元素和當前的最後一個元素交換,
        //保證當前的最後一個位置的元素都是在現在的這個序列之中最大的
        array[i]=array[0]^array[i];
        array[0]=array[0]^array[i];
        array[i]=array[0]^array[i];
        //不斷縮小調整heap的範圍,每一次調整完畢保證第一個元素是當前序列的最大值
        HeapAdjust(array,0,i);
    }
}
int main()
{
    int i;
    int num[]={9,8,7,6,5,4,3,2,1,0};
    HeapSort(num,sizeof(num)/sizeof(int));
    for(i=0;i<sizeof(num)/sizeof(int);i++)
    {
        printf("%d ",num[i]);
    }
    printf("\nok\n");
    return 0;
}

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//堆排序
//整理節點time:O(lgn)
template<typenameT>
void MinHeapify(T*arry,int size,int element)
{
int lchild=element*2+1,rchild=lchild+1;//左右子樹
while(rchild<size)//子樹均在範圍內
{
if(arry[element]<=arry[lchild]&&arry[element]<=arry[rchild])//如果比左右子樹都小,完成整理
{
return;
}
if(arry[lchild]<=arry[rchild])//如果左邊最小
{
swap(arry[element],arry[lchild]);//把左面的提到上面
element=lchild;//循環時整理子樹
}
else//否則右面最小
{
swap(arry[element],arry[rchild]);//同理
element=rchild;
}
lchild=element*2+1;
rchild=lchild+1;//重新計算子樹位置
}
if(lchild<size&&arry[lchild]<arry[element])//只有左子樹且子樹小於自己
{
swap(arry[lchild],arry[element]);
}
return;
}
//堆排序time:O(nlgn)
template<typenameT>
void HeapSort(T*arry,int size)
{
int i;
for(i=size-1;i>=0;i--)//從子樹開始整理樹
{
MinHeapify(arry,size,i);
}
while(size>0)//拆除樹
{
swap(arry[size-1],arry[0]);//將根(最小)與數組最末交換
size--;//樹大小減小
MinHeapify(arry,size,0);//整理樹
}
return;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章