由前序遍历和中序遍历还原二叉树非递归算法

原理:前序遍历的第一个为根结点,在中序遍历中找到对应的结点位置后,在该位置左侧为根结点的左子树,在该位置右侧的为右子树,并可以找到在前序遍历中根结点左子树的的前序遍历和右子树的前序遍历,这两个数组的第一个就分别为根结点的左右孩子。

思路:一层一层的还原二叉树,还原一层的结点需要两种信息,上一层的所有结点,以及上一层每个结点左右孩子的信息。

还原算法以及前序、中序和后序遍历非递归算法

package ReconstructBinaryTree.core;

import java.util.ArrayList;
import java.util.LinkedList;

public class ReconstructBinaryTreeNode {
    //由前序、中序遍历还原二叉树非递归算法
    public static BinaryTreeNode constructTree(int[] preOrder, int[] inOrder) throws IsNotSameBinaryTree {
        if (preOrder == null || inOrder == null) {
            return null;
        }
        int totalLength = preOrder.length;
        if (totalLength != inOrder.length) {
            throw new IsNotSameBinaryTree("不是同一棵二叉树的遍历");
        }
        int[] queue1 = new int[6 * (totalLength + 1)];
        //队列一和队列二作用相同,用来以6个数值为一组分别表示上一层结点的:
        // 以左孩子为根前序遍历开始坐标,以左孩子为根中序遍历开始坐标,以及其对应的长度
        // 以右孩子为根前序遍历开始坐标,以右孩子为根中序遍历开始坐标,以及其对应的长度
        int rootValue = preOrder[0]; // 前序遍历的第一个结点为根结点
        int j;
        for (j = 0; j < totalLength; j++) {
            if (inOrder[j] == rootValue) {
                break;
            }
        }

        if (inOrder[j] != rootValue) {
            throw new IsNotSameBinaryTree("不是同一棵二叉树的遍历");
        }
        //在中序遍历中找根结点,若查找失败则说明这两个二叉树的遍历有问题
        //给队列一填初值
        queue1[0] = 1;                               //前序遍历第一个为根节点,所以在前序遍历结果中从下标为1开始,长度为queue[2]的数组是以根节点左孩子为根的前序遍历结果
        queue1[1] = 0;                               //中序遍历第j个为根结点,所以在中序遍历结果中从下标为0开始, 长度为j-0的数组是以根结点左孩子为根的中序遍历结果
        queue1[2] = j;                               //由前两个得长度为j-0,即j
        queue1[3] = j + 1;                           //前序遍历第一个为根节点,所以在前序遍历结果中从下标为1+j开始,长度为queue[5]的数组是以根节点右孩子为根的前序遍历结果
        queue1[4] = j + 1;                           //中序遍历第j个为根结点,所以在中序遍历结果中从下标为j+1开始, 长度为总长减去queue1[2]和一个根节点的数组是以根结点又孩子为根的中序遍历结果
        queue1[5] = totalLength - j - 1;             //所以这个长度为总长-queue1[2]-1
        int[] queue2 = new int[6 * (totalLength + 1)];

        ArrayList<BinaryTreeNode> layer1 = new ArrayList<>(); //用来装上一层的结点
        ArrayList<BinaryTreeNode> layer2 = new ArrayList<>(); //存入下一层结点
        BinaryTreeNode root = new BinaryTreeNode(rootValue);
        layer1.add(root);                                     //放入根结点

        while (layer1.size() != 0) {
            for (int i = 0; i < layer1.size(); i++) {         //遍历上一层结点
                for (int k = 2; k < 6; k += 3) {              //每个结点都有它的左右孩子结点
                    if (queue1[i * 6 + k] > 0) {              //若为null则跳过
                        int childValue = preOrder[queue1[i * 6 + k - 2]];  //前序遍历的第一个结点为根结点,也是上一层第i个结点的孩子结点,当k为2时是左孩子结点,当k为5时是右孩子结点
                        BinaryTreeNode child = new BinaryTreeNode(childValue);
                        if (k == 2) {
                            layer1.get(i).setLeftChild(child);
                        } else {
                            layer1.get(i).setRightChild(child);
                        }
                        layer2.add(child);                     //本层新产生的结点放入layer2里
                        for (j = queue1[i * 6 + k - 1]; j < queue1[i * 6 + k - 1] + queue1[i * 6 + k]; j++) {       //在中序遍历中找到这个孩子结点
                            if (inOrder[j] == childValue) {
                                break;
                            }
                        }
                        if (inOrder[j] != childValue) {
                            throw new IsNotSameBinaryTree("不是同一棵二叉树的遍历");
                        }
                        //找到后分别在将这个孩子结点的左右孩子信息放入queue2里
                        int childCount = layer2.size() - 1;
                        queue2[childCount * 6] = queue1[i * 6 + k - 2] + 1;    //孩子结点的左孩子的前序遍历开始下标
                        queue2[childCount * 6 + 1] = queue1[i * 6 + k - 1];    //孩子结点的左孩子的中序遍历开始下标
                        queue2[childCount * 6 + 2] = j - queue1[i * 6 + k - 1];//对应的长度
                        queue2[childCount * 6 + 3] = queue2[childCount * 6] + queue2[childCount * 6 + 2]; //孩子结点的右孩子的前序遍历开始下标
                        queue2[childCount * 6 + 4] = j + 1;                                             //孩子结点的右孩子的中序遍历开始下标
                        queue2[childCount * 6 + 5] = queue1[i * 6 + k] - queue2[childCount * 6 + 2] - 1; //对应的长度
                    }
                }
            }
            layer1.clear();
            int[]  queue = queue1;
            queue1 = queue2;
            queue2 = queue;
            ArrayList<BinaryTreeNode> layer = layer1;
            layer1 = layer2;
            layer2 = layer;
        }

        return root;
    }

