二叉树的遍历与重建

重建二叉树

这里介绍一下用先序-中序,后序-中序遍历结果来构建二叉树的过程。

什么是先序,后序,中序遍历?

首先看一下如下的二叉树结构。

在这里插入图片描述

各种遍历的规则:

  • 前序遍历(根左右): 对访问到的每个结点,先访问根结点,然后是左结点,然后是右结点
  • 中序遍历(左根右): 对访问到的每个结点,先访问左结点,然后是根结点,然后是右结点
  • 后序遍历(左右根): 对访问到的每个结点,先访问左结点,然后是右结点,然后是根结点

由上述规则,各个遍历的遍历结果如下:

前序遍历:
	1,2,4,5,3,6,7
中序遍历:
	4,5,2,1,6,3,7
后序遍历:
	5,4,2,6,7,3,1

下面从代码层次讲解一下过程(递归+非递归版本)

1. 遍历实现

递归版本:最好理解,按照之前的遍历规则,码好就 ok 了

非递归版本则需要借助栈的存储结构

1)前序遍历

递归版本:

public static void preOrderRecur(Node head) {
    if(head!=null) {
        // 根
        System.out.print(head.value+" ");
        // 左
        preOrderRecur(head.left);
        // 右
        preOrderRecur(head.right);
    }
}

非递归版本:

在这里插入图片描述

上图为一个栈,由于栈的先进先出原则,首先出的为根节点,然后为左,右。

注意:这边左右节点入栈顺序相反!

原因:由于栈的先进先出策略,左边先进栈,那么左节点晚于右节点出栈!与规则不符合

public static void preOrderUnRecur(Node head) {
    System.out.print("pre-order: ");
    // 使用栈结构
    Stack<Node> st = new Stack<Node>();
    if(head != null) {
        st.push(head);
        while(!st.isEmpty()) {
            head = st.pop();
            System.out.print(head.value+" ");
            if(head.right!=null) {
                st.push(head.right);
            }
            if(head.left!=null) {
                st.push(head.left);
            }
        }
    }
    System.out.println();
}
2) 后序遍历

递归版本:

public static void posOrderRecur(Node head) {
    if(head == null) {
        return ;
    }
    // 左
    posOrderRecur(head.left);
    // 右
    posOrderRecur(head.right);
    // 根
    System.out.print(head.value+" ");
}

非递归版本:

分析:

后序遍历能不能从前序遍历的非递归版本中改过来呢?

答案显然是可以的,只需要将左右入栈顺序改变;输出是进行逆序输出就行啦!!

前序遍历(根左右),后序遍历(左右根)明显看出将前序遍历做如下修改就能变成后序遍历:

​ 根左右->根右左->左右根

代码:

public static void posOrderUnRecur1(Node head) {
    System.out.print("pos-order: ");
    if(head!=null) {
        Stack<Node> s1 = new Stack<Node>();
        // 使用stack2 进行逆序输出
        Stack<Node> s2 = new Stack<Node>();
        s1.push(head);
        while(!s1.isEmpty()) {
            head = s1.pop();
            s2.push(head);
            if(head.left != null) {
                s1.push(head.left);
            }
            if(head.right!=null) {
                s1.push(head.right);
            }
        }
        while(!s2.isEmpty()) {
            System.out.print(s2.pop().value+" " );
        }
    }
    System.out.println();
}
3) 中序遍历

递归版本:

public static void inOrderRecur(Node head) {
    if(head == null) {
        return;
    }
    // 左
    inOrderRecur(head.left);
    // 根
    System.out.print(head.value+" ");
    // 右
    inOrderRecur(head.right);
}

非递归版本:

分析:

先将左节点全部进栈,没有左节点时,进行出栈操作!

出栈操作:弹出最左节点打印,然后将其右节点入栈。

