算法:根據二叉樹的數據及其對應的順序存儲下標值(從1開始),生成對應的二叉樹

之前我實現了通過二叉樹的中序遍歷順序,加上前後序任意一種遍歷順序,最後在內存中生成二叉樹的方法(點我去看看)。今天我再實現一種根據二叉樹的數據及其對應的順序存儲下標值(從1開始),在內存中生成對應的二叉樹

我們還是拿之前文章的二叉樹來作爲例子。二叉樹有一種方式叫做順序存儲,就是把二叉樹先填滿爲完全二叉樹(沒有的節點作爲虛節點,僅作爲數位置),然後先將根節點放入數組第一個位置,然後第二層,也就是根節點的兩個兒子,先左後右依次放入數組,第三層也是按順序放。如果是沒有數據的虛節點,就是null。我們拿示例圖舉例子:
在這裏插入圖片描述
藍色的是原來的示例二叉樹,紅色的是不存在的節點,我將其填滿了,變成了一顆完全二叉樹。然後先數第一層,然後從左到右依次數第二層、第三層……直到數完(圖上紅色的數字,代表該節點在順序存儲的時候,應該存儲在數組的位置的下標值)。因爲數組下標是從0開始,所以我在圖上標註的就是從0數到9的。然後將上面的二叉樹轉化爲數組,就應該是這樣的:

{"A", "B", "C", "D", "E", null, null, null, "F", "G"}

這樣表示有一個問題,如果是一顆很 “二” 的二叉樹,例如下圖那種:
在這裏插入圖片描述
那用數組表示,就是:

{"A", null, "B", null, null, null, "C", null, null, null, null, null, null, null, "D",
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, "E"}

這樣會很浪費數組空間。所以我的順序存儲,採用Map的方式。key爲數組順序存儲下標值+1,value就爲節點值。也就是說,下標的數法跟順序存儲一樣,只不過從1開始(你也可以理解爲順序存儲的數組的下標值+1,即爲key)。然後示例圖上的節點就如下表示:

key:1, value:"A"
key:2, value:"B"
key:3, value:"C"
key:4, value:"D"
key:5, value:"E"
key:9, value:"F"
key:10, value:"G"

今天我要實現的算法,就是一個二叉樹,入參一個上面的那種key-value的Map,然後在內存裏面生成對應的二叉樹,並返回該二叉樹的根節點。以下代碼是二叉樹的節點類,帶有前中後三種遍歷方法。而本次我也是採用了泛型的數據格式,你也可以傳其他的數據類型:

import java.util.List;

/**
 * @author LiYang
 * @ClassName Node
 * @Description 二叉樹的節點類
 * @date 2019/11/4 14:18
 */
public class Node<T> {

    //二叉樹的內容(本例用String內容)
    public T data;

    //左節點
    public Node lChild;

    //右節點
    public Node rChild;

    /**
     * 如果打印的話,就打印節點的數據字符串
     * @return
     */
    @Override
    public String toString() {
        return this.data.toString();
    }

    /**
     * 二叉樹的前序遍歷
     * @param root 二叉樹根節點
     * @param orderList 前序遍歷的結果集合
     */
    public static void preOrderTraversal(Node root, List<Node> orderList){
        if(root != null){
            //先遍歷根(裝結果集合)
            orderList.add(root);

            //再遍歷左子樹
            preOrderTraversal(root.lChild, orderList);

            //最後遍歷右子樹
            preOrderTraversal(root.rChild, orderList);
        }
    }

    /**
     * 二叉樹的中序遍歷
     * @param root 二叉樹根節點
     * @param orderList 中序遍歷的結果集合
     */
    public static void midOrderTraversal(Node root, List<Node> orderList){
        if(root != null){
            //先遍歷左子樹
            midOrderTraversal(root.lChild, orderList);

            //再遍歷根(裝結果集合)
            orderList.add(root);

            //最後遍歷右子樹
            midOrderTraversal(root.rChild, orderList);
        }
    }

    /**
     * 二叉樹的後序遍歷
     * @param root 二叉樹根節點
     * @param orderList 後序遍歷的結果集合
     */
    public static void postOrderTraversal(Node root, List<Node> orderList){
        if(root != null){
            //先遍歷左子樹
            postOrderTraversal(root.lChild, orderList);

            //再遍歷右子樹
            postOrderTraversal(root.rChild, orderList);

            //最後遍歷根(裝結果集合)
            orderList.add(root);
        }
    }

}

