剑指offer(三)重建二叉树,二叉树的下一个节点

剑指offer(三)重建二叉树,二叉树的下一个节点

题目:

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

思路:

前序遍历中,第一个数字就是整棵树的根节点.

中序遍历中,根节点之前的数字为根节点的左子树,之后的为右子树.

而中序遍历中, 根节点的下标–0 就是根节点的左子树中所含的元素

根节点的下标+1 到 数组长度-1,就是跟节点的右子树中所含的元素.

找到前序和中序中左右子树的对应子序列,便可以递归的去构建这一整棵树

代码:

private Map<Integer,Integer> inOrders=new HashMap<Integer,Integer>();
    public TreeNode reConstructBinaryTree(int [] pre,int [] in)
    {

        for (int i = 0; i < in.length; i++)
        {
            inOrders.put(in[i],i);
        }
        return core(pre,0,pre.length-1,0);

    }

    private TreeNode core(int[] pre, int preL, int preR, int inL)
    {
        if (preL>preR)
        {
            return null;
        }
        TreeNode root = new TreeNode(pre[preL]);
        int inIdex = inOrders.get(root.val);
        int leftTreeSize = inIdex-inL;
        root.left = core(pre,preL+1,preL+leftTreeSize,inL);
        /*
         * 第一次:
         * 在中序遍历序列中 找到前序遍历的第一个元素“1”的下标为3
         * 那么根节点为1的这棵树的左子树的总元素数量为3-0=3个
         * 第二次:
         * 对于这棵总元素数量为3的左子树 由于在前序遍历中左子树的遍历先于右子树
         * 所以preL+1 也就是前序遍历的下一个元素 即为这棵树的根节点
         * preR是这棵树元素的终点 即是preL+这棵树的规模
         * inL在构建右子树中解释
         */
        root.right = core(pre,preL+leftTreeSize+1,preR,inL+leftTreeSize+1);
        /*
         * 第一次:
         * 找到“1”的下标3之后
         * 这棵右子树的根节点(也就是在前序遍历序列的起点)为初始结点位置+左子树规模+1
         * 即对于5 3 8 6这棵数来说 要找它的根节点
         * 需要在前序遍历序列中偏移0+3+1个位置 故在前序遍历中的下标为4
         * preR不变 因为在前序遍历序列中 右子树永远在末尾
         * inL加上了左子树的规模+1是因为
         * 在3 5 6 8这棵树中 去中序遍历中找3的下标 发现为5
         * 这棵根节点为3的树 他的左子树的规模在计算时需要知道这棵树的起点
         * 而这棵树的起点 即是去掉和他同一深度的左兄弟树的规模 再减去他的父节点占用的1个位置
         * 所以inL是用于在构建右子树时计算规模的偏移记录量
         * 而在构建左子树时这个偏移量不需要变化是因为中序遍历是以左子树开头的 所以根节点的下标位置的数字就是左子树的规模
         * 他的左边没有其他元素占用位置
         */

        return root;
    }

题目:

给定一棵二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左、右子节点的指针,还有一个指向父节点的指针.

例子:

​ a

​ b c

d e f g

​ h i

中序遍历为 d b h e i a f c g

思路:

分析之后有三种情况

1 如果一个节点有右子树,那么他的下一个节点就是他的右子树中的最左子节点, 如 d 的下一个是h

沿着右子节点出发去知道左节点,直到为空

2 如果一个节点没有右子树,且节点是它父节点的左节点,那么它的下一个节点就是他的父节点,

3 如果一个节点既没有右子树,且不是它的父节点的左子节点,需要一直向上遍历,找到一个是他父节点的左子节点的节点.

如 i 的下一个节点就是 a ,沿着 i 的父节点一直向上,找到 b是a的左节点,且a是b的父节点.

代码:

class TreeLinkNode
{
    TreeLinkNode left;
    TreeLinkNode right;
    TreeLinkNode last;
    int val;
    TreeLinkNode(int x) { this.val = x; }
}
public class Test8
{
    public TreeLinkNode GetNext(TreeLinkNode pNode)
    {
        if (pNode.right!=null)
        {
            TreeLinkNode res = pNode;
            while (res.left!=null)
            {
                res= res.left;
            }
            return res;
        }
        else
        {
            if (pNode.last.left==pNode)
            {
                return pNode.last;
            }
            else if (pNode.last.right==pNode)
            {
                TreeLinkNode p =pNode;
                while (p.last!=null)
                //停止条件为父节点为空
                {
                    if (p.last.left==p)
                    {
                        return p.last;
                    }
                    p=p.last;
                }

            }
        }
        return null;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值