目录
刷题日期:20:4827 星期一2021年3月15日
个人刷题记录,代码收集,来源皆为leetcode
经过多方讨论和请教,现在打算往Java方向发力
主要答题语言为Java
题目:
剑指 Offer 07. 重建二叉树
难度中等360
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/ \
9 20
/ \
15 7
限制:
0 <= 节点个数 <= 5000
初始解答:
二叉树的题还是第一次遇到,书本中的代码也有一页多,自己先尽量思考大致的思路,了解判断根节点,左右子树的方法,然后大概想出来思路,参考别人的代码完成。
前序遍历的第一个数字是根节点;
中序遍历根节点之前的都是左子树;
之后的都是右子树;
一直循环,再复杂多层的情况目前还想不太明白。
要使用递归不断确定到最下层的左子树、双亲、右子树。
参考了Ant的评论:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
//题目已经确定了函数名和输入的参数,那么想要递归就不重新定义函数
//而是直接把题目作为递归的那个函数
int n = preorder.length; //判断遍历结果长度
if (n == 0)
return null; //为空返回空
int rootVal = preorder[0], rootIndex = 0; //否则第一步先找到根节点,确定参数
for(int i = 0; i <n; i++) {
if(inorder[i] == rootVal) {
rootIndex = i;
break; //在中序遍历中找到根节点的位置并记录
}
}
TreeNode root = new TreeNode(rootVal);
//下面的两句是核心
root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + rootIndex), Arrays.copyOfRange(inorder, 0, rootIndex));
/* Arrays.copyOfRange(T[ ] original,int from,int to)
//将一个原始的数组original,从下标from开始复制,复制到上标to,生成一个新的数组。
注意这里包括下标from,不包括上标to */
root.right = buildTree(Arrays.copyOfRange(preorder, 1 + rootIndex, n), Arrays.copyOfRange(inorder, rootIndex + 1, n));
return root;
}
}
执行结果:效率很是一般,难道是用了递归的缘故吗,但是这种解决树的问题,不就应该用递归么。
通过
显示详情
执行用时:14 ms, 在所有 Java 提交中击败了14.42%的用户
内存消耗:88.7 MB, 在所有 Java 提交中击败了5.04%的用户
知识点:
- 前序遍历列表:第一个元素永远是 【根节点 (root)】
- 中序遍历列表:根节点 (root)【左边】的所有元素都在根节点的【左分支】,【右边】的所有元素都在根节点的【右分支】
算法思路:
- 通过【前序遍历列表】确定【根节点 (root)】
- 将【中序遍历列表】的节点分割成【左分支节点】和【右分支节点】
- 递归寻找【左分支节点】中的【根节点 (left child)】和 【右分支节点】中的【根节点 (right child)】
试试看有没有效率更高的。
参考方法二:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
//直接返回新建立函数的结果est:establish
return est(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
public TreeNode est(int[] preorder, int l1, int r1, int[] inorder, int l2, int r2) {
if(l1>r1||l2>r2) return null;
int i = l2;
while(inorder[i]!=preorder[l1]) {
i++;
}
TreeNode root = new TreeNode(preorder[l1]);
root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-2);
root.right = est(preorder,l1+i-l2+1,r1,inorder,i+1,r2);
return root;
}
}
但是输入:[3,9,20,15,7] [9,3,15,20,7]
输出
[3,null,20,null,7]
差别
预期结果
[3,9,20,null,null,15,7]
与预期答案并不相同,寻找问题所在。
少的是左子树的9,判断错误在left节点语句上,果然排查发现最后的i-1码成了i-2,所以把9给跳过了,修正便可。
root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-2);//错误代码
root.left = est(preorder,l1+1,l1+i-l2,inorder,l2,i-1);//正确代码
学习他人:
方法一:
AntL1 2020-02-13
题目函数直接递归
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
if (n == 0)
return null;
int rootVal = preorder[0], rootIndex = 0;
for (int i = 0; i < n; i++) {
if (inorder[i] == rootVal) {
rootIndex = i;
break;
}
}
TreeNode root = new TreeNode(rootVal);
root.left = buildTree(Arrays.copyOfRange(preorder, 1, 1 + rootIndex), Arrays.copyOfRange(inorder, 0, rootIndex));
root.right = buildTree(Arrays.copyOfRange(preorder, 1 + rootIndex, n), Arrays.copyOfRange(inorder, rootIndex + 1, n));
return root;
}
}
方法二:
SilenL12021-03-02
java 递归 不用HashMap
重新建立函数进行递归。
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return help(preorder,0,preorder.length-1,inorder,0,inorder.length-1);
}
public TreeNode help(int[] preorder,int l1,int r1,int[] inorder,int l2,int r2){
if(l1>r1||l2>r2) return null;
int i =l2;
while(inorder[i]!=preorder[l1]){//在中序数组中去寻找根节点
i++;
}
TreeNode root = new TreeNode(preorder[l1]);
root.left = help(preorder,l1+1,l1+i-l2,inorder,l2,i-1);
root.right = help(preorder,l1+i-l2+1,r1,inorder,i+1,r2);
return root;
}
}
help函数。
方法三:
作者:jyd
链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-di-gui-fa-qin/
来源:力扣(LeetCode)
用HashMap,代码:
class Solution {
int[] preorder;
HashMap<Integer, Integer> dic = new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
for(int i = 0; i < inorder.length; i++)
dic.put(inorder[i], i);
return recur(0, 0, inorder.length - 1);
}
TreeNode recur(int root, int left, int right) {
if(left > right) return null; // 递归终止
TreeNode node = new TreeNode(preorder[root]); // 建立根节点
int i = dic.get(preorder[root]); // 划分根节点、左子树、右子树
node.left = recur(root + 1, left, i - 1); // 开启左子树递归
node.right = recur(root + i - left + 1, i + 1, right); // 开启右子树递归
return node; // 回溯返回根节点
}
}
注意:本文方法只适用于 “无重复节点值” 的二叉树。
总结
以上就是本题的内容和学习过程了,虽然有很多简短的方法,但是书中进行替换的思路是值得学习的。
欢迎讨论,共同进步。