辛星Java树算法教程第十篇:二叉树的重建(带中序遍历)

对于重建二叉树的场景,通常是有中序遍历的,不然无法知道根节点在哪。因为前序遍历是根节点在最前面,后序遍历是根节点在最后面,所以无法明确的区分出来哪些点是左子树的点,哪些点是右子树的点。
对于重建二叉树来说,这里我们选择以前序遍历和中序遍历为例进行说明。
比如前序遍历为:
A B D E C F
比如中序遍历为:
D B E A C F
那么我们有如下的思维步骤:
(1).根据前序遍历,我们知道A是根节点。
(2).根据中序遍历,我们知道 D B E为左子树,我们知道 C F为右子树。
(3).这样第一个根节点就找到了,然后我们再来找左子树的根节点。
(4).根据前序遍历,我们可以发现B点是左子树的根节点。
(5).根据中序遍历,我们可以发现D是左子树的左子树,E是左子树的右子树。

那么我们如何来把上面的思维方式变成代码呢?
我们这里给出一个具体的代码范例吧:

package com.mengzhidu.teach.algorithm.tree.demo.reduce;

import com.mengzhidu.teach.algorithm.tree.demo.TreeNode;

import java.util.ArrayList;

/**
 * 根据前序遍历和中序遍历来重建二叉树
 * 这里使用了递归的方式,主要的思路是基于如下几点:
 * 1.根据前序遍历首先确定根节点
 * 2.根据中序遍历确定左子树和右子树的长度
 * 3.找到前序遍历的左子树和右子树
 * 4.使用递归的方式来创建其左右子树
 */
public class ReduceByPreAndInDemo {

    public static void main(String[] args) {
        ArrayList<String> preOrderList = new ArrayList<>();
        preOrderList.add("A");
        preOrderList.add("B");
        preOrderList.add("D");
        preOrderList.add("E");
        preOrderList.add("C");
        preOrderList.add("F");

        ArrayList<String> inOrderList = new ArrayList<>();
        inOrderList.add("D");
        inOrderList.add("B");
        inOrderList.add("E");
        inOrderList.add("A");
        inOrderList.add("C");
        inOrderList.add("F");

        TreeNode root = reduceByPreAndIn(preOrderList, inOrderList, 0, preOrderList.size()-1, 0, inOrderList.size()-1);
        System.out.println("前序遍历");
        preOrder(root);
        System.out.println("中序遍历");
        inOrder(root);
    }

    private static TreeNode reduceByPreAndIn(ArrayList<String> preOrder, ArrayList<String> inOrder,
                                             int preCurrent, int preEnd, int inCurrent, int inEnd) {
        if (preCurrent > preEnd || inCurrent > inEnd) {
            return null;
        }

        String value = preOrder.get(preCurrent);
        TreeNode current = new TreeNode(value, null, null);
        int span = -1;
        for (int i = inCurrent; i <= inEnd; i++) {
            if (inOrder.get(i).equals(value)) {
                span = i;
                break;
            }
        }

        if (span == -1) {
            return null;
        }

        // 前序遍历中的分界线
        int fix = preEnd - inEnd + span;

        current.setLeft(reduceByPreAndIn(preOrder, inOrder, preCurrent +1, fix, inCurrent, span -1));
        current.setRight(reduceByPreAndIn(preOrder, inOrder, fix + 1 , preEnd, span + 1 , inEnd));
        return current;
    }

    private static void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.println(root.getValue());
        preOrder(root.getLeft());
        preOrder(root.getRight());
    }

    private static void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.getLeft());
        System.out.println(root.getValue());
        inOrder(root.getRight());
    }
}

其实这里主要是分清楚哪些变量是根据哪些变量得来的即可,对于是否需要加1减1这些地方的值,我们自己列一个具体的情况分析一下即可。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值