1. 程式人生 > >Java實現堆的封裝,進行插入,調整,刪除堆頂以完成堆排序例項

Java實現堆的封裝,進行插入,調整,刪除堆頂以完成堆排序例項

簡介

堆對於排序演算法是一個比較常用的資料結構,下面我就使用Java語言來實現這一演算法

首先,我們需要知道堆的資料結構的形式,其實就是一個特殊的二叉樹。但是這個二叉樹有一定的特點,除了是完全二叉樹以外,對於最大堆而言,堆頂元素的值是最大的,而且對於堆的每一個子樹也是一個小一號的最大堆;同樣對於最小堆,性質相反就可以了。

我以最大堆為例:
要實現堆的初始化操作,就是先按照給定的元素建立一棵完全二叉樹,然後從末尾節點進行不斷地調整的過程。調整的原則是:比較要進行放置的當前節點與其父節點的數值的大小,若要進行放置的當前節點的值小於其父節點,那麼當前節點所在位置符合最大堆的定義,要進行放置的當前節點放在此處是比較合適的;如果要進行放置的當前節點的值大於其父節點的值,那說明放在當前節點是不合適的,那麼就需要將當前節點的值與其父節點的值進行交換,然後原父節點變為新的要進行放置的當前節點。迴圈比較;終止條件就是當前節點沒有父節點,但此時的調整也許並沒有結束,我們只需要讓堆頂元素為要插入的值即可。至此,最大堆的插入和調整過程結束。
程式碼如下:

public boolean insert(int x){
        if(currentSize==MAXSIZE){
            System.out.println("Sorry,this heap is full!");
            return false;
        }
        //如果堆不滿的話
        currentSize++;
        int flag=currentSize-1;
        while(flag>0){
            int parent=(flag-1)/2;
            if
(heap[parent]>x){ heap[flag]=x; return true; }else{ heap[flag]=heap[parent]; flag=parent; } } heap[0]=x; return true; }

siftDown過程:給定一個節點的位置,對其進行調整,使之符合最大堆的定義,這個過程就是我們要實現的過程。調整原則如下:
對於當前節點i而言,其孩子節點的下標滿足左節點為2*i+1,右節點為2*i+2;在進行調整的過程中,只需要比較當前節點與其子節點中最大的節點進行調整即可。具體的程式碼邏輯可在程式碼中看到:

public void siftDown(int flag){
        int want=flag;
        int x=heap[flag];

        while(want<currentSize){
            int lChild=2*want+1;
            int rChild=2*want+2;
            int MAXChildNumber;
            if(lChild>currentSize){  //沒有孩子節點
                heap[want]=x;
            }else{                   //有兩個孩子節點
                if(lChild<currentSize){
                    MAXChildNumber=heap[lChild]>heap[rChild]?lChild:rChild;
                }else{
                    MAXChildNumber=lChild;
                }
                if(heap[MAXChildNumber]<x){
                    heap[want]=x;return;
                }else{
                    heap[want]=heap[MAXChildNumber];
                    want=MAXChildNumber;
                }
            }
        }

    }

堆頂元素的刪除,我們對堆的操作基本桑就是為了獲得這個堆的最值,那麼毫無疑問,堆頂元素就是我們要研究的物件。下面是程式碼邏輯:

public int deleteTop(){
        if(currentSize<0){
            System.out.println("Sorry, this heap is empty!");
            return -1;
        }
        int target=heap[0];
        int substitute=heap[currentSize-1];
        this.currentSize--;
        heap[0]=substitute;
        siftDown(0);
        return target;
    }

下面是詳細的程式碼

package test.maxHeap;

public class MaxHeap {

    private int []heap ;
    private int currentSize;
    private static int MAXSIZE ;

    public MaxHeap(int n){
        heap=new int[n];
        currentSize=0;
        MAXSIZE=n;
    }

    public boolean insert(int x){
        if(currentSize==MAXSIZE){
            System.out.println("Sorry,this heap is full!");
            return false;
        }
        //如果堆不滿的話
        currentSize++;
        int flag=currentSize-1;
        while(flag>0){
            int parent=(flag-1)/2;
            if(heap[parent]>x){
                heap[flag]=x;
                return true;
            }else{
                heap[flag]=heap[parent];
                flag=parent;
            }
        }
        heap[0]=x;
        return true;
    }

    public void siftDown(int flag){
        int want=flag;
        int x=heap[flag];

        while(want<currentSize){
            int lChild=2*want+1;
            int rChild=2*want+2;
            int MAXChildNumber;
            if(lChild>currentSize){  //沒有孩子節點
                heap[want]=x;
            }else{                   //有兩個孩子節點
                if(lChild<currentSize){
                    MAXChildNumber=heap[lChild]>heap[rChild]?lChild:rChild;
                }else{
                    MAXChildNumber=lChild;
                }
                if(heap[MAXChildNumber]<x){
                    heap[want]=x;return;
                }else{
                    heap[want]=heap[MAXChildNumber];
                    want=MAXChildNumber;
                }
            }
        }

    }

    public int deleteTop(){
        if(currentSize<0){
            System.out.println("Sorry, this heap is empty!");
            return -1;
        }
        int target=heap[0];
        int substitute=heap[currentSize-1];
        this.currentSize--;
        heap[0]=substitute;
        siftDown(0);
        return target;
    }

}

好了,編碼已經完成。下面我們就要檢驗一下是否正確吧。

public class MaxHeapTest {

    public static void main(String []args){
        MaxHeap maxHeap=new MaxHeap(7);
        for(int i=1;i<=7;i++){
            maxHeap.insert(i);
        }
        for(int i=0;i<7;i++){
            System.out.print(maxHeap.deleteTop()+"   ");
        }
        System.out.println("\n");
    }
}

接下來是程式的執行結果:

7   6   5   4   3   2   1   
//可見,對於最大堆,刪除堆頂的操作實際上就是完成了對堆的排序任務,也證明了我們的程式碼是正確的

總結:
堆的操作很重要,我們更要學會對於堆的應用,這樣的資料結構才能使得程式的執行更加的高效和流暢。對於最小堆,我們只需要在插入方法,sift方法內稍加修改即可(也就是將值的代銷變換關係進行調整)。這樣就同樣能實現最小堆的建立和相關的操作了。
程式碼中可能存在不太恰當地地方,希望大家予以批評指正,期待與你們共同進步!