之前我实现了通过二叉树的中序遍历顺序,加上前后序任意一种遍历顺序,最后在内存中生成二叉树的方法(点我去看看)。今天我再实现一种根据二叉树的数据及其对应的顺序存储下标值(从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调用本算法,生成对应的二叉树了。