这一篇将介绍一些相对复杂的二叉树递归遍历逻辑,相对于上一篇较难。但是如果很好地理解了递归遍历的思想,也很容易找到解题思路。
Leecode257 找出二叉树的所有路径
1.判断递归结束条件:当节点为叶子结点,就可以返回该节点值对应的String集合。否则一直递归调用。
2.递归查找左子树的子路径,当前节点连接左子树中所有子路径,就是当前节点所有路径。
3.递归查找右子树的子路径,当前节点连接右子树中所有子路径,就是当前节点所有路径。
最后返回路径集合。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> list = new ArrayList<String>();
if(root==null) return list;
if(root.left==null&&root.right==null){
list.add(Integer.toString(root.val));
return list;
}
List<String> leftlist = binaryTreePaths(root.left);
for(int i=0;i<leftlist.size();i++){
list.add(Integer.toString(root.val)+"->"+leftlist.get(i));
}
List<String> rightlist = binaryTreePaths(root.right);
for(int i=0;i<rightlist.size();i++){
list.add(Integer.toString(root.val)+"->"+rightlist.get(i));
}
return list;
}
}
Leecode113 路径求和
构造一个辅助函数,记录走过的路程。当走过的路程到达根节点,则将改路程存入list中返回,相应的路程回溯将当前节点去除。
每到达一个节点,都要存储;每返回一次都要进行当前节点退出路径操作。
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> list = new ArrayList<>();
if(root == null ) return list;
Stack<Integer> stack = new Stack<Integer>();
dfs(root,sum,stack,list);
return list;
}
public void dfs(TreeNode node, int sum, Stack<Integer> path, List<List<Integer>> list){
if(node==null) return ;
//结束条件,是叶子且值相等,将当前路径添加到list中
if(node.left==null&&node.right==null&&node.val==sum){
path.add(node.val);
//list.add(path);这么写返回全是null
list.add(new ArrayList(path));
path.pop();
}
path.push(node.val);//添加当前几点再计算
dfs(node.left,sum-node.val,path,list);
dfs(node.right,sum-node.val,path,list);
path.pop();//在递归返回时,将当前节点去除
}
}
Leecode129 跟到叶子节点数字之和
利用上一题的思想:把每条路径记录在list中。返回给主函数,在主函数中遍历list集合进行累加。
(不建议)
public int sumNumbers(TreeNode root) {
List<String> list = binaryTreePaths(root);
int sum = 0;
for(int i= 0;i<list.size();i++){
int s= Integer.parseInt(list.get(i));
sum+=s;
}
return sum;
}
public List<String> binaryTreePaths(TreeNode root) {
List<String> list = new ArrayList<String>();
if(root==null) return list;
if(root.left==null&&root.right==null){
list.add(Integer.toString(root.val));
return list;
}
List<String> leftlist = binaryTreePaths(root.left);
for(int i=0;i<leftlist.size();i++){
list.add(Integer.toString(root.val)+""+leftlist.get(i));
}
List<String> rightlist = binaryTreePaths(root.right);
for(int i=0;i<rightlist.size();i++){
list.add(Integer.toString(root.val)+""+rightlist.get(i));
}
return list;
}
上面的复杂度太大啦。看一下运用遍历三步法思考。
设置一个变量记录所有数据之和sum。
1.结束条件:为叶子结点则返回当前路径的数据,与sum相加。
2.每次向下递归都是数据增加十倍的过程,所以将上一层传递过来的数据*10+当前值;
3.递归左子树;
4.递归右子树;
(容易理解)
class Solution {
private int sum = 0;
private void helper(TreeNode node, int father) {
if (node == null) return ;
int current = father * 10 + node.val;
if (node.left == null && node.right == null) {
sum += current;
return;
}
helper(node.left, current);
helper(node.right, current);
}
public int sumNumbers(TreeNode root) {
if (root == null) return sum;
helper(root, 0);
return sum;
}
}
这个稍微难理解,没向下一层数据扩大。找到根节点就全部相加。
public int sumNumbers(TreeNode root) {
return helper(root, 0);
}
public int helper(TreeNode root, int i){
if (root == null) return 0;
int temp = i * 10 + root.val;
if (root.left == null && root.right == null)
return temp;
return helper(root.left, temp) + helper(root.right, temp);
}
通过非递归实现:
注意:1.二叉树的前序遍历非递归实现通过栈stack来实现;
2.二叉树的层次遍历通过队列queu来实现;
先序遍历
在访问节点的同时,记录走到当前节点的值。当为叶子结点时,sum求和。
class Solution {
private int sum = 0;
public int sumNumbers(TreeNode root) {
if(root==null) return 0;
Stack<TreeNode> stacknode = new Stack<TreeNode>();
Stack<Integer> stack = new Stack<Integer>();
stacknode.push(root);
stack.push(0);
while(!stacknode.isEmpty()){
TreeNode node = stacknode.pop();
int cur = stack.pop();
if(node.left==null&&node.right==null){
int temp = cur*10+node.val;
sum+=temp;
}
if(node.left!=null){
stacknode.push(node.left);
stack.push(cur*10+node.val);
}
if(node.right!=null){
stacknode.push(node.right);
stack.push(cur*10+node.val);
}
}
return sum;
}
}
层次遍历
在访问节点的同时,记录走到当前节点的值。要注意与层次遍历的不同,此处需要记录层次关系。因为层次关系影响着数值的倍数关系,所以每次入栈都需要保证一层入栈后再进行下一层的操作,所以添加了一次while循环,控制每次存取都以层为单位。当为叶子结点时,sum求和。
class Solution {
private int sum = 0;
public int sumNumbers(TreeNode root) {
if(root==null) return 0;
Queue<TreeNode> que = new LinkedList<TreeNode>();
Queue<Integer> num = new LinkedList<Integer>();
que.add(root);
num.add(0);
while(!que.isEmpty()){
//由于涉及到层次对数据*10倍的影响,所以一次讲这一层的所有节点遍历完成并记住其对应的值
int size = que.size();
while(size-->0){
TreeNode node = que.poll();
int cur = num.poll()*10+node.val;
if(node.left==null&&node.right==null)
sum+=cur;
if(node.left!=null){
que.add(node.left);
num.add(cur);
}
if(node.right!=null){
que.add(node.right);
num.add(cur);
}
}
}
return sum;
}
}
更复杂的一些逻辑:
Leecode437 路径三
在这个题目中,与简单的路径计算不同。没有强调必须是从根节点到叶子结点的一条路径,也就是说这条路径可以从半路开始从半路结束。
相比较原来从跟到叶子结点的路径计算逻辑更复杂,重新来分析:
1.当前节点在路径中,那么只需要查找到当前节点在几个路径中,递归的过程需要传递sum-node.val.
2.当前节点不在路径中,那么在递归调用时,传递的值为sum。
class Solution {
public int pathSum(TreeNode root, int sum) {
if(root==null) return 0;
int res = finpath(root,sum);//当前节点在路径中
res+=pathSum(root.left,sum);//当前节点不在路径中
res+=pathSum(root.right,sum);
return res;
}
public int finpath(TreeNode node,int sum){
if(node==null) return 0;
int res = 0;
//不需要是叶子结点,所以遇到相等的值就+1。
//考虑一条路径可能存在子路径。所以不能直接返回1,而应该计数。
if(sum==node.val){
res++;
}
res+=finpath(node.left,sum-node.val);
res+=finpath(node.right,sum-node.val);
return res;
}
}