堆的定義

堆是計算機科學中一類特殊的數據結構的統稱,堆通常可以被看做是一棵完全二叉樹的數組對象。

堆的特性:

1.它是完全二叉樹,除了樹的最后一層結點不需要是滿的,其它的每一層從左到右都是滿的,如果最后一層結點不 是滿的,那么要求左滿右不滿。

image-20210824094210739

2.它通常用數組來實現。

具體方法就是將二叉樹的結點按照層級順序放入數組中,根結點在位置1,它的子結點在位置2和3,而子結點的子 結點則分別在位置4,5,6和7,以此類推。(不使用數組的第一個位置)

image-20210824094533144

如果一個結點的位置為k,則它的父結點的位置為[k/2],而它的兩個子結點的位置則分別為2k和2k+1。這樣,在不 使用指針的情況下,我們也可以通過計算數組的索引在樹中上下移動:從a[k]向上一層,就令k等於k/2,向下一層就 令k等於2k或2k+1。

3.每個結點都大於等於它的兩個子結點。這里要注意堆中僅僅規定了每個結點大於等於它的兩個子結點,但這兩個 子結點的順序並沒有做規定,跟我們之前學習的二叉查找樹是有區別的。

上浮和下沉算法

堆的操作會首先進行一些簡單的改動,打破堆的狀態,然后再遍歷堆並按照要求將堆的狀態恢復,我們稱這個過程叫做堆的有序化。

在有序化的過程中,我們會遇到兩種情況。

  • 當節點的優先級上升時(在堆底加入一個新的元素時),我們需要由下至上恢復堆的順序(即上浮 swim)。
image-20210824103949997
  • 當節點的優先級下降(例如將根節點替換為一個較小的元素時),我們需要由上至下恢復堆的順序(即下沉 sink)。
image-20210824104403467

上浮算法swim實現

先實現堆的比較和交換方法:

public class Heap<T extends Comparable<T>> {

    private T[] items;

    private int N;

    public Heap(int capacity) {
        this.items = (T[])new Comparable[capacity+1];
        this.N = 0;
    }
	//比較i位置元素是否比j位置元素小
    private boolean less(int i, int j) {
        return items[i].compareTo(items[j]) < 0;
    }
	//交換i位置元素和j位置元素
    private void exch(int i, int j) {
        T temp = items[i];
        items[i] = items[j];
        items[j] = temp;
    }
}

因為k位置的父節點的位置是k/2,所以只要循環往上比較並交換就行了

    /**
     * @author wen.jie
     * @date 2021/8/24 9:59
     * 上浮算法
     */
    private void swim(int k) {
        while (k > 1 && less(k/2, k)){
            exch(k/2, k);
            k = k/2;
        }
    }

下沉算法sink實現

下沉算法中:我們需要通過父節點和它的兩個子節點中的較大者交換來恢復堆。所以我們同樣需要循環比較並交換,代碼實現如下:

    /**
     * @author wen.jie
     * @date 2021/8/24 9:59
     * 下沉算法
     */
    private void sink(int k) {
        while (2*k <= N){
            int j = 2*k;
            if(j < N && less(j, j+1)) j++;
            if(!less(k, j)) break;
            exch(k, j);
            k = j;
        }
    }

堆的插入和刪除最大元素實現

堆的插入

我們將新元素加到數組末尾,增加堆的大小並讓新元素上浮到合適的位置:

    /**
     * @author wen.jie
     * @date 2021/8/24 10:59
     * 向堆中添加一個元素
     */
    public void insert(T t) {
        items[++N] = t;
        swim(N);
    }

刪除最大元素

我們從數組頂端刪除最大的元素,並將數組的最后一個元素放到頂端,減小堆的大小並讓這個元素下沉到合適位置。

代碼實現如下:

    /**
     * @author wen.jie
     * @date 2021/8/24 10:59
     * 刪除堆中最大的元素
     */
    public T delMax() {
        T t = items[1];
        items[1] = null;
        exch(N--, 1);
        sink(1);
        return t;
    }

下面提供完整的堆數據結構代碼:

public class Heap<T extends Comparable<T>> {

    private T[] items;

    private int N;

    public Heap(int capacity) {
        this.items = (T[])new Comparable[capacity+1];
        this.N = 0;
    }

    //比較i位置元素是否比j位置元素小
    private boolean less(int i, int j) {
        return items[i].compareTo(items[j]) < 0;
    }

    //交換i位置元素和j位置元素
    private void exch(int i, int j) {
        T temp = items[i];
        items[i] = items[j];
        items[j] = temp;
    }

    /**
     * @author wen.jie
     * @date 2021/8/24 10:59
     * 向堆中添加一個元素
     */
    public void insert(T t) {
        items[++N] = t;
        swim(N);
    }

    /**
     * @author wen.jie
     * @date 2021/8/24 9:59
     * 上浮算法
     */
    private void swim(int k) {
        while (k > 1 && less(k/2, k)){
            exch(k/2, k);
            k = k/2;
        }
    }

    /**
     * @author wen.jie
     * @date 2021/8/24 9:59
     * 下沉算法
     */
    private void sink(int k) {
        while (2*k <= N){
            int j = 2*k;
            if(j < N && less(j, j+1)) j++;
            if(!less(k, j)) break;
            exch(k, j);
            k = j;
        }
    }

    /**
     * @author wen.jie
     * @date 2021/8/24 10:59
     * 刪除堆中最大的元素
     */
    public T delMax() {
        T t = items[1];
        items[1] = null;
        exch(N--, 1);
        sink(1);
        return t;
    }
}

堆排序

基於堆,我們可以實現一種經典而優雅的排序算法---堆排序。

堆排序實現原理:

1.構造堆;

2.得到堆頂元素,這個值就是最大值;

3.交換堆頂元素和數組中的最后一個元素,此時所有元素中的最大元素已經放到合適的位置;

4.對堆進行調整,重新讓除了最后一個元素的剩余元素中的最大值放到堆頂;

5.重復2~4這個步驟,直到堆中剩一個元素為止。

代碼實現:

/**
 * @author wen.jie
 * @date 2021/8/24 14:07
 */
public class HeapSort {

    public static void sort(Comparable[] pq) {
        int n = pq.length;

        // 構造堆
        for (int k = n/2; k >= 1; k--)
            //從長度一半處開始(跳過大小為1的堆)
            sink(pq, k, n);

        // 排序
        int k = n;
        while (k > 1) {
            //交換第一個最大的元素和第k個元素
            exch(pq, 1, k--);
            //下沉重排堆,就可以得到第1個到第k-1個最大的元素
            sink(pq, 1, k);
        }
    }

    private static void sink(Comparable[] pq, int k, int n) {
        while (2*k <= n) {
            int j = 2*k;
            if (j < n && less(pq, j, j+1)) j++;
            if (!less(pq, k, j)) break;
            exch(pq, k, j);
            k = j;
        }
    }

    private static boolean less(Comparable[] pq, int i, int j) {
        return pq[i-1].compareTo(pq[j-1]) < 0;
    }

    private static void exch(Object[] pq, int i, int j) {
        Object swap = pq[i-1];
        pq[i-1] = pq[j-1];
        pq[j-1] = swap;
    }

}

測試:

    @Test
    public void test(){
        String[] arr =  new String[]{"S", "O", "R", "T", "E", "X", "A", "M", "P", "L", "E"};
        HeapSort.sort(arr);
        System.out.println(Arrays.toString(arr));
    }

image-20210824144329903


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM