本文链接:https://blog.csdn.net/qq_16488989/article/details/122532560
前言
参考代码随想录/LeetCode作的学习笔记。
特别赞同卡子哥的一句话,初学者刚开始学习算法的时候,看到简单题目没有思路很正常,千万别怀疑自己智商,学习过程都是这样的,大家智商都差不多。慢慢来,一切都会好起来~
文章目录
- 前言
- [原题链接 144.二叉树的前序遍历](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/) 难度级别 easy
- [原题链接 94. 二叉树的中序遍历](https://leetcode-cn.com/problems/binary-tree-inorder-traversal/) 难度级别 easy
- [原题链接 145. 二叉树的后序遍历](https://leetcode-cn.com/problems/binary-tree-postorder-traversal/) 难度级别 easy
- [原题链接 102. 二叉树的层序遍历](https://leetcode-cn.com/problems/binary-tree-level-order-traversal/) 难度级别 Mid
- [原题链接 226. 翻转二叉树](https://leetcode-cn.com/problems/invert-binary-tree/) 难度级别 easy
- [原题链接 101.对称二叉树](https://leetcode-cn.com/problems/symmetric-tree/) 难度级别 easy
- [原题链接:104. 二叉树的最大深度](https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/) 难度级别 easy
- [原题链接-LeetCode 111. 二叉树的最小深度](https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/) 难度级别 easy
- [原题链接 222. 完全二叉树的节点个数](https://leetcode-cn.com/problems/count-complete-tree-nodes/) 难度级别 Mid
- [原题链接 110. 平衡二叉树](https://leetcode-cn.com/problems/balanced-binary-tree/) 难度级别 easy
- [原题链接 515. 在每个树行中找最大值](https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/) 难度级别 Mid
- [原题链接 257. 二叉树的所有路径](https://leetcode-cn.com/problems/binary-tree-paths/) 难度级别 easy
- [原题链接 100. 相同的树](https://leetcode-cn.com/problems/same-tree/) 难度级别 easy
- [原题链接 572. 另一棵树的子树](https://leetcode-cn.com/problems/subtree-of-another-tree/) 难度级别 easy
- [题目链接 404. 左叶子之和](https://leetcode-cn.com/problems/sum-of-left-leaves/) 难度级别 easy
- [原题链接 513. 找树左下角的值](https://leetcode-cn.com/problems/find-bottom-left-tree-value/) 难度级别 Mid
- [原题链接 112. 路径总和](https://leetcode-cn.com/problems/path-sum/) 难度级别 easy
- [题目链接 113.路径总和II](https://leetcode-cn.com/problems/path-sum-ii/) 难度级别 Mid
- 大功告成!贴两张高木作为鼓励~
原题链接 144.二叉树的前序遍历 难度级别 easy
递归
class Solution {
ArrayList<Integer> preOrderReverse(TreeNode root) {
ArrayList<Integer> res= new ArrayList<Integer>();
preOrder(root, res);
return res;
}
void preOrder(TreeNode root, ArrayList<Integer> res) {
if (root == null) return;
res.add(root.val);// 注意这一句是插入值
preOrder(root.left, res);
preOrder(root.right, res);
}
}
迭代
- 栈存储树节点,
- 条件:栈是否为空
- 先入右节点再入左节点,比较巧妙的是每次先出栈,再将它的孩子节点先后入栈
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList();
if(root==null) return res;
Stack<TreeNode> s = new Stack<>();
s.push(root);
while(!s.isEmpty()){
TreeNode temp = s.pop();
res.add(temp.val);
if(temp.right!=null) s.push(temp.right);
if(temp.left!=null) s.push(temp.left);
}
return res;
}
}
原题链接 94. 二叉树的中序遍历 难度级别 easy
递归
class Solution {//中序递归
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res= new ArrayList<>();
inOrder(root,res);
return res;
}
public void inOrder(TreeNode root,List<Integer> l) {
if(root == null) return;
inOrder(root.left,l);
l.add(root.val);
inOrder(root.right,l);
}
}
迭代
- 中序遍历需要先遍历到最左孩子,然后判断栈,再遍历到右孩子,巧妙的是再次走条件时,它能够考虑到右边孩子
- 节点和栈判空作为条件
//很巧妙的左右孩子都访问到了
class Solution {//中序迭代
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res= new ArrayList<>();
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root;
while(cur!=null || !s.isEmpty()){
while(cur!=null){
s.push(cur);
cur = cur.left;//不空就访问它的左孩子
}
if(!s.isEmpty()){
cur = s.pop();
res.add(cur.val);
cur = cur.right;//右边孩子
}
}
return res;
}
}
- 如下图例子解析了代码的过程
原题链接 145. 二叉树的后序遍历 难度级别 easy
递归
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res= new ArrayList();
postorder(root,res);
return res;
}
public void postorder(TreeNode root,List<Integer> res){
if(root == null) return;
postorder(root.left,res);
postorder(root.right,res);
res.add(root.val);
}
}
迭代
巧妙的使用先序中左右->左右中
知识点: 倒序方法,Collections.reverse(res)
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res= new ArrayList();
Stack<TreeNode> s = new Stack<>();
if(root==null) return res;
s.push(root);
while(!s.isEmpty()){
TreeNode cur = s.pop();
res.add(cur.val);
if(cur.left!=null) s.push(cur.left);
if(cur.right!=null) s.push(cur.right);
}
Collections.reverse(res);
return res;
}
}
原题链接 102. 二叉树的层序遍历 难度级别 Mid
思路: 由于返回结果是双层List,所以采用List<List> 结构,每次遍历一层的时候添加到一个list中,遍历完一层再添加到外层List即可。
class Solution {
public List<List<Integer>> resList = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
checkFun(root);
return resList;
}
public void checkFun(TreeNode node){
if(node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
//初始化 len和list
List<Integer> list = new ArrayList<Integer>();
int len = que.size();
//遍历que
while(len > 0){
TreeNode curNode = que.poll();
list.add(curNode.val);
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
len--;
}
resList.add(list);
}
}
}
原题链接 226. 翻转二叉树 难度级别 easy
思路: 层序遍历的基础上添加了swap,将右边和左边的树节点翻转。从上面的例子看,第一次以4为根节点,然后将它的左右子树swap,然后下一次分别以7,2为根节点swap它们的子树。
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null) return root;
//先按照层入队,再逆序依次输出各层次。
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len>0){
TreeNode curNode = que.poll();
swap(curNode);
if(curNode.right!=null) que.offer(curNode.right);
if(curNode.left!=null) que.offer(curNode.left);
len--;
}
}
return root;
}
public TreeNode swap(TreeNode root){
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
return root;
}
}
原题链接 101.对称二叉树 难度级别 easy
思路: 依次检测外面的两个分支,和内部的两个分支。采用双端队列(普通队列也可以)分别存储左右的部分。具体细节见注释。
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null) return true;//空的树也是对称
Deque<TreeNode> deque = new LinkedList<TreeNode>();//双端队列
deque.offerFirst(root.left);//注意用法First 和 Last 分别指两个队列
deque.offerLast(root.right);
while(!deque.isEmpty()){
TreeNode left = deque.pollFirst();
TreeNode right = deque.pollLast();
if(left == null&& right == null){//均空则进行下一次循环
continue;
}
else if(left == null|| right == null||left.val!=right.val){
//三种情况,其中值不同指的是整个左孩子和整个右孩子之间
//比如这样 list1=[...],list2 = [...],list1,list2包含了很多值。
return false;
}
//将孩子插入队列,以便再次进入while循环
deque.offerFirst(left.left);//左右,下一次循环时先出left
deque.offerFirst(left.right);
deque.offerLast(right.right);//右左,下一次循环时先出right,从而达到检测外侧的目的
deque.offerLast(right.left);
}
return true;
}
}
原题链接:104. 二叉树的最大深度 难度级别 easy
层序遍历
标准的层序遍历,每次遍历完一层记录深度即可
class Solution {
public int maxDepth(TreeNode root) {
Queue<TreeNode> que = new LinkedList<TreeNode>();
int depth = 0;
if(root == null) return depth;
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len>0){
TreeNode curNode = que.poll();
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
len--;
}
depth++;
}
return depth;
}
}
后序遍历
这里要搞清楚为什么可以使用后序遍历,因为这里要求的根结点的最大深度,就是树的高度。所以可以使用后序遍历,遍历到根结点。代码很简洁。
class solution {
/**
* 递归法
*/
public int maxdepth(treenode root) {
if (root == null) {
return 0;
}
int leftdepth = maxdepth(root.left);
int rightdepth = maxdepth(root.right);
return Math.max(leftdepth, rightdepth) + 1;
}
}
前序遍历
使用前序遍历实现真正的求最大深度。(为什么说是真正的呢?因为深度就是由根到叶子节点计算的,所以前序遍历是最合适的)。这种做法默认root的深度为1,并且每个节点都有一个深度。每次递归都更新一次最大深度。
class Solution {
public int result = 0;
public int maxDepth(TreeNode root) {
if(root == null) return result;
getdepth(root,1);
return result;
}
void getdepth(TreeNode root,int depth){
result = depth > result?depth:result;
if(root.left == null && root.right == null) return; //结束当前结点的递归
if(root.left!=null){
depth++;
getdepth(root.left,depth);
depth--;
}
if(root.right!=null){
depth++;
getdepth(root.right,depth);
depth--;
}
}
}
回溯过程图:
为了理解这里return的用法和整个遍历过程,我们画图看看。
完美!
原题链接-LeetCode 111. 二叉树的最小深度 难度级别 easy
给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
说明:叶子节点是指没有子节点的节点。
递归法思路:
注意这里有一个陷阱。
上图,左子树为空,最小深度不是1而是2(红线部分)
定义最小深度是指根节点到叶子节点之间的距离,主要分为两种情况
- 左子树空,右子树不空,返回右子树最小深度+1
- 右子树空,左子树不空,返回左子树最小深度+1
最后返回其他情况,左右子树深度的最小值+1
//递归
class Solution{
public int minDepth(TreeNode root){
if(root==null) return 0;
TreeNode left = root.left;
TreeNode right = root.right;
if(left == null && right!=null){
return minDepth(right)+1;
}
if(left!=null && right==null){
return minDepth(left)+1;
}
return Math.min(minDepth(left),minDepth(right))+1;
}
}
迭代法思路:
采用层序遍历,到当前节点时左右孩子均为空时,说明到达了最低点,返回深度即可。
class Solution {
public int minDepth(TreeNode root) {
int depth = 0;
if(root == null) return depth;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
depth++;
while(len>0){
TreeNode curNode = que.poll();
if(curNode.left==null&&curNode.right==null) return depth;
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
len--;
}
}
return depth;
}
原题链接 222. 完全二叉树的节点个数 难度级别 Mid
思路: 使用普通的二叉树计算节点数的方法就不说啦(可以采用层次遍历,遍历结点的时候计算即可,其他遍历方式也可以),这里主要用到了完全二叉树的性质,它由满二叉树组成。计算满二叉树的深度的方法为2^depth - 1。具体细节见代码注释。
class Solution {
public int countNodes(TreeNode root) {
if(root == null) return 0;
TreeNode left = root.left;
TreeNode right = root.right;
int leftDepth = getDepth(left);//分别计算深度
int rightDepth = getDepth(right);
//如果左边和右边的深度相同,说明是上图的情况
//如果不相同,说明左子树的下端有多出来的结点,且右子树为满二叉树
if(leftDepth == rightDepth) return (1<<leftDepth) +countNodes(right);
else return (1<<rightDepth) + countNodes(left);
}
int getDepth(TreeNode root){//因为完全二叉树的性质,它的最左边总是有节点的,所以一直向左走就可以计算其深度。
int depth = 0;
TreeNode cur = root;
while(cur!=null){
depth++;
cur = cur.left;
}
return depth;
}
}
原题链接 110. 平衡二叉树 难度级别 easy
高度与深度的区别:
- 首先从高度和深度的定义上,高度和深度是相反的表示,深度是从上到下数的,而高度是从下往上数的。如下图,根结点开始算深度,和以4为开始的叶子节点开始算高度。
- 根节点是没有父节点的节点。根结点的深度为1。
- 虽然树的深度和高度一样,但是具体到树的某个节点,其深度和高度是不一样的。
- 虽然定义不同,有时候深度高度不同,但是它们的目标值有时候处理问题时是相同的,比如求下图1的左子树的高度,就可以用深度来求,具体情况还需要多研究题总结。
递归思路: 如果是单纯的求一个树的高度,用迭代法是ok的。但是这里要求多个节点的高度,再用迭代法就较为复杂了。所以本题使用递归法来解决,先判断左右子树是否是平衡二叉树,再判断整个树本身是否满足平衡二叉树,若满足则求得当前节点的高度,并回溯到上一次递归中。具体细节见代码注释。
class Solution{
public boolean isBalanced(TreeNode root){
return getHeight(root) == -1?false:true;
}
int getHeight(TreeNode root){
if(root == null) return 0;
int result = 0;
int leftheight = getHeight(root.left);//判断子树是否满足平衡二叉树
if(leftheight == -1) return -1; //如果子树不满足,则整个树也不满足
int rightheight = getHeight(root.right);
if(rightheight == -1) return -1;
if(Math.abs(leftheight-rightheight)>1) result = -1;
else result = Math.max(leftheight,rightheight) + 1;//加上根的高度1,通过这里计算高度的
return result;
}
}
原题链接 515. 在每个树行中找最大值 难度级别 Mid
思路: 采用层序遍历,每层计算得到最大值,然后每层遍历完后加入到结果列表中。注意,初始最大值设置为-(1 << 31)。
class Solution {
public List<Integer> largestValues(TreeNode root) {
List<Integer> reslist = new ArrayList<Integer>();
if(root == null) return reslist;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
int max = -(1 << 31);
while(len>0){
TreeNode curNode = que.poll();
max = curNode.val>max?curNode.val:max;
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
len--;
}
reslist.add(max);
}
return reslist;
}
}
原题链接 257. 二叉树的所有路径 难度级别 easy
递归思路: 终止条件为左右子孩子均为空,即找到叶子节点,当达到条件时将path加入到结果集paths中。注意path中用字符串。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> paths = new ArrayList<String>();
searchWay(root,"",paths);
return paths;
}
void searchWay(TreeNode root,String path,List<String> paths){
if(root!=null){
StringBuilder sb = new StringBuilder(path);
sb.append(Integer.toString(root.val));
if(root.left == null&&root.right == null){//当前节点时叶子节点
paths.add(sb.toString());//把路径加入到答案中
}
else{
sb.append("->");
searchWay(root.left,sb.toString(),paths);
searchWay(root.right,sb.toString(),paths);
}
}
}
}
迭代
采用两个队列分别存储结点 和 字符串路径。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> paths = new ArrayList<String>();
if(root == null) return paths;
Queue<TreeNode> nodeQueue = new LinkedList<TreeNode>();
Queue<String> pathQueue = new LinkedList<String>();
nodeQueue.offer(root);
pathQueue.offer(Integer.toString(root.val));
while(!nodeQueue.isEmpty()){
TreeNode curnode = nodeQueue.poll();
String curpath = pathQueue.poll();
if(curnode.left == null&&curnode.right == null){
paths.add(curpath);
}
if(curnode.left!=null){
nodeQueue.offer(curnode.left);
pathQueue.offer(new StringBuffer(curpath).append("->").append(curnode.left.val).toString());
}
if(curnode.right!=null){
nodeQueue.offer(curnode.right);
pathQueue.offer(new StringBuffer(curpath).append("->").append(curnode.right.val).toString());
}
}
return paths;
}
}
注意:这里均用到了StringBuilder 是为了方便添加String字符串。
原题链接 100. 相同的树 难度级别 easy
按顺序遍历所有节点(包括空节点)(自己的错误思路)
以相同的次序依次遍历两颗树,记录结点值,再比较。就用层序遍历,但是这样必须得遍历完一棵树。如果遇到null就添加-1。
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
//首先结点的值要相同,可以层序遍历一遍,每个位置结点的值是否相同,但是要遍历两颗树?开辟两个空间?
//还是用递归比较好,但是也要遍历两颗树?
//同时遍历两颗树绝对不行,同时拿两棵树对比也肯定不行
//以相同的次序依次遍历两颗树,记录结点值,再比较。就用层序遍历,但是这样必须得遍历完一棵树
List<Integer> resp = new ArrayList<>();
List<Integer> resq = new ArrayList<>();
resp = search(p);
resq = search(q);
System.out.println(resp);
System.out.println(resq);
if(resp.size() == resq.size()){
for(int a:resq){
if(!resp.contains(a)) return false;
}
}
return true;
}
//层序遍历
public List<Integer> search(TreeNode root){
List<Integer> res = new ArrayList<>();
if(root == null){
res.add(-1);
return res;
}
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len>0){
TreeNode node = que.poll();
res.add(node.val);
if(node.left==null) res.add(-1);
else que.offer(node.left);
if(node.right==null) res.add(-1);
else que.offer(node.right);
len--;
}
}
return res;
}
}
本身分别得到的结果应该是[1,2,-1,-1,-1]和[1,-1,2,-1,-1]的,但是由于程序遍历这里不是用递归,没有回溯。当遍历到空节点时就加入-1,不能做到先遍历完左子树的全部结点,再去遍历右子树的内容。所以它不能判别对称的情况。
if(node.left==null) res.add(-1);
else que.offer(node.left);
if(node.right==null) res.add(-1);
else que.offer(node.right);
所以我的想法是应该采用前序遍历+后序遍历确定一棵树。但是这种思路也比较麻烦。下面看正确思路。
正确思路(递归法和迭代法)
递归
分别比较两棵树的左子树、右子树。与对称二叉树相似的解法(做了一点点小改变)。
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
return compare(p,q);
}
public boolean compare(TreeNode left,TreeNode right){
// 首先排除空节点的情况
if (left == null && right != null) return false;
else if (left != null && right == null) return false;
else if (left == null && right == null) return true;
// 排除了空节点,再排除数值不相同的情况
else if (left.val != right.val) return false;
boolean outside = compare(left.left, right.left); // 左子树:左、 右子树:左 (相对于求对称二叉树,只需改一下这里的顺序)
boolean inside = compare(left.right, right.right); // 左子树:右、 右子树:右
boolean isSame = outside && inside; // 左子树:中、 右子树:中 (逻辑处理)
return isSame;
}
}
迭代
采用双端队列,注意遍历孩子节点也需要同样的判断。
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q==null) return true;//空的树也是对称
if(p==null || q==null) return false;
Deque<TreeNode> deque = new LinkedList<TreeNode>();//双端队列
deque.offerFirst(p);//注意用法First 和 Last 分别指两个队列
deque.offerLast(q);
while(!deque.isEmpty()){
TreeNode curp = deque.pollFirst();
TreeNode curq = deque.pollLast();
if(curp == null && curq == null){
continue;
}
else if(curp == null|| curq == null||curp.val!=curq.val){
//三种情况,其中值不同指的是整个左孩子的值
return false;
}
//将孩子插入队列,以便再次进入while循环
deque.offerFirst(curp.left);//左右,下一次循环时先出left
deque.offerFirst(curp.right);
deque.offerLast(curq.left);
deque.offerLast(curq.right);
}
return true;
}
}
原题链接 572. 另一棵树的子树 难度级别 easy
过一半用例(仅仅作为学习记录)
用层序遍历得到大树的每个节点,然后再用这每个节点为根的树与小树比较是否是结构相同且值相同的树。(子树称作小树,另一颗树称作大树)
//这种解法 是包含这个部分,但是不是小树
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(subRoot == null) return true;
if(root == null) return false;
//先层序遍历得到树root的所有节点,然后让它为根的多个子树与subRoot比较
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
while(len>0){
TreeNode curNode = que.poll();
compare(curNode,subRoot);
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
len --;
}
}
return true;
}
public boolean compare(TreeNode root,TreeNode subRoot){
if(root == null&&subRoot == null) return true;
if(root == null||subRoot == null) return false;
if(subRoot.val != root.val) return false;
boolean left = compare(root.left,subRoot.left);
boolean right = compare(root.right,subRoot.right);
boolean isSubtr = left && right;
return isSubtr;
}
}
也即是这种情况,表现为包含这个部分[4,1,2]
主要问题是public boolean isSubtree(TreeNode root, TreeNode subRoot) 函数的返回部分,已经在循环中处理了所有情况,并且应该可以获得一定的返回,结尾return不知道该怎么处理。
总结 :本方案思路没有问题,但是如何使用大树的每个节点 去和小树比较是个问题。此外,解法又是迭代的层序遍历又是递归的深度,有些乱,仅仅作为学习记录。详情见正确答案。
看了答案的return,来了灵感,代码如下,更加简洁。
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(subRoot == null) return true;
if(root == null) return false;
//不要用层次遍历,采用其它遍历试试
return compare(root,subRoot)||isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
}
public boolean compare(TreeNode root,TreeNode subRoot){
if(root == null&&subRoot == null) return true;
if(root == null||subRoot == null) return false;
if(subRoot.val != root.val) return false;
boolean left = compare(root.left,subRoot.left);
boolean right = compare(root.right,subRoot.right);
boolean isSubtr = left && right;
return isSubtr;
}
}
正确方案(如何使用大树的每个节点去和小树比较)
类似的题,对称二叉树101,相同的树100
专门写了一个dfs用来深度遍历!太秒了!用或运算来兼顾所有情况。
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
return dfs(root,subRoot);
}
public boolean dfs(TreeNode s, TreeNode t) {
if (s == null) {
return false;
}
return compare(s, t) || dfs(s.left, t) || dfs(s.right, t);
}
//遍历左右子树的常规做法(如对称二叉树101,相同的树100)
public boolean compare(TreeNode root,TreeNode subRoot){
if(root == null&&subRoot == null) return true;
if(root == null||subRoot == null) return false;
if(subRoot.val != root.val) return false;
boolean left = compare(root.left,subRoot.left);
boolean right = compare(root.right,subRoot.right);
boolean isSubtr = left && right;
return isSubtr;
}
}
看了答案的return,来了灵感,代码如下,更加简洁。
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(subRoot == null) return true;
if(root == null) return false;
//不要用层次遍历,采用其它遍历试试
return compare(root,subRoot)||isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
}
public boolean compare(TreeNode root,TreeNode subRoot){
if(root == null&&subRoot == null) return true;
if(root == null||subRoot == null) return false;
if(subRoot.val != root.val) return false;
boolean left = compare(root.left,subRoot.left);
boolean right = compare(root.right,subRoot.right);
boolean isSubtr = left && right;
return isSubtr;
}
}
Nice! 进步很大!
题目链接 404. 左叶子之和 难度级别 easy
迭代法
采用先序遍历。搞清楚条件就解决了:左孩子不空并且左孩子的左右孩子都为空。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
//首先得是左孩子,然后它的左右孩子都为空
//采用前序遍历
int result = 0;
if(root == null) return 0;//result不变
Stack<TreeNode> s = new Stack<TreeNode>();
s.push(root);
while(!s.isEmpty()){
TreeNode curNode = s.pop();
if(curNode.left!=null && curNode.left.left==null && curNode.left.right == null){
result += curNode.left.val;
}
if(curNode.right!=null) s.push(curNode.right);
if(curNode.left!=null) s.push(curNode.left);
}
return result;
}
}
递归法
同样是前序遍历。
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) return 0;
int midValue = 0;
if (root.left != null && root.left.left == null && root.left.right == null) {
midValue = root.left.val;
}
return midValue + sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
}
}
原题链接 513. 找树左下角的值 难度级别 Mid
迭代
层序遍历非常方便,直接输出第一个元素值,最后一层时即可获得结果。
class Solution {
public int findBottomLeftValue(TreeNode root) {
if(root == null) return 0;
int result = 0;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int len = que.size();
for(int i = 0;i<len;i++){
TreeNode curNode = que.poll();
if(i == 0) result = curNode.val;
if(curNode.left!=null) que.offer(curNode.left);
if(curNode.right!=null) que.offer(curNode.right);
}
}
return result;
}
}
原题链接 112. 路径总和 难度级别 easy
迭代 采用前序遍历。采用两个栈,同步记录每个节点的当前路径值。(也可以使用队列,广度遍历)
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
//前序遍历
if(root == null) return false;
Stack<TreeNode> s1 = new Stack<>();
Stack<Integer> s2 = new Stack<>();
s1.push(root);s2.push(root.val);
while(!s1.isEmpty()){
TreeNode curNode = s1.pop();
int sum = s2.pop();
if(curNode.left==null && curNode.right ==null && sum == targetSum) return true;
if(curNode.right!=null){//压进栈时记录它的路径值
s1.push(curNode.right);
s2.push(sum+curNode.right.val);
}
if(curNode.left!=null){
s1.push(curNode.left);
s2.push(sum+curNode.left.val);
}
}
return false;
}
}
递归
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
//前序遍历
if(root == null) return false;
if(root.left == null && root.right == null && root.val == targetSum) return true;
return hasPathSum(root.left,targetSum-root.val)||hasPathSum(root.right,targetSum-root.val);
}
}
题目链接 113.路径总和II 难度级别 Mid
前序遍历
path更新过程:
[5,]
[5,4]
[5,4,11]
[5,4,11,7]
因为7是叶子节点,travesal(root.left, count);travesal(root.right, count);均在遇到if (root == null) return;后就返回了,所以回溯
[5,4,11]
[5,4,11,2]添加到result中
回溯
[5,4,11]
回溯
[5,4]
回溯
[5]
[5,8]
[5,8,3]
回溯
[5,8,4]
[5,8,4,5]添加到result中
回溯
[5,8,4]
[5,8,4,1]
遍历结束
// 解法2
class Solution {
List<List<Integer>> result;
LinkedList<Integer> path;
public List<List<Integer>> pathSum (TreeNode root,int targetSum) {
result = new LinkedList<>();
path = new LinkedList<>();
travesal(root, targetSum);
return result;
}
private void travesal(TreeNode root, int count) {
if (root == null) return;
path.offer(root.val);
count -= root.val;
if (root.left == null && root.right == null && count == 0) {
result.add(new LinkedList<>(path));
}
travesal(root.left, count);
travesal(root.right, count);
path.removeLast(); // 回溯
}
}
需要注意,这里的path 和 result都是LinkedList来存储,以便path使用removeLast方法删除最后一个元素。
大功告成!贴两张高木作为鼓励~