剑指offer——重建二叉树

简介: 剑指offer——重建二叉树

剑指offer——重建二叉树(亚马逊面试题)


原题描述

输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。
注意:
二叉树中每个节点的值都互不相同;
输入的前序遍历和中序遍历一定合法;
样例
给定:
前序遍历是:[3, 9, 20, 15, 7]
中序遍历是:[9, 3, 15, 20, 7]
返回:[3, 9, 20, null, null, 15, 7, null, null, null, null]
返回的二叉树如下所示:
    3
   / \
  9  20
    /  \
   15   7
原题链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/

参考代码(c++版)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    map<int,int> hash;
    vector<int> preorder,inorder;
    TreeNode* buildTree(vector<int>& _preorder, vector<int>& _inorder) {
        preorder = _preorder,inorder= _inorder;
        for(int i = 0; i < inorder.size();i++) hash[inorder[i]] = i;
        return dfs(0,preorder.size()-1,0,inorder.size()-1);
    }
    TreeNode* dfs(int pl,int pr,int il, int ir)
    {
        if(pl>pr) return nullptr;
        auto root = new TreeNode(preorder[pl]);
        int k = hash[root->val];
        auto left = dfs(pl+1,pl+1+k-il-1,il,k-1);
        auto right = dfs(pl+1+k-il,pr,k+1,ir);
        root->left = left;
        root->right = right;
        return root;
    }
};

解析


涉及知识点


1.树:n(n >= 0)个结点构成的有限集合。

微信图片_20221017125349.jpg

2.二叉树:每个结点最多有两个子树(左子树和右子树)的树结构

3.前序遍历:先访问根节点,再依次遍历左子树和右子树

4.中序遍历:先遍历左子树,再访问根节点,最后遍历右子树

5.后序遍历:先遍历左子树,再遍历右子树,最后访问根节点

6.由两种遍历序列确定二叉树–> 必须要有中序遍历。微信图片_20221017125505.jpg

前序是根左右,后序是左右根。都可以很快的确定根。但是对于左右子树的确定就存在争议。比如例子中的两个序列,都知道A是根,但是现在B是左子树还是右子树了?


分析


根据先序遍历序列的第一个结点确定根节点


根据根节点在中序遍历序列中分割出左右两个子序列


对左子树和右子树分别递归使用相同的方法继续分解

微信图片_20221017125557.jpg


为了快速找到数据,考虑使用哈希表进行优化(哈希表可以实现近似O(1)时间复杂度的插入、删除、查找等操作)


代码逐步落实


1.因为之后要在递归函数中使用到前序序列和中序序列。将两个序列处理为全局变量

vector<int> preorder,inorder;
//return dfs(0,preorder.size()-1,0,inorder.size()-1);

2.记录中序序列中每个数据的位置,用于后文的查询

for(int i = 0; i< inorder.size() ; i++) hash[inorder[i]] = i;

3,编写核心的递归函数dfs(int pl,int pr,int il,int ir)

pl,pr,il,ir分别代表前序序列的左端点、右端点和中序遍历序列的左端点、右端点

1.特判——倘若传入的序列里面没有元素,返回空

if(pl > pr) return nullptr;

2.获取根节点信息

auto root = new TreeNode(preorder[pl]);

3,计算根节点的哈希值

int k = hash[root->val];

4.对左右子树分别递归使用相同方法继续分解

auto left = dfs(pl+1,pl+k-il,il,k-1);
auto right = dfs(pl+k-il+1,pr,k+1,ir);

5.将最终结果返回到根节点root,维护为一棵完整的树

root->left = left, root->right = right;

难点剖析


步骤3-4 中取区间范围是比较棘手的点


理解

核心是谨记这句话"对左右子树分别递归使用相同方法继续分解"


处理左子树


结合上图,对于前序遍历而言,左子树所在的区间是pl+1 ~ (pl+1)+(k-il)-1,经过合并,就得到了上述表达式pl+1 ~ pl+k-il。对于中序遍历而言,左子树所在的区间是il ~ k-1。


处理右子树


结合上图,对于前序遍历而言,右子树所在的区间是(pl+k-il+1) ~ pr。对于中序遍历而言,右子树所在的区间是(k+1) ~ ir


相关文章
|
3月前
|
消息中间件 Kubernetes NoSQL
剑指offer常见题 - 二叉树问题(三)
剑指offer常见题 - 二叉树问题(三)
|
3月前
|
消息中间件 Kubernetes NoSQL
剑指offer常见题 - 二叉树问题(一)
剑指offer常见题 - 二叉树问题(一)
|
3月前
|
消息中间件 Kubernetes NoSQL
剑指offer常见题 - 二叉树问题(二)
剑指offer常见题 - 二叉树问题(二)
|
3月前
|
C++
【剑指offer】-重建二叉树-04/67
【剑指offer】-重建二叉树-04/67
【剑指offer】-反转链表-15/67
【剑指offer】-反转链表-15/67
|
11月前
剑指offer-6.重建二叉树
剑指offer-6.重建二叉树
21 0
|
Perl
剑指offer 06. 重建二叉树
剑指offer 06. 重建二叉树
42 0
剑指offer 23. 反转链表
剑指offer 23. 反转链表
45 0
重建二叉树(剑指offer 07)
输入某二叉树的前序遍历和中序遍历的结果,请构建该二叉树并返回其根节点。 假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
重建二叉树(剑指offer 07)
|
存储 算法 Java
反转链表(剑指offer 24)
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。