下面的代碼就是今天的主題了:通過目標二叉樹的順序存儲的下標值和節點數據的Map,在內存中生成目標二叉樹,並用三種遍歷方式來驗證其正確性:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author LiYang
 * @ClassName BinaryTreeGenerator
 * @Description 二叉樹的生成工具類
 * @date 2019/11/4 14:20
 */
public class BinaryTreeGenerator<T> {

    /**
     * 根據節點數據,生成二叉樹,返回根節點
     * @param data 二叉樹數據
     * @return 生成的二叉樹的根節點
     */
    public Node generateBinaryTree(Map<Integer, T> data) {
        //如果沒有1這個鍵,證明沒有根節點,直接返回null
        if (!data.containsKey(1)){
            return null;
        }

        //如果有1這個鍵,那就先初始化二叉樹根節點
        Node root = new Node();
        //賦值根節點
        root.data = data.get(1);

        //從根節點開始遞歸擴展
        expandBinaryTree(root, 1, data);

        //擴展完畢,返回根節點
        return root;
    }

    /**
     * 二叉樹擴展方法
     * @param root 根節點
     * @param index 根節點的下標值
     * @param data 二叉樹數據
     */
    public void expandBinaryTree(Node root, int index, Map<Integer, T> data){
        //左節點的下標
        int leftIndex = index * 2;
        
        //右節點的下標
        int rightIndex = index * 2 + 1;
        
        //如果左節點數據存在
        if (data.containsKey(leftIndex)){
            //實例化左節點
            root.lChild = new Node();
            //賦值左節點
            root.lChild.data = data.get(leftIndex);
        
            //遞歸擴展左節點
            expandBinaryTree(root.lChild, leftIndex, data);
        }
        
        //如果右節點數據存在
        if (data.containsKey(rightIndex)){
            //實例化右節點
            root.rChild = new Node();
            //賦值右節點
            root.rChild.data = data.get(rightIndex);
            
            //遞歸擴展右節點
            expandBinaryTree(root.rChild, rightIndex, data);
        }
    }

    /**
     * 驗證算法生成的示例圖中的二叉樹
     * 對生成的二叉樹進行前中後序遍歷,看遍歷順序是否正確
     * @param args
     */
    public static void main(String[] args) {
        //二叉樹生成類的實例(泛型的話,不能static)
        //注意,本例的數據是String,故泛型是String
        BinaryTreeGenerator<String> treeGenerator = new BinaryTreeGenerator<String>();
        
        //示例二叉樹的節點,以及其順序存儲的下標值(從1開始)
        //因爲BinaryTreeGenerator類的泛型是String,所以下面的Map的value也是String
        Map<Integer, String> data = new HashMap<Integer, String>();
        data.put(1, "A");
        data.put(2, "B");
        data.put(3, "C");
        data.put(4, "D");
        data.put(5, "E");
        data.put(9, "F");
        data.put(10, "G");

        //根據以上的Map數據,生成二叉樹,得到根節點
        Node root = treeGenerator.generateBinaryTree(data);

        //用於存儲二叉樹遍歷順序的List
        List<Node> orderList = new ArrayList<Node>();
        
        //驗證前序遍歷
        Node.preOrderTraversal(root, orderList);
        System.out.println("前序遍歷結果:" + orderList);
        
        //驗證中序遍歷
        orderList.clear();
        Node.midOrderTraversal(root, orderList);
        System.out.println("中序遍歷結果:" + orderList);
        
        //驗證後序遍歷
        orderList.clear();
        Node.postOrderTraversal(root, orderList);
        System.out.println("後序遍歷結果:" + orderList);
    }
    
}

根據之前那篇博客的內容(回顧一下),我們可以知道,示例二叉樹的三種遍歷結果是:
前序遍歷:ABDFEGC
中序遍歷:DFBGEAC
後序遍歷:FDGEBCA

然後我們運行 BinaryTreeGenerator.java 類的main方法,用算法生成示例圖的二叉樹,然後進行三種遍歷,控制檯輸出:

前序遍歷結果:[A, B, D, F, E, G, C]
中序遍歷結果:[D, F, B, G, E, A, C]
後序遍歷結果:[F, D, G, E, B, C, A]

根據控制檯輸出結果,可以對比看出,我們用算法生成的二叉樹,三種遍歷順序完全一致!至此我們可以說,該算法確實生成了示例圖中的二叉樹。這下,初始化生成二叉樹就又多一種方法了!

最後再說明一點,如果你是順序存儲的數組,那也好辦,將數組遍歷,如果爲null則不管,如果不爲null,就將下標值+1弄成key,數組元素弄成value,裝入到Map中,然後就可以用該Map調用本算法,生成對應的二叉樹了。

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