堆排序詳解

堆排序

1. 堆定義

堆是一種特殊的二叉樹,具備以下兩種性質

1)每個節點的值都大於(或小於,稱爲最小堆)其子節點的值

2)樹是完全平衡的,並且最後一層的樹葉都在最左邊

二叉堆是一種完全二叉樹,其任意子樹的左右節點(如果有的話)的鍵值一定比根節點大,下圖是一個最小堆。

二叉堆的表示結構:

由於二叉堆的結構性質,可以採用一維數組來表示二叉堆。

二叉堆元素從數組下標1開始,則

T[i]的左兒子爲T[2*i],右兒子爲T[2*i+1]

T[i]的父結點爲T[i/2]

150933611.png


2.堆的操作

2.1 插入

將待插入的元素放在數組的最後位置,這可能破壞了二叉堆結構性質,通過“上浮”操作維護其結構。


2.2 刪除

將數組的第一位元素刪除,並將數組最後位置的元素放到第一位,這可能破壞其結構性質,可通過“下沉”操作維護二叉堆的結構。


上浮操作:

void swap(int* _a, int* _b)

{

int tmp =*_a;

*_a = *_b;

*_b = tmp;

}


/*

* 從底向上維護二叉堆中第n位元素的堆序性質

* 如果第n位元素的值大於其父結點n/2的值,則交換

* 直到根結點或滿足父結點值小於當前節點值

**/

void shiftUp(int* T, int n)

{

assert(T !=NUU && n>=1);


int i = n;

int p =i/2;

while(i !=1)

{

p =i/2;

//不滿足最小堆結構性質,交換當前結點與其父結點值

//指向當前結點的父結點,繼續判斷是否滿足堆序性質

if(T[i]< T[p])

{

swap(&T[i],&T[p]);

i= p;

}else{

return;

}

}

}


下沉操作:

/*

* 自頂向下維護二叉堆的堆序性質

* 如果第i位元素沒有子結點,則結束

* 如果第i位元素有子結點,則c爲值較小子結點的下標

* 如果T[i]>T[c],則交換i和c位置的元素值,i=c,繼續進行

**/

void shiftDown(int* T, int n)

{

assert(T !=NULL && n >= 1);

int i = 1;

int c;

do

{

c =2 * i;

if(c> n)

return;

//有兩個子結點,找到值小的子結點

if((c+1)<= n)

{

if(T[c+1]< T[c])

c++;

}

if(T[i]> T[c])

{

swap(&T[i],&T[c]);

i= c;

}else{

return;

}

} while(c <= n);

}


3.堆排序

有了二叉堆及其操作,可以據其定義堆排序算法。

未排序堆

已排序部分

1 i n


堆排序主要包括兩個部分:

a.建立堆

通過循環的shiftUp操作,將原數組構造成堆結構;

b.排序

數組分成兩部分,未排序的堆和已排序部分。初始時,已排序部分爲空。

此時T[1]爲前i個元素中最小的,將T[1]和T[i]交換,這樣就把剩下數組中最小的元素放到已排序部分;再通過shiftDown操作維護堆序性質;i值從n到2循環


void heapSort(int* T, int len)

{

assert(T !=NULL && len >= 0);

int i;

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

{

shiftUp(T,i);

}

for(i=len; i>=2; --i)

{

swap(&T[i],&T[1]);

shiftDown(T,i-1);

}

}


int main()

{

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

printf("before heapSort:\n");

int i, len = 9;

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

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

printf("\n");


heapSort(a, len);

printf("after heapSort:\n");

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

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

printf("\n");


return 0;

}


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