“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情”
要学习二叉树这个数据结构,本人认为做题是检验理解的最佳方式,其中这道题是比较经典的,也是比较简单的\
剑指 Offer 07. 重建二叉树
题目内容
给出两个整型数组preorder和inorder,其中数组preorder中储存着一棵二叉树的前序遍历序列,inorder数组中则存储该树的中序遍历序列,请根据这两个数组内容还原出这两个序列所代表的二叉树\
思路方法
要解题首先需要知道两种遍历方式的区别 前序遍历是 根节点->左子树->右子树 ,而中序遍历是 左子树->根节点->右子树 那么结合两种方式我们可以知道前序遍历的第一个节点必然是根节点,中序遍历中根节点左边的部分是左子树,右边的部分为右子树。 由这个结论,再结合递归的思想,可以得出如下的方法:
- 根据两种遍历序列找到根节点和左子树右子树
- 把左子树和右子树各自的两种遍历序列分别存进新的四个数组里
- 用新的数组来分别还原左子树和右子树
- 左子树和右子树的还原过程同样遵循第1-3步
具体细节将在代码的注释中体现
代码实现
/**
* 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) {
if (preorder.length == 0) return null;//若是空树则直接返回
TreeNode root = new TreeNode(preorder[0]);//根节点初始化
if (preorder.length == 1) return root;//若子树递归到这里只剩一个节点可以直接返回
int n = preorder.length;
int num = 0;
//假设左子树序列长度为x,该循环是遍历到根节点才结束,所以刚好会遍历到x才结束(左子树的最后一个元素下标为x-1)
for (int i = 0; i < n; i++) {
if (inorder[i] != preorder[0]) {
num++;//num最后的值既是左子树的长度,也是根节点在中序遍历数组中的下标
} else {
break;
}
}
//rnum就是右子树的长度
int rnum = n - num - 1 > 0 ? n - num - 1 : 0;
//分别建立左子树和右子树各自的两种遍历序列
int[] l_pre = new int[num];
int[] r_pre = new int[rnum];
int[] l_in = new int[num];
int[] r_in = new int[rnum];
//前面已经找到了左右子树的序列长度,这里需要做的就是在整棵树的序列中找到范围,把值复制进去即可
//左子树的前序遍历起点:1,终点:num(根节点在前序遍历数组下标为0)
//左子树的中序遍历起点:0,终点:num-1(根节点在中序遍历数组下标为num)
for (int i = 0; i < num; i++) {
l_pre[i] = preorder[i + 1];
l_in[i] = inorder[i];
}
//右子树的前序遍历起点:num+1,终点:n
//右子树的中序遍历起点和终点同上面一致(右子树总是在遍历的最后)
for (int i = num + 1; i < n; i++) {
r_pre[i - num - 1] = preorder[i];
r_in[i - num - 1] = inorder[i];
}
//递归,用上面的过程继续还原左子树和右子树
root.left = buildTree(l_pre, l_in);
root.right = buildTree(r_pre, r_in);
//递归结束,返回根节点
return root;
}
}
熟悉了树的遍历还不够,二叉树的搜索也是需要掌握的
那再看看这道题
剑指 Offer 34. 二叉树中和为某一值的路径
题目内容
思路
本题可以使用深度优先搜索(与前序遍历相似)的思想解决问题,搜索到叶子节点就计算路径上所有节点的和,与target相等的话就存答案的list里面。这里使用递归解法。
具体细节可以看代码注释
代码实现
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> temp=new ArrayList<Integer>();//遍历过程中暂时存放解的list
public List<List<Integer>> ans=new ArrayList<List<Integer>>();//存放所有解的list
//主方法
public List<List<Integer>> pathSum(TreeNode root, int target) {
dfs(root,target);
return ans;
}
//深度优先搜索
/*
遍历整棵树找到路径,把所有路径都存进ans中
*/
public void dfs(TreeNode root, int target){
if(root==null){//遍历到叶子节点
int counter=0;
boolean f=false;
//这里放到最后才加是因为存在空树的情况,在此时单纯判断target(此时样例中target值为0)和counter是否相等会导致长度为0的list被加进去,所以要加上一个布尔类型变量判断路径中是否真的有节点
for(int a:temp){
counter+=a;
f=true;
}
//创建一个新的是因为若直接加temp,那么temp的值发生变化,ans里面相应list也会发生变化
List<Integer> t=new ArrayList<Integer>();
t.addAll(temp);
if(target==counter&&f) ans.add(t);
return;
}
temp.add(root.val);
//防止重复遍历叶子节点(因为叶子节点执行下面的else的话会导致添加两次list,所以用一个笨方法解决)
if(root.left==null&&root.right==null){
dfs(root.left,target);
}else if(root.left==null&&root.right!=null){
dfs(root.right,target);
}else if(root.right==null&&root.left!=null){
dfs(root.left,target);
}else{
dfs(root.left,target);
dfs(root.right,target);
}
temp.remove(temp.size()-1);
}
}