堆排序
堆排序是時(shí)間復(fù)雜度為O(N*logN)寨闹,空間復(fù)雜度為O(1)的算法,該算法是不穩(wěn)定的霸琴。
首先二叉堆是滿足如下條件的完全二叉樹:
1.父節(jié)點(diǎn)的值大(小)于等于左右子節(jié)點(diǎn)雄家,稱為大(小)頂堆;
2.每個(gè)節(jié)點(diǎn)都滿足1的條件;
如下:
有了這樣的堆,如果用于排序旺聚,取跟節(jié)點(diǎn)的值就行了织阳,然后再把移除取出值后的二叉樹再進(jìn)行一次堆化即可,然后就會(huì)發(fā)現(xiàn)最大(小)的值又在根節(jié)點(diǎn)了砰粹。這樣可以減少選擇排序里的重復(fù)比較唧躲。
1.考慮一下如何堆化一個(gè)數(shù)組
a.首先考慮如何把一個(gè)除根節(jié)點(diǎn)外已經(jīng)堆化的二叉樹的根節(jié)點(diǎn)放到合適的位置。
如圖碱璃,如何為79找到合適的位置:
堆化一個(gè)節(jié)點(diǎn)弄痹,為它找到合適位置的的方法如下(小頂堆):
/**
*table是待堆化的數(shù)組,i是需要堆化的那個(gè)根節(jié)點(diǎn),這里是輸入0嵌器,n是數(shù)組的長(zhǎng)度-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)前的值大于較小的那個(gè)子節(jié)點(diǎn)爽航,交換
if(tem > table[left]){
table[i] = table[left];
table[left] = tem;
i = left;
}else{
//如果當(dāng)前的值大于或者等于當(dāng)前的較小的值蚓让,說明到了合適的位置,終止
break;
}
//下一個(gè)左子樹的位置是當(dāng)前位置*2 + 1;
left = i * 2 + 1;
}
}
b.a中已經(jīng)堆化了一個(gè)根節(jié)點(diǎn)不滿足最小堆的二叉樹了讥珍,下面就是如何生成一個(gè)最小堆了:
首先历极,葉子節(jié)點(diǎn)沒有左右子節(jié)點(diǎn),所以是滿足1條件的衷佃,所以葉子節(jié)點(diǎn)的父節(jié)點(diǎn)就可以看成是a中的那個(gè)根節(jié)點(diǎn)趟卸,所以第一個(gè)堆化的節(jié)點(diǎn)應(yīng)該是從下往上第一個(gè)有葉子節(jié)點(diǎn)的節(jié)點(diǎn):i = n / 2 -1;
而一旦堆化了一個(gè)父節(jié)點(diǎn),那么父節(jié)點(diǎn)的父節(jié)點(diǎn)又滿足了a條件,可以繼續(xù)循環(huán)往下了锄列。
如圖,26,27可以看成滿足已經(jīng)堆化图云,那么第一個(gè)需要堆化的就是16,位置是5/2 -1 = 1
所以堆化一個(gè)數(shù)組的代碼如下:
public static void generateHeap(int[] table){
int n = table.length;
//i==0時(shí),即使到了根節(jié)點(diǎn)
for(int i = n / 2 - 1 ; i >= 0 ; i--){
sift(table,i,n - 1);
}
print(table);
}
2.堆化后的排序
一旦數(shù)組堆化后邻邮,排序就容易了竣况,直接取出table[0]的值,即是被選擇出來的最小值饶囚,然后把它放到數(shù)組的尾部帕翻,然后把原來尾部的那個(gè)數(shù)放到原來table[0]的位置
堆化后:
取出根節(jié)點(diǎn)值(最小的值)到末尾,同時(shí)把末尾值放到根節(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;
//這里后一個(gè)值i-1是因?yàn)楹竺娴闹凳桥判蚝蟮闹倒娑瑁粦?yīng)該再進(jìn)行堆化睬塌。
sift(table,0,i-1);
}
3.完整代碼:
//里面出現(xiàn)的英語。歇万。破eclipse無法打中文我會(huì)亂說咩揩晴。。
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("");
}
}