二叉树的理论基础
二叉树的种类,存储方法,遍历方式,定义
二叉树的遍历
分为两种:深度和广度
深度分为三种:前序,中序,后序
处理问题的思路又分为两种:迭代和递归
迭代:要理解递归的思路并且熟练的使用它,就是要想清楚你想做什么(处理逻辑),什么时候停止。
递归:三要素,参数(决定了这个递归函数是否要独立写出来),结束条件,处理逻辑(可以先凶从二叉树处理分析逻辑)
就是迭代更直接,更好理解;
递归不好理解,但是代码很套路,摸清三要素
迭代
前序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root){
if (root == null){
return res;
}
preorder(root);
return res;
}
public void preorder(TreeNode root){
if(root==null)
return ;
res.add(root.val);
preorder(root.left);
preorder(root.right);
}
}
中序
class Solution {
List<Integer> result = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
if (root == null){
return result;
}
inorder(root);
return result;
}
public void inorder(TreeNode root){
if(root==null)
return ;
inorder(root.left);
result.add(root.val);
inorder(root.right);
}
}
后序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
if (root == null){
return res;
}
postorder(root);
return res;
}
public void postorder(TreeNode root){
if(root==null) return ;
postorder(root.left);
postorder(root.right);
res.add(root.val);
}
}
层序
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null) return res;
Queue<TreeNode> quenue=new LinkedList<TreeNode>();
quenue.offer(root);
while(!quenue.isEmpty()){
int len=quenue.size();
List<Integer> aslist=new ArrayList<Integer>();
while(len>0){
TreeNode node=quenue.poll();
aslist.add(node.val);
len--;
if(node.left!=null) quenue.offer(node.left);
if(node.right!=null) quenue.offer(node.right);
}
res.add(aslist);
}
return res;
}
}
递归
借助栈这个存储空间
这里我选择使用统计递归的那个代码,只需要记住一个就行了,就和迭代的代码一样,只需要改变中左右的顺序就可以实现遍历功能
前序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> preorderTraversal(TreeNode root){
if (root == null){
return res;
}
Stack<TreeNode> stack=new Stack<>();
if(root!=null) stack.push(root);
while(!stack.isEmpty()){
TreeNode node=stack.peek();
if(node!=null){
stack.pop();
if(node.right!=null) stack.push(node.right);
if(node.left!=null) stack.push(node.left);
stack.push(node);
stack.push(null);
}else{
stack.pop();
TreeNode n=stack.peek();
stack.pop();
res.add(n.val);
}
}
return res;
}
}
中序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root){
if (root == null){
return res;
}
Stack<TreeNode> stack=new Stack<>();
if(root!=null) stack.push(root);
while(!stack.isEmpty()){
TreeNode node=stack.peek();
if(node!=null){
stack.pop();
if(node.right!=null) stack.push(node.right);
stack.push(node);
stack.push(null);
if(node.left!=null) stack.push(node.left);
}else{
stack.pop();
TreeNode n=stack.peek();
stack.pop();
res.add(n.val);
}
}
return res;
}
}
后序
class Solution {
List<Integer> res = new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root){
if (root == null){
return res;
}
Stack<TreeNode> stack=new Stack<>();
if(root!=null) stack.push(root);
while(!stack.isEmpty()){
TreeNode node=stack.peek();
if(node!=null){
stack.pop();
stack.push(node);
stack.push(null);
if(node.right!=null) stack.push(node.right);
if(node.left!=null) stack.push(node.left);
}else{
stack.pop();
TreeNode n=stack.peek();
stack.pop();
res.add(n.val);
}
}
return res;
}
}
层序
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res=new ArrayList<>();
if(root==null) return res;
dfs(1,root,res);
return res;
}
public void dfs(int index,TreeNode root,List<List<Integer>> res){
if(res.size()<index){
res.add(new ArrayList<>());
}
res.get(index-1).add(root.val);
if(root.left!=null)
dfs(index+1,root.left,res);
if(root.right!=null)
dfs(index+1,root.right,res);
}
}
二叉树的属性
对称二叉树
迭代
借助队列去实现
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root.left);
queue.offer(root.right);
while(!queue.isEmpty()){
TreeNode left=queue.poll();
TreeNode right=queue.poll();
if(left==null&&right==null)
continue;
if(left==null||right==null)
return false;
if(left.val!=right.val)
return false;
queue.offer(left.left);
queue.offer(right.right);
queue.offer(left.right);
queue.offer(right.left);
}
return true;
}
}
递归
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return compare(root.left,root.right);
}
public boolean compare(TreeNode left,TreeNode right){
if(left==null&&right!=null) return false;
if(left!=null&&right==null) return false;
if(left==null&&right==null) return true;
if(left.val!=right.val) return false;
boolean inner=compare(left.right,right.left);
boolean outside=compare(left.left,right.right);
return inner&&outside;
}
}
二叉树的最大深度
迭代
层序遍历BFS
class Solution {
public int maxDepth(TreeNode root) {
int res=0;
if(root==null) return res;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int len=queue.size();
while(len>0){
TreeNode node=queue.poll();
len--;
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
res++;
}
return res;
}
}
递归
转换思路
求根节点最大高度就是最大深度
class Solution {
public int maxDepth(TreeNode root) {
if(root==null) return 0;
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
二叉树的最小深度
迭代
利用层序遍历
class Solution {
public int minDepth(TreeNode root) {
int res=0;
if(root==null) return res;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int len=queue.size();
res++;
while(len>0){
TreeNode node=queue.poll();
len--;
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
if(node.left==null&&node.right==null)return res;
}
}
return res;
}
}
递归
求根节点最小高度就是最小深度
class Solution {
public int minDepth(TreeNode root) {
int res=Integer.MAX_VALUE;
if(root==null) return 0;
if(root.left==null&&root.right==null) return 1;
if(root.left!=null)
res=Math.min(res,minDepth(root.left));
if(root.right!=null)
res=Math.min(res,minDepth(root.right));
return res+1;
}
}
完美二叉树的节点
迭代
class Solution {
public int countNodes(TreeNode root) {
int res=0;
if(root==null) return res;
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int len=queue.size();
while(len>0){
TreeNode node=queue.poll();
res++;
len--;
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
}
return res;
}
}
递归
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
if(root.left==null&&root.right==null) return 1;
int res=0;
if(root.left!=null)
res+=countNodes(root.left);
if(root.right!=null)
res+=countNodes(root.right);
return res+1;
}
}
平衡二叉树
递归
平衡二叉树的概念:左子树右子树的高度差不超过1
思路:
思路是对二叉树做后序遍历,从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。
class Solution {
public boolean isBalanced(TreeNode root) {
return Balance(root)!=-1;
}
public int Balance(TreeNode root){
if(root==null) return 0;
if(root.left==null&&root.right==null) return 1;
int left=Balance(root.left);
if(left==-1) return -1;
int right=Balance(root.right);
if(right==-1) return -1;
if(Math.abs(right-left)>1)
return -1;
return Math.max(left,right)+1;
}
}
另一种思路更好想到,但是没有第一种秒!!
class Solution {
public boolean isBalanced(TreeNode root) {
return Balance(root)!=-1;
}
public int Balance(TreeNode root){
if(root==null) return 0;
if(root.left==null&&root.right==null) return 1;
int left=Balance(root.left);
if(left==-1) return -1;
int right=Balance(root.right);
if(right==-1) return -1;
if(Math.abs(right-left)>1)
return -1;
return Math.max(left,right)+1;
}
}
二叉树的所有路径
迭代
根据栈“先入后出”的特点,结合前序遍历的顺序,迭代的过程也就出来了:
每次都是先将根节点放入栈,然后右子树,最后左子树。
// 节点和路径出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
这一步有回溯的思想
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
// 初始化
List<String> res = new ArrayList<>();
if (root == null)
return res;
Stack<Object> stack = new Stack<>();
stack.push(root);
stack.push(root.val + "");
while (!stack.isEmpty()) {
// 节点和路径出栈
String path = (String) stack.pop();
TreeNode node = (TreeNode) stack.pop();
// 如果当前节点为叶子节点
if (node.left == null && node.right == null) {
res.add(path);
}
// 右子树入栈
if (node.right != null) {
stack.push(node.right);
stack.push(path + "->" + node.right.val);
}
// 左子树入栈
if (node.left != null) {
stack.push(node.left);
stack.push(path + "->" + node.left.val);
}
}
return res;
}
}
递归
和字符串相关题目,把握String和StringBuilder
class Solution {
List<String> res=new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
String s="";
deal(root,s);
return res;
}
public void deal(TreeNode root,String s){
if(root==null) return ;
if(root.left==null&&root.right==null)
res.add(new StringBuilder(s).append(root.val).toString());
String temp=new StringBuilder(s).append(root.val).append("->").toString();
deal(root.left,temp);
deal(root.right,temp);
}
}
左叶子之和
迭代
public int sumOfLeftLeaves(TreeNode root) {
if (root == null) {
return 0;
}
LinkedList<Pair<TreeNode, Boolean>> stack = new LinkedList<>();
stack.push(new Pair<>(root, false));
int sum = 0;
Boolean flag;
while (!stack.isEmpty()) {
Pair<TreeNode, Boolean> pair = stack.pop();
root = pair.getKey();
flag = pair.getValue();
if (flag && root.left == null && root.right == null) {
sum += root.val;
}
if (root.left != null) {
stack.push(new Pair<>(root.left, true));
}
if (root.right != null) {
stack.push(new Pair<>(root.right, false));
}
}
return sum;
}
利用BFS迭代
借助队列
// 层序遍历迭代法
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
int sum = 0;
if (root == null) return 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
int size = queue.size();
while (size -- > 0) {
TreeNode node = queue.poll();
if (node.left != null) { // 左节点不为空
queue.offer(node.left);
if (node.left.left == null && node.left.right == null){ // 左叶子节点
sum += node.left.val;
}
}
if (node.right != null) queue.offer(node.right);
}
}
return sum;
}
}
递归
class Solution {
public int sumOfLeftLeaves(TreeNode root) {
if(root==null)
return 0;
int left=sumOfLeftLeaves(root.left);
int right=sumOfLeftLeaves(root.right);
int leaf=0;
if(root.left!=null&&root.left.left==null&&root.left.right==null){
leaf=root.left.val;
}
return leaf+left+right;
}
}
找树左下角的值
迭代
使用
BFS
进行「层序遍历」,每次用当前层的首个节点来更新ans
,当BFS
结束后,ans
存储的是最后一层最靠左的节点。
class Solution {
public int findBottomLeftValue(TreeNode root) {
Deque<TreeNode> d = new ArrayDeque<>();
d.addLast(root);
int ans = 0;
while (!d.isEmpty()) {
int sz = d.size();
ans = d.peekFirst().val;
while (sz-- > 0) {
TreeNode poll = d.pollFirst();
if (poll.left != null) d.addLast(poll.left);
if (poll.right != null) d.addLast(poll.right);
}
}
return ans;
}
}
Deque <TreeNode> d = new ArrayDeque<>();
在Java中,`Deque` 接口是双端队列的接口,它继承自 `Queue` 接口。`Deque` 提供了在两端添加和移除元素的方法。
- `peekFirst()` 方法用于获取双端队列的第一个元素,但不移除它。
- `peekLast()` 方法用于获取双端队列的最后一个元素,也不移除它。
而 `peek()` 方法的行为取决于实现 `Deque` 接口的具体类。如果 `Deque` 实现是按照先进先出(FIFO)原则工作的,比如 `ArrayDeque`,那么 `peek()` 方法会返回第一个元素。如果 `Deque` 实现是按照后进先出(LIFO)原则工作的,比如 `LinkedList`,那么 `peek()` 方法会返回最后一个元素。
所以,`peek()` 方法取的是第一个元素还是最后一个元素,取决于 `Deque` 的具体实现。
递归
思路:
可以使用 DFS 进行树的遍历,每次优先 DFS 当前节点的左子树,每次第一次搜索到当前深度 depth 时,必然是当前深度的最左节点,此时用当前节点值来更新 ans。
class Solution {
int max,ans;
public int findBottomLeftValue(TreeNode root) {
dfs(root,1);
return ans;
}
public void dfs(TreeNode root,int depth){
if(root==null) return ;
if(depth>max){
max=depth;
ans=root.val;
}
dfs(root.left,depth+1);
dfs(root.right,depth+1);
}
}
路径总和
迭代
BFS
class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
// 如果根节点为空,直接返回false
if (root == null) return false;
// 创建一个队列,用于广度优先搜索(BFS)
Deque<Pair<TreeNode, Integer>> queue = new LinkedList<>();
// 将根节点和它的值作为一个对(Pair)添加到队列中
queue.offer(new Pair<>(root, root.val));
// 当队列不为空时,继续循环
while (!queue.isEmpty()) {
// 从队列中取出一个对
Pair<TreeNode, Integer> pair = queue.poll();
// 获取对中的节点和路径和
TreeNode node = pair.getKey();
int pathSum = pair.getValue();
// 如果当前节点是叶子节点(即左右子节点都为空),并且路径和等于目标值,返回true
if (node.left == null && node.right == null && pathSum == sum) {
return true;
}
// 如果左子节点不为空,将左子节点和当前路径和加上左子节点的值作为一个对添加到队列中
if (node.left != null) {
queue.offer(new Pair<>(node.left, pathSum + node.left.val));
}
// 如果右子节点不为空,将右子节点和当前路径和加上右子节点的值作为一个对添加到队列中
if (node.right != null) {
queue.offer(new Pair<>(node.right, pathSum + node.right.val));
}
}
// 如果遍历完所有节点都没有找到满足条件的路径,返回false
return false;
}
递归
DFS
public class Solution {
public boolean hasPathSum(TreeNode root, int sum) {
if(root == null){
return false;
}
if(root.left == null && root.right == null){
return root.val == sum;
}
return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
}
}
剩下的二叉树的题目明天再复盘,我感觉一次性那么多题目,我脑子都不想动了,就被动的看答案了