public static void inOrderUnRecur(Node head) {
    System.out.print("in-order: ");
    if(head!=null) {
        Stack<Node> st = new Stack<Node>();
        while(!st.isEmpty() || head!=null) {
            if(head!=null) {
                st.push(head);
                head = head.left;
            }else {
                head = st.pop();
                System.out.print(head.value+" " );
                head = head.right;
            }
        }
    }
    System.out.println();
}

2. 重建二叉树

1)前中序重建二叉树

LeetCode 105

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

	3
  / \
  9  20
    /  \
   15   7

分析:

在这里插入图片描述

首先把中序数组分为如上的部分:

index:指向根节点(前序遍历的第一个为根节点,然后找到中序遍历中根节点的位置)

li/ri : 为中序遍历的首尾索引

lp/rp:为前序遍历的首尾索引

left: 为左子树的长度

在这里插入图片描述

​ 递归过程就是找到头节点和构造左右子树的过程,如上图所示。

代码:

public TreeNode buildTree(int[] preorder, int[] inorder) {
    if (preorder == null || preorder.length == 0)
        return null;
    TreeNode root = new TreeNode(preorder[0]);
    int index = 0;
    for (int i = 0; i < inorder.length; i++) {
        if (preorder[0] == inorder[i]){
            index = i;
            break;
        }
    }
    int left = index;
    int right = inorder.length-index-1;
    root.left = process(preorder,inorder, 1,left,0,index-1);
    root.right = process(preorder,inorder,left+1,preorder.length-1, index+1,inorder.length-1);
    return root;
}
public TreeNode process(int[] preorder, int[] inorder,int lp,int rp,int li, int ri){
    // base case
    if (lp > rp || li>ri)
        return null;
    if (lp == rp){
        return new TreeNode(preorder[lp]);
    }
    if (ri == li){
        return new TreeNode(preorder[li]);
    }
    TreeNode root = new TreeNode(preorder[lp]);
    // find the root value's index in inorder array
    int index = 0;
    for (int i = li; i <= ri; i++) {
        if (preorder[lp] == inorder[i]){
            index = i;
            break;
        }
    }
    // calculate the length of left tree
    int left = index-li;
    // build left tree
    root.left = process(preorder,inorder,lp+1,lp+left,li,index-1);
    // build right tree
    root.right = process(preorder,inorder,lp+left+1,rp,index+1,ri);
    return root;
}
2)中后序遍历重建二叉树

LeetCode 106

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

	3
   / \
  9  20
    /  \
   15   7

代码:

public TreeNode buildTree(int[] inorder, int[] postorder) {
    if (inorder == null || inorder.length == 0)
        return null;
    TreeNode root = new TreeNode(postorder[postorder.length-1]);
    int index = 0;
    for (int i = 0; i < inorder.length; i++) {
        if (postorder[postorder.length-1] == inorder[i]){
            index = i;
            break;
        }
    }
    int left = index - 0;
    root.left = buildTree(inorder,postorder, 0, index-1, 0, left-1);
    root.right = buildTree(inorder,postorder,index+1,inorder.length-1, left, postorder.length-2);
    return root;
}
public TreeNode buildTree(int[] inorder, int[] postorder, int li, int ri, int lp, int rp){
    // base case
    if (li>ri || lp>rp){
        return null;
    }
    if (li == ri){
        return new TreeNode(inorder[li]);
    }
    if (lp == rp){
        return new TreeNode(inorder[lp]);
    }
    TreeNode node = new TreeNode(postorder[rp]);
    // find the index of root index in inorder array
    int index = 0;
    for (int i = li; i <= ri; i++) {
        if (postorder[rp] == inorder[i]){
            index = i;
            break;
        }
    }
    int left = index-li;
    // build left tree
    node.left = buildTree(inorder,postorder,li,index-1,lp,lp+left-1);
    // build right tree
    node.right = buildTree(inorder,postorder,index+1,ri,lp+left,rp-1);
    return  node;
}

分析跟1)相似:不同在于根节点在后序数组中的最后位置。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值