算法--勝者樹-敗者樹


勝者樹與敗者樹  

 

       勝者樹和敗者樹都是完全二叉樹,是樹形選擇排序的一種變型。每個葉子結點相當於一個選手,每個中間結點相當於一場比賽,每一層相當於一輪比賽。

      不同的是,勝者樹的中間結點記錄的是勝者的標號;而敗者樹的中間結點記錄的敗者的標號。 

       勝者樹與敗者樹可以在log(n)的時間內找到最值。任何一個葉子結點的值改變后,利用中間結點的信息,還是能夠快速地找到最值。在k路歸並排序中經常用到。

      

勝者樹:

 勝者樹的一個優點是,如果一個選手的值改變了,可以很容易地修改這棵勝者樹。只需要沿着從該結點到根結點的路徑修改這棵二叉樹,而不必改變其他比賽的結果。

 

Fig. 1

Fig.1是一個勝者樹的示例。規定數值小者勝。

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為3;

2.         b3 PK b0,b3勝b0負,內部結點ls[2]的值為3;

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為1;

4.         b3 PK b1,b3勝b1負,內部結點ls[1]的值為3。.

當Fig. 1中葉子結點b3的值變為11時,重構的勝者樹如Fig. 2所示。

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為3;

2.         b3 PK b0,b0勝b3負,內部結點ls[2]的值為0;

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為1;

4.         b0 PK b1,b1勝b0負,內部結點ls[1]的值為1。.

Fig. 2


敗者樹:     

  敗者樹是勝者樹的一種變體。在敗者樹中,用父結點記錄其左右子結點進行比賽的敗者,而讓勝者參加下一輪的比賽。敗者樹的根結點記錄的是敗者,需要加一個結點來記錄整個比賽的勝利者。采用敗者樹可以簡化重構的過程。

 

Fig. 3

Fig. 3是一棵敗者樹。規定數大者敗。

1.         b3 PK b4,b3勝b4負,內部結點ls[4]的值為4;

2.         b3 PK b0,b3勝b0負,內部結點ls[2]的值為0;

3.         b1 PK b2,b1勝b2負,內部結點ls[3]的值為2;

4.         b3 PK b1,b3勝b1負,內部結點ls[1]的值為1;

5.         在根結點ls[1]上又加了一個結點ls[0]=3,記錄的最后的勝者。

敗者樹重構過程如下:

·            將新進入選擇樹的結點與其父結點進行比賽:將敗者存放在父結點中;而勝者再與上一級的父結點比較。

·            比賽沿着到根結點的路徑不斷進行,直到ls[1]處。把敗者存放在結點ls[1]中,勝者存放在ls[0]中。

Fig. 4

       Fig. 4是當b3變為13時,敗者樹的重構圖。

 

       注意,敗者樹的重構跟勝者樹是不一樣的,敗者樹的重構只需要與其父結點比較。對照Fig. 3來看,b3與結點ls[4]的原值比較,ls[4]中存放的原值是結點4,即b3與b4比較,b3負b4勝,則修改ls[4]的值為結點3。同理,以此類推,沿着根結點不斷比賽,直至結束。

 

        由上可知,敗者樹簡化了重構。敗者樹的重構只是與該結點的父結點的記錄有關,而勝者樹的重構還與該結點的兄弟結點有關。

 

對一個數組使用構建其敗者樹

代碼如下:

package struct.tree;

import java.util.Random;

/**
 * 敗者樹
 * 父節點中只存放敗者
 */
public class LoserTree {
    public static int[] tree = null;
    public static int[] data = null;

    public static void main(String[] args) {
        int data_len = 10;
        data = new int[data_len];
        Random rd = new Random();
        for (int index = 0; index < data_len; index++) {
            data[index] = rd.nextInt(10000);
        }
        for (int i = 0; i < data.length; ) {
            System.out.printf("%d\t", data[i++]);
        }

        System.out.println();

        //初始化敗者樹,敗者樹的長度和葉子節點個數相同
        tree = new int[data_len];

        //指定敗者樹頭的值
        tree[0] = createLoserTree(1);
        for (int i = 0; i < tree.length; ) {
            System.out.printf("%d\t", tree[i++]);
        }

        System.out.println();

        data[2] = 2;
        adjust(2);

        for (int i = 0; i < tree.length; ) {
            System.out.printf("%d\t", tree[i++]);
        }
    }

    /**
     * 創建敗者樹,返回勝利者,敗者賦值給treeIndex節點
     * 要設置treeIndex 的值必須先計算其子節點的值,依次遞歸,直到葉子節點
     *
     * @param treeIndex 計算該節點值
     * @return 勝者下標
     */
    public static int createLoserTree(int treeIndex) {

        //將樹組下標影射到葉子節點
        if (treeIndex >= tree.length) return treeIndex - tree.length;
        int left = treeIndex * 2;
        int right = treeIndex * 2 + 1;
        int leftWinner = createLoserTree(left);
        int rightWinner = createLoserTree(right);

        if (data[leftWinner] < data[rightWinner]) {
            tree[treeIndex] = rightWinner;
            return leftWinner;
        } else {
            tree[treeIndex] = leftWinner;
            return rightWinner;
        }
    }

    /**
     * 調整敗者樹
     *
     * @param s 需要調整的下標
     */
    public static void adjust(int s) {
        for (int t = (s + tree.length) / 2; t > 0; t /= 2) {
            //當前值大於其父節點值
            if (data[s] > data[t]) {
                int tmp = s;
                s = tree[t];
                tree[t] = tmp;
            }
        }
        tree[0] = s;
    }
}

 

 


免責聲明!

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



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