算法:根据二叉树的数据及其对应的顺序存储下标值(从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调用本算法,生成对应的二叉树了。

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