堆排序
注:本文參考http://www.cnblogs.com/dolphin0520/ 作者:海子
<1>概念
堆實際上是一棵完全二叉樹,其任何一非葉節點滿足性質: Key[i]<=key[2i+1]&&Key[i]<=key[2i+2] 或 Key[i]>=Key[2i+1]&&key>=key[2i+2] 即任何一非葉節點的關鍵字不大於或者不小於其左右孩子節點的關鍵字。 堆分爲大頂堆和小頂堆,滿足Key[i]>=Key[2i+1]&&key>=key[2i+2]稱爲大頂堆,滿足 Key[i]<=key[2i+1]&&Key[i]<=key[2i+2]稱爲小頂堆。由上述性質可知大頂堆的堆頂的關鍵字肯定是所有關鍵字中最大的,小頂堆的堆頂的關鍵字是所有關鍵字中最小的。
<2>排序思想
利用大頂堆(小頂堆)堆頂記錄的是最大關鍵字(最小關鍵字)這一特性,使得每次從無序中選擇最大記錄(最小記錄)變得簡單。其基本思想爲(大頂堆):1)將初始待排序關鍵字序列(R1,R2....Rn)構建成大頂堆,此堆爲初始的無序區;2)將堆頂元素R[1]與最後一個元素R[n]交換,此時得到新的無序(R1,R2,......Rn-1)和新的有序區(Rn),且滿足R[1,2...n-1]<=R[n];3)由於交換後新的堆頂R[1]可能違反堆的性質,因此需要對當前無序區(R1,R2,......Rn-1)調整爲新堆,然後再次將R[1]與無序區最後一個元素交換,得到新的無序區(R1,R2....Rn-2)和新的有序區(Rn-1,Rn)。不斷重複此過程直到有序區的元素個數爲n-1,則整個排序過程完成。操作過程如下:1)初始化堆:將R[1..n]構造爲堆;2)將當前無序區的堆頂元素R[1]同該區間的最後一個記錄交換,然後將新的無序區調整爲新的堆。因此對於堆排序,最重要的兩個操作就是構造初始堆和調整堆,其實構造初始堆事實上也是調整堆的過程,只不過構造初始堆是對所有的非葉節點都進行調整。下面舉例說明:給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。首先根據該數組元素構建一個完全二叉樹,得到
圖四 交換最大頂點和最後一個非頂點值
此時3位於堆頂不滿堆的性質,則需調整繼續調整
圖五 這個排序過程
這樣整個區間便已經有序了。 從上述過程可知,堆排序其實也是一種選擇排序,是一種樹形選擇排序。只不過直接選擇排序中,爲了從R[1...n]中選擇最大記錄,需比較n-1次,然後從R[1...n-2]中選擇最大記錄需比較n-2次。事實上這n-2次比較中有很多已經在前面的n-1次比較中已經做過,而樹形選擇排序恰好利用樹形的特點保存了部分前面的比較結果,因此可以減少比較次數。對於n個關鍵字序列,最壞情況下每個節點需比較log2(n)次,因此其最壞情況下時間複雜度爲nlogn。堆排序爲不穩定排序,不適合記錄較少的排序。
C語言實現
#include <iostream>
#include <algorithm>
using namespace std;
void HeapAdjust(int *a,int i,int size) //調整堆
{
int lchild = 2*i; //i的左孩子節點序號
int rchild = 2*i+1; //i的右孩子節點序號
int max = i; //臨時變量
if(i <= size/2) //如果i是葉節點就不用進行調整
{
if(lchild <= size&& a[lchild] > a[max])
{
max = lchild;
}
if(rchild <= size&& a[rchild]>a[max])
{
max=rchild;
}
if(max != i)
{
swap(a[i],a[max]);
HeapAdjust(a,max, size); //避免調整之後以max爲父節點的子樹不是堆
}
}
}
void BuildHeap(int *a, int size) //建立堆
{
int i;
for(i=size/2; i>=1;i--) //非葉節點最大序號值爲size/2
{
HeapAdjust(a, i,size);
}
}
void HeapSort(int *a, int size) //堆排序
{
int i;
BuildHeap(a, size);
for(i=size; i>=1;i--)
{
swap(a[1],a[i]); //交換堆頂和最後一個元素,即每次將剩餘元素中的最大者放到最後面
HeapAdjust(a,1,i-1); //重新調整堆頂節點成爲大頂堆
}
}
int main(int argc, char *argv[])
{
inta[]={0,4,1,3,2,16,9,10,14,8,7}; // 首位不用
int size = 10;
HeapSort(a, size);
for(inti=1;i<=size;i++)
cout<<a[i]<<" ";
cout << endl;
return 0;
}
Java語言實現
package com.ali.demo;
public class HeapSortDemo {
public void HeapSort(inta[], int size){
BuildHeap(a,size);
int temp = 0;
for(int i = size;i > 0; i--){
temp = a[i];
a[i] = a[1];
a[1] = temp;
HeapAdjust(a,1, i-1);
}
}
public void BuildHeap(inta[], int size) {
for(int i =size/2; i > 0; i--){
HeapAdjust(a,i, size);
}
}
private voidHeapAdjust(int[] a, int i, int size) {
int l = i*2;
int r = l + 1;
int t = i;
if(i <=size/2){
if(l <=size && a[t] < a[l]){
t = l;
}
if(r <=size && a[t] < a[r]){
t = r;
}
if(t != i){
inttemp = a[t];
a[t] =a[i];
a[i] =temp;
HeapAdjust(a,t, size);
}
}
}
public static voidmain(String[] args) {
inta[]={0,4,1,3,2,16,9,10,14,8,7};
(newHeapSortDemo()).HeapSort(a, 10);
for(int c:a){
System.out.print(c+"\t");
}
System.out.println();
}
}
參考:堆排序 作者:海子 出處:http://www.cnblogs.com/dolphin0520/ 說明:筆者原意爲傳播知識,若有侵權行爲,請告知本文筆者,筆者會及時處理。