幾種排序總結(上)——堆排序
堆排序
這幾天看了演算法導論的排序部分,作一下總結。
堆排序的優點
1)最壞情況下o(nlgn)的時間複雜度
2)就地排序,不用輔助陣列
幾種操作(以最大堆為例)
1.保持堆性質
這是主要操作,對於節點A[i],前提是以LEFT(i)和RIGHT(i)為根的子樹已經是一個排列好的堆,因此如果A[i]比他的子節點要小,對A[i]進行判斷遞迴下沉。
在最壞的情況下,對於一棵有n個節點的樹,使根結點下沉至樹的底部,需要比較lgn次,因此時間複雜度為o(lgn),若對應高度為h,則為o(h)
2.建堆
對於堆中的所有葉子節點來說,他們的父節點可以使用保持堆性質進行建堆,因為葉子節點本身就可以看作一棵排列好的堆。
此時葉子節點以及他們的父節點都已經滿足堆特性,至底向上對每個剩下的非葉子節點逐一呼叫保持堆性質操作直至到達根結點,建堆完成。
由於在一棵有n個節點的樹中,高度為h至多有(n/2^(h+1))(取上界)個節點,對高度為h的節點使用保持堆特性演算法的時間複雜度為o(h),因此有
而且
所以建堆的緊確時間複雜度為o(n)
3.堆排序
使用堆進行排序,其實就在建好的堆中取出堆頂的元素,然後將堆中最後一個元素放入堆頂部,再呼叫保持堆特性操作得到。將堆中元素全部取出後,就可以得到有序的排列。
時間複雜度分析,第一步首先建堆需要用時o(n),第二步對大小為n的堆,取出元素放入陣列尾部用時o(1),重新進行保持堆特性為o(lgn),因此o(n)+o(nlgn),總體時間時間複雜度為o(nlgn)
由於從堆頂取出元素後,會將堆中的最後一個元素放入堆頂,因此會打亂了原來陣列中元素的順序,所以堆排序是不穩定的。
4.優先順序佇列
優先順序佇列建立在堆的基礎上,主要有返回最大元素/返回最大元素並取出/將第i個元素優先順序增長為key/將元素x插入到優先順序佇列這幾個操作。
對於返回最大元素,直接返回最大堆堆頂即可。對於返回並取出最大元素,在返回後將最後一個堆元素放入堆頂,使堆大小減一併呼叫保持堆特性操作即可。對於將i元素優先順序增長為key,不斷比較i元素與他的父節點如果大於父節點則交換即可。對於插入元素,可以使堆大小加一,令最後一個元素為無窮小,再呼叫增長key操作即可。這些操作都可以在o(lgn)時間內完成。
實現程式碼如下:
#include<stdio.h>
#include<algorithm>
using namespace std;
int a[100];
//保持堆特性操作
void max_heapify(int *a, int i, int len){
int l=2*i;
int r=2*i+1;
int largest=i;
if(l<=len){
if(a[l]>a[i])
largest=l;
else
largest=i;
}
if(r<=len){
if(a[r]>a[largest])
largest=r;
}
if(largest!=i){
swap(a[i],a[largest]);
max_heapify(a,largest,len);
}
}
//建堆
void build_max_heap(int *a, int len){
for(int i=len/2; i>=1; i--){
max_heapify(a,i,len);
}
}
//堆排序
void heapsort(int *a,int len){
build_max_heap(a,len);
int heapsize=len;
for(int i=len; i>=2; i--){
swap(a[1],a[len]);
len--;
max_heapify(a,1,len);
}
}
//優先佇列中返回最大元素
int heap_maximum(int *a){
return a[1];
}
//優先佇列中返回並去掉最大元素
int heap_extract_max(int *a,int &len){
int max = a[1];
a[1]=a[len];
len--;
max_heapify(a,1,len);
return max;
}
//將第i個元素的值增加為key
void heap_increase_key(int *a,int i,int key){
if(key>a[i]){
a[i]=key;
while(i>1&&a[i/2]<a[i]){
swap(a[i/2],a[i]);
i=i/2;
}
}
}
//在優先佇列中插入元素
void max_heap_insert(int *a,int key,int &len){
len++;
a[len]=-99999999;
heap_increase_key(a,len,key);
}
int main(){
int len=0;
scanf("%d",&len);
for(int j=1; j<=len; j++){
scanf("%d",&a[j]);
}
build_max_heap(a,len);
// heapsort(a,len);
// for(int i=1; i<=len; i++){
// if(i!=len)
// printf("%d ",a[i]);
// else
// printf("%d\n",a[i]);
// }
max_heap_insert(a,4,len);
for(int k=1; k<=len; k++){
if(k!=len)
printf("%d ",a[k]);
else
printf("%d\n",a[k]);
}
printf("%d\n",heap_maximum(a));
printf("%d\n",heap_extract_max(a,len));
for(int l=1; l<=len; l++){
if(l!=len)
printf("%d ",a[l]);
else
printf("%d\n",a[l]);
}
heap_increase_key(a,2,10);
for(int m=1; m<=len; m++){
if(m!=len)
printf("%d ",a[m]);
else
printf("%d\n",a[m]);
}
return 0;
}