【算法】【只有自己看得懂系列】玩轉二叉堆

二叉堆的性質(以最小堆爲例)

  • 每個子節點必須小於等於根節點
  • 堆結構必須是一顆滿二叉樹

應用

  • 優先隊列
  • 預排序

Java實現

package mairuis.algorithm.heap;

/**
 * 最小堆
 * 性質:
 * 1.每個子節點都小於等於其父節點
 *
 * @author Mairuis
 * @date 2019/5/23
 */
public class MinHeap<T extends Comparable<T>> {
    private Comparable[] array;
    private int size;

    public MinHeap(int size) {
        this.array = new Comparable[size];
    }

    public MinHeap(Comparable[] array) {
        this.array = array;
        this.size = array.length;
    }


    public void insert(T element) {
        //在尾部插入然後上浮該節點
        this.set(size + 1, element);
        this.size += 1;
        this.swim(size);
    }

    public T getMin() {
        return (T) array[0];
    }

    public void deleteMin() {
        //刪除根節點,將最右邊的節點賦值到根節點
        this.swap(1, size);
        this.set(size, null);
        size -= 1;
        //下沉該節點維護堆的性質
        this.sink(1);
    }

    private void set(int index, T v) {
        array[index - 1] = v;
    }

    private void swim(int node) {
        int x = node;
        int parent = node >> 1;
        //判斷1:如果是根節點 node >> 1必定爲0,parent != 0 以此實現終止
        //判斷2:比較該節點與父節點,如果小於父節點則交換,維護性質
        while (parent != 0 && compare(x, parent)) {
            swap(x, parent);
            x = parent;
            parent = parent >> 1;
        }
    }

    private void sink(int node) {
        int least = node, root = node;
        //如果不是根節點則交換,然後遞歸的維護下一棵樹
        while (root <= size) {
            //找到樹中最大的節點
            int left = root << 1, right = (root << 1) + 1;
            if (left <= size && compare(left, root)) {
                least = left;
            }
            if (right <= size && compare(right, least)) {
                least = right;
            }
            if (least != root) {
                //將樹中最小的節點與根交換
                swap(root, least);
                //循環維護下一棵樹
                root = least;
            } else {
                break;
            }
        }
    }

    /**
     * 構建一個堆
     *
     * @param array
     * @param <T>
     * @return
     */
    public static <T extends Comparable<T>> MinHeap<T> buildHeap(Comparable<T>[] array) {
        MinHeap<T> heap = new MinHeap<>(array);
        //從size除以2開始,篩選掉葉子節點,然後從下往上遞歸的維護每棵樹的堆性質
        for (int i = heap.size >> 1; i > 0; i -= 1) {
            heap.sink(i);
        }
        return heap;
    }

    /**
     * 堆排序
     *
     * @param array
     * @param <T>
     * @return
     */
    public static <T extends Comparable<T>> T[] heapSort(T[] array) {
        MinHeap<T> heap = buildHeap(array);
        for (int i = heap.size; i > 1; i -= 1) {
            //將最小元素和最後一個元素交換位置
            heap.swap(1, i);
            //維護size字段,以保證最小元素在堆外
            heap.size -= 1;
            //將新的根下沉維護堆性質
            heap.sink(1);
        }
        return (T[]) heap.array;
    }


    private boolean compare(int a, int b) {
        return less(a, b);
    }

    private boolean less(int a, int b) {
        return array[a - 1].compareTo(array[b - 1]) < 0;
    }

    private void swap(int a, int b) {
        Comparable temp = array[a - 1];
        array[a - 1] = array[b - 1];
        array[b - 1] = temp;
    }

    public static void main(String[] e) {
//        MinHeap<Integer> heap = new MinHeap<>(1000);
//        int[] array = Sort.generalIntegers(1000);
//        for (int i = 0; i < array.length; i++) {
//            heap.insert(array[i]);
//        }
//        for (int i = 0; i < heap.size; i++) {
//            System.out.println(heap.getMin());
//            heap.deleteMin();
//        }

//        Integer[] data = new Integer[]{1, 3, 2, 5, 4};
//        MinHeap<Integer> heap = buildHeap(data);
//        System.out.println(heap.getMin());
//        heap.deleteMin();
//        System.out.println(heap.getMin());
//        heap.deleteMin();
//        System.out.println(heap.getMin());
//        heap.deleteMin();
//        System.out.println(heap.getMin());
//        heap.deleteMin();
//        System.out.println(heap.getMin());
//        heap.deleteMin();
//
//        Integer[] array = heapSort(new Integer[]{2, 1, 3, 4, 5});
//        for (int i = 0; i < array.length; i++) {
//            System.out.println(array[i]);
//        }
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章