【數據結構】—— 6、優先隊列和堆

1、優先隊列

優先隊列和其實是隊列的一種

  • 普通隊列:先進先出;後進先出
  • 優先隊列:出隊順序和入隊順序無關;和優先級相關
2、堆

堆本身也是一棵樹,其實堆也有很多種,我們在這裏主要使用二叉樹來表示堆,說白了,
二叉堆就是滿足一些特殊性質的二叉樹:

  • 二叉堆是一棵完全二叉樹

  • 堆中某個節點的值總是不大於其父節點的值(所以也叫做最大堆),注意:層次大的元素值不一定小於層次小的元素

  • 滿二叉樹:滿二叉樹就是對於整顆樹來說,除了葉子節點,所有的節點左右孩子都不爲空如下圖

  • 完全二叉樹:不一定是滿二叉樹,但它不滿的那一部分,也就是缺失節點的那一部分一定是在整顆樹的右下側。對於一個滿二叉樹來講,一個樹有多少層,節點有多少個其實是固定的(如一層有1個,二層有3個,三層6個),但很多時候我們的元素數量不符合滿二叉樹的元素數量,所以我們當一層滿了後再進入下一層,且從左向右排列元素(即元素一層一層的放)

  • 二叉樹可以和二叉樹一樣使用左右節點的方式來實現,但是我們可以看到我們現在是一層一層按順序排放的,所以我們可以用更巧妙的方式來實現 (數組)

public class MaxHeap<E extends Comparable<E>> {

    private Array<E> data;
    public MaxHeap(int capacity){
        data = new Array<>(capacity);
    }
    public MaxHeap(){
        data = new Array<>();
    }
    // 返回堆中的元素個數
    public int size(){
        return data.getSize();
    }
    // 返回一個布爾值, 表示堆中是否爲空
    public boolean isEmpty(){
        return data.isEmpty();
    }
    // 返回完全二叉樹的數組表示中,一個索引所表示的元素的父親節點的索引
    private int parent(int index){
        if(index == 0)
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        return (index - 1) / 2;
    }
    // 返回完全二叉樹的數組表示中,一個索引所表示的元素的左孩子節點的索引
    private int leftChild(int index){
        return index * 2 + 1;
    }
    // 返回完全二叉樹的數組表示中,一個索引所表示的元素的右孩子節點的索引
    private int rightChild(int index){
        return index * 2 + 2;
    }
}

向堆中添加元素和Shift Up

現在我們要添加元素52,先添加到索引爲10的位置,然而現在不滿足二叉堆的性質,所以要調整52的位置,所以讓52和自己的父親節點依次與父親節點相比較,如52大於父親節點,就交換52與父親節點的位置-------Shift up

	// 向堆中添加元素
    public void add(E e){
        data.addLast(e);
        siftUp(data.getSize() - 1); // 傳入它的索引
    }

    private void siftUp(int k){
        while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){
            data.swap(k, parent(k));
            k = parent(k);
        }
    }

從堆中取出元素(最大值)和Shift Down

我們先取出最大值62,因爲將兩個子樹融合成一棵樹比較複雜,所以我們可以把最小值16換到根節點,然後在採用shift-down操作,

shift-down:把16與左右兩個孩子節點相比較,選擇兩個孩子中最大的那個元素,如果最大的那個元素比16大,則將16與它交換,很顯然52與16做交換

然後繼續與41做交換

因爲16比15大,所以就完成啦

我們來看下代碼:

 // 看堆中的最大元素
    public E findMax(){
        if(data.getSize() == 0)
            throw new IllegalArgumentException("Can not findMax when heap is empty.");
        return data.get(0);
    }

    // 取出堆中最大元素
    public E extractMax(){

        E ret = findMax(); // 找到最大的元素(根元素)

        data.swap(0, data.getSize() - 1); // 交換0下標元素和最後一位元素
        data.removeLast(); // 刪除原0下標元素
        siftDown(0);

        return ret;
    }

    private void siftDown(int k){

        while(leftChild(k) < data.getSize()) { // 葉子節點
            int j = leftChild(k); // 在此輪循環中,data[k]和data[j]交換位置
            // k左右孩子中較大的一個
            if( j + 1 < data.getSize() &&
                    data.get(j + 1).compareTo(data.get(j)) > 0 ) {
                j++;   // data[j] 是 leftChild 和 rightChild 中的最大值
            }
            
            if(data.get(k).compareTo(data.get(j)) >= 0 )
                break;

            data.swap(k, j);
            k = j;
        }
    }
基於堆的優先隊列
public class PriorityQueue<E extends Comparable<E>> implements Queue<E> {

    private MaxHeap<E> maxHeap;
    public PriorityQueue(){
        maxHeap = new MaxHeap<>();
    }

    @Override
    public int getSize() {
        return maxHeap.size();
    }

    @Override
    public boolean isEmpty() {
        return maxHeap.isEmpty();
    }

    @Override
    public void enqueue(E e) {
        maxHeap.add(e);
    }

    @Override
    public E dequeue() {
        return maxHeap.extractMax();
    }

    @Override
    public E getFront() {
        return maxHeap.findMax();
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章