數據結構學習——堆

1 基本介紹

堆數據結構是一種數組對象,它可以被視爲一顆完全二叉樹。堆的訪問可以通過三個函數來進行即,

parent(i)
    return floor(i/2);
left(i)
    return 2i;
right(i)
    return 2i + 1;

left操作可以通過一步左移操作完成,right操作可以通過左移並在地位+1實現,parent操作則可以通過把i右移一位得到。在實現中通常會使用宏或者內聯函數來實現這三個操作。

二叉堆有兩種,最大堆和最小堆。對於最大堆有

A[i] >= A[left(i)] && A[i] >= A[right(i)]

最小堆則是

A[i] <= A[left(i)] && A[i]<= A[right(i)]

最堆排序算法中,使用的是最大堆,最小堆通常在構造優先隊列時使用。堆可以被看成是一棵樹,結點在堆中的高度定義爲從本結點到葉子的最長簡單下降路徑上邊的數目;定義堆的高度爲樹根的高度。


2 原理部分

下面就關於堆的幾個基本函數做一下說明:

maxHeapify(),運行時間爲O(lgn)用於保持堆的性質;

bulidMaxHeap(),以線性時間運行,可以在無序的輸入數組基礎上構造出最大堆;

heapSort(),運行時間爲O(nlgn),對一個數組原地進行排序。


2.1 保持堆的性質

其輸入爲一個數組A和下標i,當maxHeapify被調用時,我們假定以leftIi)和right(i)爲根的兩棵二叉樹都是最大堆,但這時A[i]可能小於其子女,這樣就違反了最大堆的性質。maxHeapify讓A[i]在最大堆中“下降”使以i爲根的子樹成爲最大堆。僞代碼如下所示。

maxHeapify(A,i)
    l <- left(i)
    r <- right(i)
    if l <= heap-size[A] and A[l] > A[i]
        then largest <- l
        else largest <- i
    if r <= heap-size[A] and A[r] > A[largest]
        then largest <- r
    if largest != i
        then exchange A[i] <-> A[largest]  // 交換i和比它大的那個子結點
            maxHeapify(A,largest);         // 遞歸調用

2.2 建堆

我們可以自底向上用maxHeapify來將一個數組A[1...n](此處 n = length[A])變成一個最大堆。子數組A[floor(n/2) + 1)..n]中的元素都是樹中的葉子節點,因此每個都可以看作是一個只含一個元素的堆。buildMAXHeap對樹中每一個其他節點都調用一次maxHeapify。僞代碼如下所示。
buildMaxHeap(A)
    heap-size[A] <- length[A]
    for i <- floor(length[A] / 2) downto 1
        do max-heapify(A,i)

2.3 堆排序算法

開始時,堆排序算法先用buildMaxHeap將輸入數組A[1..n](此處 n = length[A])構造成一個最大堆,因爲數組中的最大元素在根A[1],則可以通過把它與A[n]互換來達到最終正確的位置。現在,如果從堆中“去掉”節點n(通過減小heap-size[A]),可以很容易的將A[1..n-1]建成最大堆,原來根的子女仍是最大堆,而新的根元素可能違背了最大堆性質。這是調用maxHeapify(A,1)就可以保持這一性質,在A[1..(n-1)]構造出最大堆。堆排序算法不斷重複這個過程,堆的大小由n-1一直降到2.
heapSort(A)
    buildMaxHeap(A)
    for i <- length[A] downto 2
        do exchange A[1] <-> A[i]
            heap-size[A] <- heap-size[A] - 1
            maxHeapify(A,1)

雖然堆排序算法是一個很nice的算法,但是在實際應用中,快速排序的一個好的實現往往優於堆排序。

3 java實現

3.1 數據結構

Heap.java
import java.io.Serializable;

/**
 * Date: 2014/8/17
 * Time: 16:02
 */
public class Heap implements Serializable{
    private int heapLength;
    private int [] data;

    public int getHeapLength() {
        return heapLength;
    }

    public void setHeapLength(int heapLength) {
        this.heapLength = heapLength;
    }

    public int[] getData() {
        return data;
    }

    public void setData(int[] data) {
        this.data = data;
    }
}

3.2 操作類

HeapSort.java

/**
 * Created with IntelliJ IDEA.
 * Date: 2014/8/17
 * Time: 15:39
 */
public class HeapSort {
    public final static int getLeft(int i) {
        return i << 1;
    }

    public final static int getRight(int i) {
        return (i << 1) + 1;
    }

    public final static int getParent(int i) {
        return i >> 1;
    }

    /**
     * 保持堆的性質
     *
     * @param heap
     * @param i
     */
    public static void maxHeapify(Heap heap, int i) {
        if (null == heap || null == heap.getData() || heap.getData().length <= 0 || i < 0)
            return;
        int l = getLeft(i);
        int r = getRight(i);
        int largest = 0;
        if (l < heap.getHeapLength() && heap.getData()[l] > heap.getData()[i])
            largest = l;
        else
            largest = i;
        if (r < heap.getHeapLength() && heap.getData()[r] > heap.getData()[largest])
            largest = r;
        if (largest != i) {
            int tmp = heap.getData()[i];
            heap.getData()[i] = heap.getData()[largest];
            heap.getData()[largest] = tmp;
            maxHeapify(heap, largest);
        }
    }

    /**
     * 建立最大堆
     *
     * @param array
     * @return
     */
    public static Heap bulidMaxHeap(int[] array) {
        if (null == array || array.length <= 0)
            return null;
        Heap heap = new Heap();
        heap.setData(array);
        heap.setHeapLength(array.length);
        for (int i = (array.length >> 1); i >= 0; i--)
            maxHeapify(heap, i);
        return heap;
    }

    /**
     * 堆排序
     *
     * @param array
     */
    public static void heapSort(int[] array) {
        if (null == array || array.length <= 0)
            return;
        Heap heap = bulidMaxHeap(array);
        if (null == heap)
            return;
        for (int i = heap.getHeapLength() - 1; i > 0; i--) {
            int tmp = heap.getData()[0];
            heap.getData()[0] = heap.getData()[i];
            heap.getData()[i] = tmp;
            heap.setHeapLength(heap.getHeapLength() - 1);
            maxHeapify(heap, 0);
        }
    }
}

3.3 測試類
Main.java
public class Main {

    public static void main(String[] args) {
        int a[] = {9, 3, 4, 1, 5, 10, 7};
        System.out.println("Hello World!");
        Sort sort = new Sort();
//        sort.bubbleSort(a);
//        sort.selectSort(a);
//        sort.insertionSort(a);
        HeapSort.heapSort(a);
        for (int i = 0; i < a.length; i++)
            System.out.println(a[i]);
    }
}


補充的筆記
Top k問題可以使用堆來解決:
例如從一個長度爲N的數組A裏面尋找前k大的k個數(k << N),則可以維護一個堆的長度爲k的最小堆,首先使用A的前k個元素來初始化這個最小堆,然後繼續遍歷k+1到N,如果某一個元素比最小堆的根大,則讓它入堆,最後遍歷完成後即可找到前k大的k個數據。這樣下來,總費時O(k*logk+(n-k)*logk)=O(n*logk)
發佈了53 篇原創文章 · 獲贊 15 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章