堆排序

堆排序

堆排序是時間復(fù)雜度為O(N*logN),空間復(fù)雜度為O(1)的算法,該算法是不穩(wěn)定的。
首先二叉堆是滿足如下條件的完全二叉樹:

1.父節(jié)點(diǎn)的值大(小)于等于左右子節(jié)點(diǎn),稱為大(小)頂堆;
2.每個節(jié)點(diǎn)都滿足1的條件;

如下:

堆化后.png

有了這樣的堆,如果用于排序,取跟節(jié)點(diǎn)的值就行了,然后再把移除取出值后的二叉樹再進(jìn)行一次堆化即可,然后就會發(fā)現(xiàn)最大(小)的值又在根節(jié)點(diǎn)了。這樣可以減少選擇排序里的重復(fù)比較。

1.考慮一下如何堆化一個數(shù)組

a.首先考慮如何把一個除根節(jié)點(diǎn)外已經(jīng)堆化的二叉樹的根節(jié)點(diǎn)放到合適的位置。

如圖,如何為79找到合適的位置:

調(diào)整根節(jié)點(diǎn)到合適的堆位置.png

堆化一個節(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

未堆化的二叉樹.png

所以堆化一個數(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]的位置
堆化后:

堆化后.png

取出根節(jié)點(diǎn)值(最小的值)到末尾,同時把末尾值放到根節(jié)點(diǎn),如下

排序.png

再為根節(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("");
    }

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容