目录
系列文章目录
刷题笔记(一)–数组类型:二分法
刷题笔记(二)–数组类型:双指针法
刷题笔记(三)–数组类型:滑动窗口
刷题笔记(四)–数组类型:模拟
刷题笔记(五)–链表类型:基础题目以及操作
刷题笔记(六)–哈希表:基础题目和思想
刷题笔记(七)–字符串:经典题目
刷题笔记(八)–双指针:两数之和以及延伸
刷题笔记(九)–字符串:KMP算法
刷题笔记(十)–栈和队列:基础题目
刷题笔记(十一)–栈和队列:Top-K问题
刷题笔记(十二)–复习:排序算法
刷题笔记(十三)–二叉树:前中后序遍历(复习)
刷题笔记(十四)–二叉树:层序遍历和DFS,BFS
前言
上面讲了关于二叉树的层序遍历问题,这一篇博客在上篇博客基础上对二叉树的属性进行了探讨,比如他的高度,深度,子树,平衡二叉树等等。
题录
101. 对称二叉树
题目链接如下:
题目截图如下:
这个题目是关于对称二叉树的,
递归写法
public class 对称二叉树_递归 {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return false;
}
return DFS(root.left,root.right);
}
public boolean DFS(TreeNode left,TreeNode right){
//如果说左右子树都为Null,那就是true
if(left == null && right == null){
return true;
}
//如果说其中有有一个为空,那就是flase
if(left == null || right == null){
return false;
}
//如果当前节点不相同,就返回false
if(left.val != right.val){
return false;
}
//镜像对称,所以是左子树的左子树和右子树的右子树比对,左子树的右子树和右子树的左子树比对
return DFS(left.left,right.right) && DFS(left.right,right.left);
}
}
好像有点多,那换种写法
class Solution {
public boolean isSymmetric(TreeNode root) {
return DFS(root.left,root.right);
}
public boolean DFS(TreeNode left,TreeNode right){
if(left == null && right == null) return true;
if(left != null && right != null && left.val == right.val){
return DFS(left.left,right.right) && DFS(left.right,right.left);
}
return false;
}
}
迭代写法
public class 对称二叉树_迭代 {
public boolean isSymmetric(TreeNode root) {
if(root == null) return false;//如果为空直接返回false
Queue<TreeNode> q1 = new LinkedList<>();
Queue<TreeNode> q2 = new LinkedList<>();
q1.offer(root.left);
q2.offer(root.right);
while(!q1.isEmpty() && !q2.isEmpty()){
TreeNode lefe = q1.poll();
TreeNode right = q2.poll();
if(lefe == null && right == null) continue;//如果两个都为空就直接进入下次循环
if(lefe == null || right == null) return false;//如果有一个为空另外一个不为空就返回flase
if(lefe.val != right.val) return false;//不相等直接返回好吧
//这里保证了左右子树都不为空,所以开始把左右子树按照一定的顺序入队列
q1.offer(lefe.left);
q1.offer(lefe.right);
q2.offer(right.right);
q2.offer(right.left);
}
return q1.isEmpty() && q2.isEmpty();
}
}
100. 相同的树
题目链接如下:
题目截图如下:
这个题目和上面题目比较不同的地方就在于对于节点的访问顺序要能理解。
递归写法
public class 相同的树_递归 {
public boolean isSameTree(TreeNode p, TreeNode q) {
return isSame(p,q);
}
public boolean isSame(TreeNode left,TreeNode right){
//如果左右子树为空就肯定相同
if(left == null && right == null) return true;
//如果左右子树不为空并且值相等就继续去检测对应的左右子树
if(left != null && right != null && left.val == right.val){
return isSame(left.left,right.left) && isSame(left.right,right.right);
}
return false;
}
}
迭代写法
public class 相同的树_迭代 {
public boolean isSameTree(TreeNode p, TreeNode q) {
Queue<TreeNode> q1 = new LinkedList<>();
Queue<TreeNode> q2 = new LinkedList<>();
//不为空才可以入栈
if(p != null) q1.offer(p);
if(q != null) q2.offer(q);
while(!q1.isEmpty() && !q2.isEmpty()){
TreeNode node1 = q1.poll();
TreeNode node2 = q2.poll();
//如果当前两个节点都为null,就直接进入下次比较就行
if(node1 == null && node2 == null) continue;
//如果两个节点同时不为空并且值相等,就把左右子树入队列
if(node1 != null && node2 != null && node1.val == node2.val){
q1.offer(node1.left);
q1.offer(node1.right);
q2.offer(node2.left);
q2.offer(node2.right);
}else {
return false;
}
}
return q1.isEmpty() && q2.isEmpty();
}
}
572. 另一棵树的子树
题目链接如下:
题目截图如下:
这里的话递归加迭代一起写,都用一下,就不分开单独写了。
public class 另一棵树的子树_迭代写法 {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
Queue<TreeNode> queue = new LinkedList<>();//用来遍历第一个二叉树
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
//这里用迭代来写母树的遍历
if(node != null){
queue.offer(node.left);
queue.offer(node.right);
if(node.val == subRoot.val && isSame(node,subRoot)){
return true;
}
}
}
return false;
}
//这里用递归来写两颗二叉树的是否想同的遍历过程
public boolean isSame(TreeNode node1,TreeNode node2){
if(node1 == null && node2 == null) return true;
if(node1 != null && node2 != null && node1.val == node2.val){
return isSame(node1.left,node2.left) && isSame(node1.right,node2.right);
}
return false;
}
}
算了,这里再写一下纯递归的写法。
public class 另一棵树的子树 {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
//因为题目提前就已经说了,两棵树都不为空,所以这里就单单让母树不为空就行
if(root == null) return false;
//这里的话注意一下方法的名称
return isSame(root,subRoot) || isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot);
}
//这里用递归来写两颗二叉树的遍历过程
public boolean isSame(TreeNode node1,TreeNode node2){
if(node1 == null && node2 == null) return true;
if(node1 != null && node2 != null && node1.val == node2.val){
return isSame(node1.left,node2.left) && isSame(node1.right,node2.right);
}
return false;
}
}
104. 二叉树的最大深度
题目链接如下:
题目截图如下:
还是和上面的题一样的思路
递归写法
public class 二叉树的最大深度 {
public int maxDepth(TreeNode root) {
return DFS(root);
}
public int DFS(TreeNode root){
//如果说节点是null就返回0,不然返回左右子树的最大高度
return root == null ? 0 : Math.max(DFS(root.left),DFS(root.right)) + 1;
}
}
迭代写法
其实还是老规矩,层序遍历的基础上进行延展
public class 二叉树的最大深度_迭代写法 {
public int maxDepth(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) return 0;
queue.offer(root);
int count = 0;
while (!queue.isEmpty()){
//注意,当前的队列里面存储的一层的元素,所以说只要队列不为空,那么这一层就有元素
count++;
int size = queue.size();//准备遍历当前层次的所有节点
while(size-- > 0) {
TreeNode node = queue.poll();
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue .offer(node.right);
}
}
return count;
}
}
111. 二叉树的最小深度
题目链接如下:
题目截图如下:
这个题目和上面的题目是一样的,就是对最大深度做了一些改变。要注意关于叶子结点的判断
递归写法
public class 二叉树的最小深度_递归写法 {
public int minDepth(TreeNode root) {
return DFS(root);
}
public int DFS(TreeNode root){
if(root == null) return 0;//如果当前节点为叶子结点就返回0
//如果当前节点的左子树不为空,右子树为空那就往左遍历
if(root.left != null && root.right == null) return DFS(root.left) + 1;
//如果当前节点的右子树不为空,左子树为空那就往右遍历
if(root.left == null && root.right != null) return DFS(root.right) + 1;
//如果都不为空就选择小的哪一个
return Math.min(DFS(root.left),DFS(root.right)) + 1;
}
}
迭代写法
还是一样的思路
public int minDepth(TreeNode root) {
if(root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int count = 0;//记录一下层数
while(!queue.isEmpty()){
count++;//当前队列不为空,那么这一层肯定就是有元素的。
int size = queue.size();//遍历当前层次元素
while(size-- > 0){
TreeNode node = queue.poll();
if(node.left == null && node.right == null) return count;//这是碰到叶子结点啦
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
}
return count;
}
222. 完全二叉树的节点个数
题目链接如下:
题目截图如下:
递归写法
public class 完全二叉树的节点个数_递归写法 {
public int countNodes(TreeNode root) {
return DFS(root);
}
public int DFS(TreeNode root){
//如果当前节点为null,那就返回0
if(root == null) return 0;
//不为null,就返回左右子树个数+1
return DFS(root.left) + DFS(root.right) + 1;
}
}
迭代写法
public int countNodes(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root != null) queue.offer(root);
int count = 0;//用来记录节点个数
while(!queue.isEmpty()){
count++;
TreeNode node = queue.poll();
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
return count;
}
110. 平衡二叉树
题目链接如下:
题目截图如下:
递归写法
public class 平衡二叉树_递归写法 {
public boolean isBalanced(TreeNode root) {
if(root == null) return true;
//左右子树高度差不超过1 && 左/右子树的子树高度差不超过1
return Math.abs(func(root.left) - func(root.right)) > 1 && isBalanced(root.left) && isBalanced(root.right);
}
public int func(TreeNode node){//求树的高度
return node == null ? 0 : Math.max(func(node.left),func(node.right)) + 1;
}
}
递归优化
优化的原因是因为每一个节点会被遍历两次
public class 平衡二叉树_迭代写法 {
public boolean isBalanced(TreeNode root) {
//用-1来判断是否高度差是否大于1
return solve(root) != -1;
}
public int solve(TreeNode root){
if(root == null) return 0;
//拿到左子树的高度值,如果为-1就证明左子树的子树高度差大于1
int left_high = solve(root.left);
if(left_high == -1) return -1;
//拿到右子树的高度值,如果为-1就证明右子树的子树高度差大于1
int right_high = solve(root.right);
if(right_high == -1) return -1;
//如果说左右子树的高度差大于1,就返回-1
if(Math.abs(left_high - right_high) > 1) return -1;
//如果为平衡二叉树就去检验子树是否是平衡二叉树
return Math.max(solve(root.left),solve(root.right)) + 1;
}
}
257. 二叉树的所有路径
题目链接如下:
题目截图如下:
这个题目其实挺有意思的,方法还不少。
递归写法–DFS
public class 二叉树的所有路径_递归写法 {
public List<String> binaryTreePaths(TreeNode root) {
List<String> list = new ArrayList<>();
if(root == null) return list;
solve(root,"",list);
return list;
}
public void solve(TreeNode node,String s,List<String> list){
if(node == null) return;
s += node.val;
//如果说当前结点为叶子结点,那么就把当前节点的String加入到字符顺序表中
if(node.left == null && node.right == null) list.add(s);
else{
//不为叶子结点,就往下走就行
solve(node.left,s+"->",list);
solve(node.right,s+"->",list);
}
}
}
迭代写法–BFS
public class 二叉树的所有路径_迭代写法 {
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 node = nodeQueue.poll();
String path = pathQueue.poll();
if (node.left == null && node.right == null) {
paths.add(path);
} else {
if (node.left != null) {
nodeQueue.offer(node.left);
pathQueue.offer(new StringBuffer(path).append("->").append(node.left.val).toString());
}
if (node.right != null) {
nodeQueue.offer(node.right);
pathQueue.offer(new StringBuffer(path).append("->").append(node.right.val).toString());
}
}
}
return paths;
}
}
404. 左叶子之和
题目链接如下:
题目截图如下:
递归写法
public class 左叶子之和_递归写法 {
int val;
public int sumOfLeftLeaves(TreeNode root) {
solve(root);
return val;
}
public void solve(TreeNode root){
//如果当前节点为null就直接返回
if(root == null) return;
//如果是左叶子节点就加到val上去
if(root.left != null && root.left.left == null && root.left.right == null) val += root.left.val;
//递归调用左右子树
solve(root.left);
solve(root.right);
}
}
迭代写法
public class 左叶子之和_迭代写法 {
public int sumOfLeftLeaves(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
if(root == null) return 0;
queue.offer(root);
int val = 0;//用来统计所有的左叶子之和
while(!queue.isEmpty()){
int size = queue.size();
while(size-- > 0){
TreeNode node = queue.poll();
//如果当前节点的左子树为叶子结点,那么计入它的值
if(node.left != null && node.left.left == null && node.left.right == null) val += node.left.val;
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
}
}
return val;
}
}
513. 找树左下角的值
题目链接如下:
题目截图如下:
这个题目还挺有意思的,它和上一篇博客里的题目颇有相似之处,
递归写法
public class 找树左下角的值_递归写法 {
int num = 0;//时刻记录最左边的值
int level = 0;//level记录已经被遍历过的层次
public int findBottomLeftValue(TreeNode root) {
solve(root,1);//先从1开始遍历,假设0层已经被遍历过了
return num;
}
public void solve(TreeNode node,int height){
if(node == null) return;
if(height > level){//如果说height > level,那么证明当前递归是这个层次的第一个递归,就要更新数据
num = node.val;
level = height;
}
solve(node.left,height+1);
solve(node.right,height+1);
}
}
迭代写法
public class 找树左下角的值_迭代写法 {
public int findBottomLeftValue(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int num = 0;//实时更新每一层最左边的节点值
while(!queue.isEmpty()){
int size = queue.size();
int key = size;
while(size > 0){
TreeNode node = queue.poll();
//如果size == key,证明是当前层次的第一个遍历元素,就要更新值
if(size == key) num = node.val;
if(node.left != null) queue.offer(node.left);
if(node.right != null) queue.offer(node.right);
size--;
}
}
return num;
}
}
112. 路径总和
题目链接如下:
题目截图如下:
递归写法
public class 路径总和_递归写法 {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
List<Integer> list = new ArrayList<>();//用一个顺序表来存储每个路径的和
solve(root,0,list);
for(Integer a:list) {
if(a == targetSum){
return true;
}
}
return false;
}
public void solve(TreeNode node,int sum,List<Integer> list){
if(node == null) return;
sum += node.val;//不为空就存储当前的值
if(node.left == null && node.right == null){
list.add(sum);
} else{
solve(node.left,sum,list);
solve(node.right,sum,list);
}
}
}
迭代写法
public class 路径总和_迭代写法 {
public boolean hasPathSum(TreeNode root, int sum) {
if (root == null) {
return false;
}
Queue<TreeNode> queNode = new LinkedList<TreeNode>();
Queue<Integer> queVal = new LinkedList<Integer>();
queNode.offer(root);
queVal.offer(root.val);
while (!queNode.isEmpty()) {
TreeNode now = queNode.poll();
int temp = queVal.poll();
if (now.left == null && now.right == null) {
if (temp == sum) {
return true;
}
continue;
}
if (now.left != null) {
queNode.offer(now.left);
queVal.offer(now.left.val + temp);
}
if (now.right != null) {
queNode.offer(now.right);
queVal.offer(now.right.val + temp);
}
}
return false;
}
}