LeetCode105-从前序与中序遍历序列构造二叉树

28 篇文章 0 订阅
28 篇文章 0 订阅

LeetCode105-从前序与中序遍历序列构造二叉树

最近全国疫情严重,待在家里没事干,马上又要准备春招了,最近刷刷题,记录一下!再说一句,武汉加油,大家出门记得戴口罩!

1、题目

根据一棵树的前序遍历与中序遍历构造二叉树。
注意:
你可以假设树中没有重复的元素。
示例:

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

2、思路

如何遍历一棵树

有两种通用的遍历树的策略:

  • 宽度优先搜索(BFS):我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到。
  • 深度优先搜索(DFS):在这个策略中,我们采用深度作为优先级,以便从跟开始一直到达某个确定的叶子,然后再返回根到达另一个分支。
    深度优先搜索策略又可以根据根节点、左孩子和右孩子的相对顺序被细分为前序遍历,中序遍历和后序遍历。
    我们使用哈希表实现(递归) O(n)
    递归建立整棵二叉树:先递归创建左右子树,然后创建根节点,并让指针指向两棵子树。
具体步骤如下:
  • 先利用前序遍历找根节点:前序遍历的第一个数,就是根节点的值;
  • 在中序遍历中找到根节点的位置 k,则 k 左边是左子树的中序遍历,右边是右子树的中序遍历;
  • 假设左子树的中序遍历的长度是 l,则在前序遍历中,根节点后面的 l 个数,是左子树的前序遍历,剩下的数是右子树的前序遍历;
  • 有了左右子树的前序遍历和中序遍历,我们可以先递归创建出左右子树,然后再创建根节点;
    如图所示:
    在这里插入图片描述
    时间复杂度分析:我们在初始化时,用哈希表(unordered_map<int,int>)记录每个值在中序遍历中的位置,这样我们在递归到每个节点时,在中序遍历中查找根节点位置的操作,只需要 O(1) 的时间。此时,创建每个节点需要的时间是 O(1),所以总时间复杂度是 O(n)。
    1. 从左到右遍历preorder、inorder
    2. preorder的第一个元素一定是根元素
      2.1. 找到根元素后,可以在inorder中区分左、右子树
      2.2. 当前根的左子树范围:inorder[inorder未查找的最左边(从0开始), 和根元素相等的索引位置)
      2.3. 当前根的左子树范围:preorder(当前根索引位置(从0开始), 当前根的索引位置 + inorder已知左子树长度]
           原因:因为preorder、inorder中左、右子树的长度相等(只是观察得出,为什么这么巧,还没想透彻)
    3. 当preorder的指针向右移动到"左子树长度",说明当前根节点的左子树已经处理完毕
    4. 递归开始查找,如果没有超出左子树范围,preorder指针向右移动一位继续搜索
      4.1. 此时的节点最多只可能有2种身份:A + B
        A. 根节点
        B. 左节点或右节点
      4.2. inorder的起始节点是"最左的节点",当preorder中的值与他相等时,可以判断无后续数据,结束搜索
      4.3  inorder指针向右移动一位(排除已使用节点),缩小搜索范围
    5. 右节点确定规则:因为上一步确定的是一个左节点,preorder顺序为根左右,所以preorder的下一个节点就是右节点

3、代码

递归实现:

class Solution {
public:
    //定义哈希表存储中序遍历中每个点的位置
    unordered_map<int,int> Hash;
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        //计算整颗子树的大小
        int n=preorder.size();
        //哈希表存储中序遍历中每个点的位置
        for(int i=0;i<n;i++)
        {
            Hash[inorder[i]]=i;
        }
        return dfs(preorder,inorder,0,n-1,0,n-1);
    }
    TreeNode* dfs(vector<int>& preorder,vector<int>& inorder,int pl,int pr,int il,int ir)
    {
        if (pl > pr) return NULL;
        //根节点的值
        int val=preorder[pl];
        int k=Hash[val];//距离
        int len=k-il;
        //定义根节点
        auto root=new TreeNode(val);
        root->left=dfs(preorder,inorder,pl+1,pl+len,il,k-1);
        root->right=dfs(preorder,inorder,pl+len+1,pr,k+1,ir);
        return root;
    }
};

Java

class Solution {
    //定义哈希表,存储中序遍历每个点的位置
    HashMap<Integer,Integer> map = new HashMap<Integer, Integer>();
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        for (int i = 0; i < inorder.length; i++) {
            map.put(inorder[i], i);
        }
        return dfs(preorder, 0, preorder.length, inorder, 0, inorder.length);  
    }
    private TreeNode dfs(int[] preorder, int p_start, int p_end, int[] inorder, int i_start, int i_end) {
        // preorder 为空,直接返回 null
        if (p_start == p_end) {
            return null;
        }
        int root_val = preorder[p_start];
        TreeNode root = new TreeNode(root_val);
        //在中序遍历中找到根节点的位置
        
        //int i_root_index = 0;
        /*
        for (int i = i_start; i < i_end; i++) {
            if (root_val == inorder[i]) {
                i_root_index = i;
                break;
            }
        }
        */
        int i_root_index = map.get(root_val);
        //k          i_root_index
        //leftNum    len
        int leftNum = i_root_index - i_start;
        //递归的构造左子树
        root.left =dfs(preorder, p_start + 1, p_start + leftNum + 1, inorder, i_start,i_root_index);
        //递归的构造右子树
        root.right =dfs(preorder, p_start + leftNum + 1, p_end, inorder, i_root_index + 1, i_end);
        return root;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值