    private static int[] preOrder;
    private static int[] inOrder;

    //由前序、中序遍历还原二叉树递归算法
    public static BinaryTreeNode reConstructTree(int[] preOrder, int[] inOrder) throws IsNotSameBinaryTree {
        if (preOrder == null || inOrder == null) {
            return null;
        }

        int totalLength = preOrder.length;

        if (totalLength != inOrder.length) {
            throw  new IsNotSameBinaryTree("不是同一棵二叉数的遍历");
        }

        ReconstructBinaryTreeNode.preOrder = preOrder;
        ReconstructBinaryTreeNode.inOrder = inOrder;
        return reConstructTree(0, totalLength -1, 0 , totalLength-1);
    }

    private static BinaryTreeNode reConstructTree(int preStart, int preEnd, int inStart, int inEnd) throws IsNotSameBinaryTree {
        int rootValue = ReconstructBinaryTreeNode.preOrder[preStart];
        BinaryTreeNode root = new BinaryTreeNode(rootValue);

        if (preStart == preEnd) {
            if (inStart == inEnd && rootValue == ReconstructBinaryTreeNode.inOrder[inStart]) {
                return root;
            } else {
                throw new IsNotSameBinaryTree("不是同一棵二叉树的遍历");
            }
        }

        int i;
        for (i = inStart; i <= inEnd; i++) {
            if (ReconstructBinaryTreeNode.inOrder[i] == rootValue) {
                break;
            }
        }

        if (ReconstructBinaryTreeNode.inOrder[i] != rootValue) {
            throw new IsNotSameBinaryTree("不是同一棵二叉树的遍历");
        }

        preStart += 1;
        int length = i - inStart;

        if (i > inStart) {
            root.setLeftChild(reConstructTree(preStart,preStart + length - 1, inStart, i - 1));
        }

        if (i < inEnd){
            root.setRightChild(reConstructTree(preStart + length, preEnd,i + 1,inEnd));
        }

        return root;
    }

    public static void prePrintBinaryTree(BinaryTreeNode root) {
        if (root == null) {
            return;
        }

        LinkedList<BinaryTreeNode> stack = new LinkedList<>();
        BinaryTreeNode next = root;

        while (stack.size() != 0 || next != null) {
            if (next != null) {
                stack.addFirst(next);
                System.out.println(next.getValue());
                next = next.getLeftChild();
            } else {
                next = stack.removeFirst();
                next = next.getRightChild();
            }
        }
    }

