之前我實現了通過二叉樹的中序遍歷順序,加上前後序任意一種遍歷順序,最後在內存中生成二叉樹的方法(點我去看看)。今天我再實現一種根據二叉樹的數據及其對應的順序存儲下標值(從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調用本算法,生成對應的二叉樹了。