1. 程式人生 > >二叉樹(堆和優先佇列)

二叉樹(堆和優先佇列)

堆是一種特殊的二叉樹。

最小值堆:最小值堆的特性。

對於堆的任意非葉節點K,K的值總是小於或者等於左右子節點。

K <= 左節點;K <= 又節點;

堆例項:

堆實際上是一個完全二叉樹(若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。)可以用陣列表示。

堆中儲存是區域性有序的。

建立堆:

交換建立堆,交換有2,1)向下交換,2)向上交換。

1)向下交換。

假設根的左右子樹都已是堆,並且根元素為R,此時有2種情況。

  (1)R的值小於或者等於左右子結點,此時堆完成。

  (2)R的值大於其中一個或者兩個子女,此時R應該與兩個子女中較小的一個交換,結果得到一個新堆,

R到達新位置後繼續和其兩個子女,如果R小於或者等於其左右子女,建堆完成,否則重複上一過程,直到

R小於或等於其左右子女,或R成為新的葉結點。

堆的陣列實現:

public abstract class AbstractHeap<T extends Comparable<T>> implements Heap<T>  {

    //最大堆大小
    private static final int MAX_SIZE = Integer.MAX_VALUE;

    //陣列大小
    protected int size;
    //堆中陣列個數
    protected int currentSize;
    //儲存堆的資料
    protected T[] data;

    AbstractHeap(){
        this(0,16,null);
    }

    AbstractHeap(T[] data){
        this(data.length,data.length,data);
    }

    AbstractHeap(int currentSize,int size,T[] data){
        this.currentSize = currentSize;
        this.size = size;
        this.data = data;
        if (data != null)
//根據給定陣列 建立堆
            buildHeap(data);
    }
}

用給定的陣列建立堆,從堆中第一個分支結點,從下往上網上交換建立堆:

    private void buildHeap(T[] t){
        if (t.length > 1){
            //從下到上第一個分支結點
            int pos = t.length / 2 - 1;
            for (int i = pos ; i >= 0 ; i -- ){
//向下交換
                siftDown(i);
            }
        }
    }

交換函式:

void siftDown(int pos) {
// pos為第一個分支結點位置
        int tmp = pos;
// j為其左結點
        int j = tmp * 2 + 1;
//儲存當前結點值
        T tmpNode = data[tmp];
        while ( j < currentSize){
//j < currentSize - 1 防止右結點不存在,j+1 = tmp *2 +1 ,data[j+1] 為右結點

            if (j < currentSize - 1 && data[j].compareTo(data[j+1]) == 1){
//若右結點值比較小,j++
                j++;
            }
            if (tmpNode.compareTo(data[j]) == 1){
//若當前結點值大於左右結點中最小的值,將最小的哪個結點值上移當前結點,
                data[tmp] = data[j];
//tmp 為上移結點下標
                tmp = j;
                j = j * 2 + 1;
            }
//如果當前結點值小於其左右子女中最小的,建堆完成,跳出迴圈
else break;
//將當前結點值 賦值給上移結點
            data[tmp] = tmpNode;
        }
    }

2)向上交換

在堆中新增值時,採用向上交換:

    public boolean insert(T t) {
        if (t == null){
            return false;
        }
        if (currentSize == size){
            return false;
        }
//直接將新增值放到陣列的最後一個位置,並向上交換
        data[currentSize] = t;
        siftUp(currentSize);
        currentSize ++;
        return true;
    }
    void siftUp(int pos) {
//當前位置下標
        int tmp = pos;
//父結點下標
        int j = (pos - 1) / 2;
//當前結點
        T tmpNode = data[tmp];
        while (j >= 0){
//如果當前結點值小於父結點,當前結點值向上移動,直到根結點或者當前結點值打於根結點值跳出迴圈
            if (tmpNode.compareTo(data[j]) == -1){
                data[tmp] = data[j];
                tmp = j;
                j = j / 2 - 1;
            }else break;
            tmpNode = data[tmp];
        }
    }

建立堆可以根據已有資料建立採用向下移動建立,也可以一個一個的插入值,採用向上移動的方法建立堆。

小結:

堆的特性,父結點的值小於左右子女結點值。

堆保持區域性有序性,可以用左優先佇列。

堆排序時間複雜度為O(n)