堆排序
堆排序是時間復(fù)雜度為O(N*logN),空間復(fù)雜度為O(1)的算法,該算法是不穩(wěn)定的。
首先二叉堆是滿足如下條件的完全二叉樹:
1.父節(jié)點(diǎn)的值大(小)于等于左右子節(jié)點(diǎn),稱為大(小)頂堆;
2.每個節(jié)點(diǎn)都滿足1的條件;
如下:
有了這樣的堆,如果用于排序,取跟節(jié)點(diǎn)的值就行了,然后再把移除取出值后的二叉樹再進(jìn)行一次堆化即可,然后就會發(fā)現(xiàn)最大(小)的值又在根節(jié)點(diǎn)了。這樣可以減少選擇排序里的重復(fù)比較。
1.考慮一下如何堆化一個數(shù)組
a.首先考慮如何把一個除根節(jié)點(diǎn)外已經(jīng)堆化的二叉樹的根節(jié)點(diǎn)放到合適的位置。
如圖,如何為79找到合適的位置:
堆化一個節(jié)點(diǎn),為它找到合適位置的的方法如下(小頂堆):
/**
*table是待堆化的數(shù)組,i是需要堆化的那個根節(jié)點(diǎn),這里是輸入0,n是數(shù)組的長度-1
*
*
**/
public static void sift(int[] table,int i ,int n){
int tem = table[i] ;
//左子節(jié)點(diǎn)的位置
int left = i * 2 + 1;
//如果存在左子樹,那么循環(huán)繼續(xù)
while(left <= n){
if(left + 1 <= n && table[left+1] < table[left]){ //如果右子節(jié)點(diǎn)存在并且右子節(jié)點(diǎn)小于左子節(jié)點(diǎn),比較的值變成了右子節(jié)點(diǎn)
left ++; //use right to compare;
}
//如果當(dāng)前的值大于較小的那個子節(jié)點(diǎn),交換
if(tem > table[left]){
table[i] = table[left];
table[left] = tem;
i = left;
}else{
//如果當(dāng)前的值大于或者等于當(dāng)前的較小的值,說明到了合適的位置,終止
break;
}
//下一個左子樹的位置是當(dāng)前位置*2 + 1;
left = i * 2 + 1;
}
}
b.a中已經(jīng)堆化了一個根節(jié)點(diǎn)不滿足最小堆的二叉樹了,下面就是如何生成一個最小堆了:
首先,葉子節(jié)點(diǎn)沒有左右子節(jié)點(diǎn),所以是滿足1條件的,所以葉子節(jié)點(diǎn)的父節(jié)點(diǎn)就可以看成是a中的那個根節(jié)點(diǎn),所以第一個堆化的節(jié)點(diǎn)應(yīng)該是從下往上第一個有葉子節(jié)點(diǎn)的節(jié)點(diǎn):i = n / 2 -1;
而一旦堆化了一個父節(jié)點(diǎn),那么父節(jié)點(diǎn)的父節(jié)點(diǎn)又滿足了a條件,可以繼續(xù)循環(huán)往下了。
如圖,26,27可以看成滿足已經(jīng)堆化,那么第一個需要堆化的就是16,位置是5/2 -1 = 1
所以堆化一個數(shù)組的代碼如下:
public static void generateHeap(int[] table){
int n = table.length;
//i==0時,即使到了根節(jié)點(diǎn)
for(int i = n / 2 - 1 ; i >= 0 ; i--){
sift(table,i,n - 1);
}
print(table);
}
2.堆化后的排序
一旦數(shù)組堆化后,排序就容易了,直接取出table[0]的值,即是被選擇出來的最小值,然后把它放到數(shù)組的尾部,然后把原來尾部的那個數(shù)放到原來table[0]的位置
堆化后:
取出根節(jié)點(diǎn)值(最小的值)到末尾,同時把末尾值放到根節(jié)點(diǎn),如下
再為根節(jié)點(diǎn)35找到合適的位置,即是1a的堆化根節(jié)點(diǎn)。
public static void heapSort(int[] table){
generateHeap(table);
int tem;
for(int i = table.length - 1 ; i >= 0 ; i--){
tem = table[i];
table[i] = table[0];
table[0] = tem;
//這里后一個值i-1是因?yàn)楹竺娴闹凳桥判蚝蟮闹担粦?yīng)該再進(jìn)行堆化。
sift(table,0,i-1);
}
3.完整代碼:
//里面出現(xiàn)的英語。。破eclipse無法打中文我會亂說咩。。
public class HeapSort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] table = new int[]{81,49,38,65,548,548,1587,97,76,9,84,13};
heapSort(table);
print(table);
}
//change a num from high to low
public static void sift(int[] table,int i ,int n){
int tem = table[i] ;
int left = i * 2 + 1;
while(left <= n){
if(left + 1 <= n && table[left+1] < table[left]){ //if right is smaller ,use right to compare
left ++; //use right to compare;
}
if(tem > table[left]){
table[i] = table[left];
table[left] = tem;
i = left;
}else{
break;
}
left = i * 2 + 1;
}
}
//generage a heap
public static void generateHeap(int[] table){
int n = table.length;
for(int i = n / 2 - 1 ; i >= 0 ; i--){
sift(table,i,n - 1);
}
print(table);
}
public static void heapSort(int[] table){
generateHeap(table);
int tem;
for(int i = table.length - 1 ; i >= 0 ; i--){
tem = table[i];
table[i] = table[0];
table[0] = tem;
sift(table,0,i-1);
}
}
public static void print(int[] table){
for(int i : table){
System.out.print(i + " ");
}
System.out.println("");
}
}