    public static void inPrintBinaryTree(BinaryTreeNode root) {
        if (root == null) {
            return;
        }

        LinkedList<BinaryTreeNode> stack = new LinkedList<>();
        BinaryTreeNode next = root;

        while (stack.size() != 0 || next != null) {
            if (next != null) {
                stack.addFirst(next);
                next = next.getLeftChild();
            } else {
                next = stack.removeFirst();
                System.out.println(next.getValue());
                next = next.getRightChild();
            }
        }
    }

    public static void lastPrintBinaryTree(BinaryTreeNode root) {
        if (root == null) {
            return;
        }

        LinkedList<BinaryTreeNode> stack = new LinkedList<>();
        stack.addFirst(root);
        BinaryTreeNode last = null;
        while (stack.size() != 0) {
            BinaryTreeNode first = stack.getFirst();
            if ((first.getLeftChild() == null && first.getRightChild() == null)
                    || (last != null && (last == first.getLeftChild() || last == first.getRightChild()))) {
                stack.removeFirst();
                System.out.println(first.getValue());
                last = first;
            } else {
                if (first.getRightChild() != null) {
                    stack.addFirst(first.getRightChild());
                }
                if (first.getLeftChild() != null) {
                    stack.addFirst(first.getLeftChild());
                }
            }
        }
    }
}

测试程序

package ReconstructBinaryTree.Test;

import ReconstructBinaryTree.core.BinaryTreeNode;
import ReconstructBinaryTree.core.IsNotSameBinaryTree;
import ReconstructBinaryTree.core.ReconstructBinaryTreeNode;

import java.util.Scanner;

public class Test {
    public static void main(String[] args) {
        System.out.println("请输入二叉树节点数");
        Scanner scanner = new Scanner(System.in);
        int count = scanner.nextInt();
        while (true) {
            if (count <= 0) {
                System.out.println("请重新输入二叉节点数");
                count = scanner.nextInt();
            } else {
                break;
            }
        }
        System.out.println("请依次输入前序遍历和中序遍历");
        int[] preOrder = new int[count];
        int[] inOrder = new int[count];
        for (int i = 0; i <count; i++) {
            preOrder[i] = scanner.nextInt();
        }
        for (int i = 0; i < count; i++) {
            inOrder[i] = scanner.nextInt();
        }

        try {
            BinaryTreeNode root = ReconstructBinaryTreeNode.reConstructTree(preOrder, inOrder);
            ReconstructBinaryTreeNode.lastPrintBinaryTree(root);
        } catch (IsNotSameBinaryTree isNotSameBinaryTree) {
            isNotSameBinaryTree.printStackTrace();
        }
    }
}

 

最新版H5十四合一代付系统源码带搭建教程,程简洁丝滑流畅,修复新增了很多功能,目前这一套程可以正常在微信使用,没有那么容易红域名,代码是全开源的可二次开发测试环境:MySQL5.7,PHP7.4一、功能:1.带有14个模板,分别是美团、京东、拼多多、滴滴出行(2个)、携程(2个)、咸鱼、抖音、淘宝闪购、淘宝好物、得物、飞猪、猫眼2.前台账号密码登录,无需服务号登录(不过卡片分享的时候需要用到服务号js接口)3.带有代理功能,支持按比列分成(可自定义设置),用户支持提现,带有推广下级功能4.支付接口支持对接总后台支付,也支持每个用户自行配置支付5.用户可自行上架商品,后台审核通过之后前台显示6.后台运营大屏,可清晰了解网站营收7.支持多种支付接口,有微信官方、易支付、个人码等8.每个模板带有专属海报二、更新修复:1.修复部分模板分享卡片无法正常显示问题2.修复远程js、css文件,改成本地加载提高流畅度3.修复因为图片失效到账后台加载慢的问题4.去掉用户注册功能5.修改首页显示商品逻辑,未登录用户无法正常商品6.更新订单筛选功能,支持按金额、商品名字、用户昵称、支付状态查询,最多显示200条7.后台支持手动添加用户功能8.分享卡片配置集成后台修改,可随意修改分享卡片的文字图片9.分享卡片图片支持自定义设置,可以默认调用商品图或者固定上传图片10.修改滴滴新版模板司机头像问题,改用获取商品图片11.修复更新若干小问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值