力扣—树题目

1.二叉树"层序遍历"的知识点

1.层序遍历——>>树的深度

listsRes.size() 就是本二叉树的深度,因为它代表层数

2.层序遍历——>>树的最大宽度

暂存队列list.size() 的最大值就是二叉树最大的宽度

  • listsRes.add(list);前加入

max = list.size>max ? list.size() : max ; //在每层结束后都判断max是否需要更新

3.本程序是层序遍历,且"自顶向下"按层输出

102. 二叉树的层序遍历
在这里插入图片描述

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> listsRes = new LinkedList<>();
        1.处理特殊情况
        if (root==null) return listsRes;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root); 队列先入先出
        while (queue.size()!=0){
            2.辅助队列,暂时存储本层的数据,待本层遍历结束后,把数据存进listRes,开启下次循环前重新更新
            List<Integer> list = new LinkedList<>();  //辅助队列
                2.1巧妙的分层方法,循环遍历queue.size()次数
            for (int i = queue.size(); i >0 ; i--) {
                TreeNode node = queue.remove();//队列先入先出
                list.add(node.val);
                2.2判断并添加新的下级子节点
                if (node.left!=null) queue.add(node.left);
                if (node.right!=null) queue.add(node.right);
            }
            3.把每层的数值添加进去
            listsRes.add(list);
        }
        return listsRes;
    }
}

4.层序遍历——自底向上,按层输出

107. 二叉树的层序遍历 II

  • 跟上一个程序的区别在于:
    采用双向队列deque,每层遍历结束之后,把该层元素添加至 队首即可。
    在这里插入图片描述
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
		0.双向队列,可以把元素添加到 队首
        Deque<List<Integer>> listsRes = new LinkedList<>();
        1.处理特殊情况
        if (root==null) return new LinkedList<>() ;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root); 队列先入先出
        while (queue.size()!=0){
            2.辅助队列,暂时存储本层的数据,待本层遍历结束后,把数据存进listRes,开启下次循环前重新更新
            List<Integer> list = new LinkedList<>();  //辅助队列
            	2.1巧妙的分层方法,循环遍历queue.size()次数
            for (int i = queue.size(); i >0 ; i--) {
                TreeNode node = queue.remove();//队列先入先出
                list.add(node.val);
                2.2判断并添加新的下级子节点
                if (node.left!=null) queue.add(node.left);
                if (node.right!=null) queue.add(node.right);
            }
            3.把每层的数值添加进去,而且要添加到队首!!
            listsRes.addFirst(list);  
        }
        return (List)listsRes;
    }
}

5.层序遍历Z字形输出——自顶向下按层输出,偶数层反转

103. 二叉树的锯齿形层序遍历

  • 跟3的区别在于:
    1.加入反转标志:boolean flagReverse
    2.采用反转方法:Collections.reverse(list); 对list数组进行反转!
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> listsRes = new LinkedList<>();
        1.处理特殊情况
        if (root==null) return listsRes;
         1.5 加入反转标志
        boolean flagReverse = false;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root); 队列先入先出
        while (queue.size()!=0){
            2.辅助队列,暂时存储本层的数据,待本层遍历结束后,把数据存进listRes,开启下次循环前重新更新
            List<Integer> list = new LinkedList<>();  //辅助队列
            	2.1巧妙的分层方法,循环遍历queue.size()次数
            for (int i = queue.size(); i >0 ; i--) {
                TreeNode node = queue.remove();//队列先入先出
                list.add(node.val);
                2.2判断并添加新的下级子节点
                if (node.left!=null) queue.add(node.left);
                if (node.right!=null) queue.add(node.right);
            }
            3.把每层的数值添加进去
                3.1反转标志,每隔一层反转一次
            if (flagReverse) Collections.reverse(list);
            flagReverse = !flagReverse;
            listsRes.add(list);
        }
        return listsRes;
    }
}

2. 二叉树前、中、后序遍历(如何写出他们仨的迭代方法)

自己整理:前中后–递归+迭代

0.二叉树遍历框架

void traverse(TreeNode root) {
    1.前序遍历
    traverse(root.left)
    2.中序遍历
    traverse(root.right)
    3.后序遍历
}

1.中序遍历:(左—>中—>右 或者 右—>中—>左)

1.1,对于二叉搜索树
  • 1.左中右 遍历的list是升序排列;
  • 2.右中左 遍历的list是降序排列。
1.1.1.下边的例题可以采用中序遍历——右-中-左——顺序

在这里插入图片描述

1.基于中序遍历的变体专门针对二叉搜索树降序、升序排列的
class Solution {
    public int kthLargest(TreeNode root, int k) {
        List<Integer> list = new ArrayList<>();
        inorder(root,list,k);
        return list.get(k-1);
    }

    二叉树的中序遍历——(右 中 左)
    在二叉搜索树中这样得到的list是按降序排列的
    public void inorder(TreeNode node,List list, int k){
        if (node==null || list.size()>k) return;

        inorder(node.right,list, k);
        list.add(node.val);
        inorder(node.left,list, k);
    }
}

3.二叉树的深度

104. 二叉树的最大深度

1.可以基于层序遍历(不再描述)

2.可以基于递归算法

  • 注意
    • 1.int maxDepth = 0; 必须定义到类体,
      因为如果作为参数传入回溯方法,更新后的maxDepth并没有被再传出去,所以一直会是0!!
class Solution {
    int maxDepth = 0;
    public int maxDepth(TreeNode root) {
        1.处理特殊情况
        if (root==null) return 0;
        int tempDepth=0;
        backtrack(root,tempDepth);
        return maxDepth;
    }

    public void backtrack(TreeNode node, int tempDepth){
        1.结束条件
        if (node==null) return;
        2.做出选择 ++
        tempDepth++;
        backtrack(node.left,tempDepth);
        backtrack(node.right,tempDepth);
        2.5更新数据
        if (tempDepth>maxDepth) maxDepth=tempDepth;
        3.撤销选择 --
        tempDepth--;
    }
}

4.对称二叉树

1.什么是对称二叉树?(他的所有子树也都应该是对称的!)

  • 1.除根节点外,其他层的结点数为偶数;
  • 2.每层节点从外向里,数值相同,做右节点不同(即不能同是上一层的左节点,或同是右节点)
  • 3.满足条件1、2的,在数值相同时,还要满足各自的父节点在上一层的下标之和 = 上一层宽度-1

2.解题思路

  • 1.可以选择层序遍历,然后按照对称二叉树的条件1、2、3,来进行;
  • 2.采用递归思路,扩散递归。

对称的二叉树—层序遍历+递归-2种解法

  • 注意:观察下列递归函数,可知传入的是2个参数,return的是4个参数 所以这个递归是指数级扩张的。
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root==null) return true;
        return recursion(root.left,root.right);
    }
    public boolean recursion(TreeNode left,TreeNode right){
        终止条件:1.两个节点是 空
        if (left==null && right==null)
            return true;
        终止条件:2. 其中一个节点为空,另一个不为空
                 或  两个节点的数值不同
        if (left==null || right==null ||left.val!= right.val)
            return false;
        *******************************************************    
        递归:3。出于对称位置的节点 进入同一递归函数
             而且 应该用 && 符号连接
        return recursion(left.left,right.right) && recursion(left.right,right.left);
    }
}

5.二叉树的镜像

1.什么是二叉树的镜像?

2.考虑递归解法

  • 注意 :此递归解法,传入1个参数,又有2个参数参与递归,所以是指数级扩张递归!
class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        0.终止条件
        if (root==null) return null;
        
        1.左右节点反转
        TreeNode temp = root.left;
        root.left = root.right;
        root.right = temp;
        
        2.进入递归,而且是扩散式递归
        mirrorTree(root.left);
        mirrorTree(root.right);
        
        3.结束
        return root;
    }
}

6.剑指 Offer 55 - II. 平衡二叉树

1.什么是平衡二叉树?

  • 1.任意节点的树的深度差值 都要 <= 1.
  • 2.其实还要保证该树是二叉排序树(即:中序遍历(左中右)的list数组是严格递增即可!)(但本题不要求)
    力扣-答案

2.解题思路

  • 1.从根节点开始递归所有子节点,并检查所有子节点的左右子树的高度差值
    如下图所示,根节点符合要求,但节点4不符合要求,所以不是平衡二叉树
    在这里插入图片描述
  • 注意:本递归程序也是传入1个参数,return回2个参数,所以是扩散式递归
class Solution {
    public boolean isBalanced(TreeNode root) {
        0.终止条件
        if (root==null) return true;
        1.检查当前节点的左右子树的 深度差值
        if (Math.abs(treeDepth(root.left)-treeDepth(root.right))>1)
            return false;
        2.检查当前节点的左右节点,是否也是平衡二叉树
          因为只要任意子树不是平衡二叉树,那整棵树都不是平衡二叉树
        return isBalanced(root.left) && isBalanced(root.right);
    }

    求树的深度
    public int treeDepth(TreeNode node){
        if (node==null) return 0;
        return Math.max(treeDepth(node.left),treeDepth(node.right))+1;
    }
}

7.面试题34. 二叉树中和为某一值的路径(回溯算法思想)

1.回溯算法的套路:

  • 1.路径:就是已经做出的选择;
  • 2.选择列表:就是当前可以做的选择;
  • 3.结束条件:也就是达到决策层,无法再做选择的条件。
2.回溯算法代码框架:
result = []
void backTrack(路径,选择列表){
	if 满足结束条件:
		result.add(路径)
		return
	for 选择 in 选择列表:
		做选择;
		backTrack(路径,选择列表);
		撤销选择
}

3.此题思路:

  • 1.建立一个返回lists列表,建立一个临时路径列表tempList,建立变量tempSum存储此路径的和;
  • 2.当 1.到达叶子节点、2.且sum=target时,那当前路径tempList存入lists列表。

注意:在把tempList存入lists时,要新new一个list,不然即使存入lists了,但其中的tempList的内容还是会改变。

class Solution {
    List<List<Integer>> lists = new ArrayList<>();
    List<Integer> tempList = new ArrayList<>();
    int tempSum = 0;
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        if (root==null) return new ArrayList<>();
        trackBack(root,target);
        return lists;
    }

    public void trackBack(TreeNode root,int target){
        0.终止条件
        if (root==null) return;

        1.做出选择
        tempSum += root.val;
        tempList.add(root.val);

        2.调用递归
        trackBack(root.left, target);
        trackBack(root.right,target);

        2.5做判断,这里不仅确保 tempSum=target 还要确保是 叶子节点
        if (tempSum==target && root.right==null && root.left==null){
            //重新new一个list,是因为如果直接存tempList,存的只是他的地址,而他的内容并不固定
            lists.add(new ArrayList<>(tempList));
        }
        3.撤销选择
        tempSum -=root.val;
        tempList.remove(tempList.size()-1);
    }
}

8.剑指 Offer 68 - II. 二叉树的最近公共祖先

在这里插入图片描述

0.递归解题思路

(注意这个题目的设定是p、q一定是存在于树中的)

  • 1.在递归解法中,被寻找到的p、q会被一层层的 return 上去
  • 2.在 p 、q 两个节点被一层层向上return的过程中,当两者首次会合
    即:if (left != null && right!=null) 满足此条件时,那么向上只return这个让两者首次回合的节点root
  • 3.如果p 、 q最后在根节点回合的,那就 根节点就是公共节点。
  • 4.假设p在q的子树中,那么就没有回合发生了,直接就向上return节点q就可以,因为他就是最近公共节点。
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
         0.终止条件
        if (root==null) return null;
        0.1 发现p(或者q) 就直接向上return了,
        如果q 在p下的子树中,也不必担心,因为p就是最近公共节点了
        if (root==p || root==q) return root;

        0.5 递归 左右子树  是否有公共节祖先
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right= lowestCommonAncestor(root.right, p, q);

        1.如果左右同时为空,说明左右子树均不包含节点 p q
        if (left == null && right==null) return null;
        2.说明p q 都不在左子树中
        if (left == null) return right;
        3.说明p q 都不在右子树中
        if (right== null) return left;

        4.说明p q分别在左右子树 中,所以当前节点就是 最近公共节点
//        if (left != null && right!=null)    //这句判断 存不存在都一样
        return root;
    }
}

1.解题思路:

  • 1.先找到节点p、q两个节点,最浅的深度lowDepth(假设这是p的深度);
  • 2.从此深度上的p开始,以p为根节点进行遍历,如果找到q,则p符合要求;
  • 3.如果没有从上一深度从头遍历,看是否能同时含有p或者q。
class Solution1 {
    Deque<Deque<TreeNode>> level = new LinkedList<>();
    int flag = 0;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root==null) return null;
        TreeNode res = new TreeNode();
        levelTravel(root,p,q);
        for (Deque<TreeNode> list:level) {
            for (int i = list.size(); i > 0 ; i--) {
                flag=0;更新标志位
                res = list.removeLast();
                backTrack(res, p,q);
                if (flag==2)
                    return res;
            }
        }
        return null;
    }
    层序遍历,找到p q 两者最浅层 及其之前的元素
    public void levelTravel(TreeNode root,TreeNode p,TreeNode q){
        if (root==null) return;
        Deque<TreeNode> list = new LinkedList<>();
        list.add(root);
        Deque<TreeNode> temp = new LinkedList<>();
        while (!list.isEmpty()){
            for (int i = list.size(); i > 0 ; i--) {
                TreeNode node = list.remove();
                temp.add(node);
                if (node == p || node==q){
                    level.addFirst(temp); 这里应该 下边的层 添加在 前边,这样遍历时,就由深层 到其按层遍历
                    return;
                }
                if (node.left!=null) {
                    list.add(node.left);
                }
                if (node.right!=null){
                    list.add(node.right);
                }
            }
            level.addFirst(temp); 这里应该 下边的层 添加在 前边,这样遍历时,就由深层 到其按层遍历
        }
    }

    遍历节点是否同时含有p q
    public void backTrack(TreeNode node, TreeNode p,TreeNode q){
        终止条件
        if (node==null) return;
        检查是否有 p q
        if (node==p || node== q) flag++;
        backTrack(node.left, p, q);
        backTrack(node.right, p, q);
    }
}

2.剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

注意:这是二叉搜索树,更简单一点。

1.解题思路:
  • 1.二叉搜索树是严格按大小排列的,左< 根 < 右
  • 2.如果节点x 是 节点p、q (假设 p<q)的最近公共节点,那么一定有 p < x < q
  • 3.如果节点x 是公共节点 但不是最近的,那就会有 x < p < q 或者 x > q > p
    在这里插入图片描述
代码就像在比大小
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root==null) return null;
        TreeNode ancestorLowest = root;
        while (true) {
            if (ancestorLowest.val < p.val && ancestorLowest.val < q.val)
                ancestorLowest = ancestorLowest.right;
            else if (ancestorLowest.val > p.val && ancestorLowest.val > q.val)
                ancestorLowest = ancestorLowest.left;
            else
                break;
        }
        return ancestorLowest;
    }
}

9.剑指 Offer 26. 树的子结构(这个题不会!)(双方法递归)

剑指 Offer 26. 树的子结构

1.思路:

  • 1.先构造1个辅助递归方法recur(),来比较输入的A、B根节点 ,它们的各个节点值是否相同,(当某节点B为null,A不是null时,也算是相同);
  • 2.在主方法内也进行递归,主要是判断:
    1. A 、B是否为null,如果是则为false;(因为规定,空树不是任何树的子结构)
    1. 把A、B 送入辅助递归方法recur(),具体判断节点是否相同;
    1. 如果A、B 为根节点的结构无法匹配,那么A.left 、A.right 这两个分别为根节点再和 根节点B去匹配。
      在这里插入图片描述

注意:这是两个不同方法 分别进行扩散递归

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
    	1.判断 AB是否为 null2.选定A树上的某个节点node,与B进行具体的节点值比较递归;
    	3.当前节点node失败后,再把node的左右子节点分别送入主方法
        return (A!=null && B!=null) && (recuur(A,B) || isSubStructure(A.left,B) || isSubStructure(A.right,B));
    }

    public boolean recuur(TreeNode A, TreeNode B){
        1.说明B的节点已经匹配完
        if (B==null) return true;
        2.A先为空 或者 节点值不一样  返回false
        if (A==null || A.val!=B.val) return false;
        3.比较A B 的左右子树是否相等
        return recuur(A.left,B.left) && recuur(A.right,B.right);
    }
}

10.剑指 Offer 07. 重建二叉树

剑指 Offer 07. 重建二叉树

1.思路:

  • 1.二叉树的前序遍历(根左右)数组的第1个数,就是根节点root;
  • 2.根据前序遍历得出的root,再到中序遍历(左根右)数组中,以root为界限,分出左右子树的数组,即左子数组、右子数组。
  • 3.然后依次递归即可。
    注意:核心其实时如何把前序遍历 中序遍历数组给分成左右子树的,
    如果要划分,其中最重要的又是找到 根节点中序遍历数组的下标rootIndex,而且你会发现rootIndex还可以用于划分前序数组为左右子树的下标。

在这里插入图片描述

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
    	0.处理特殊情况
        if (preorder.length==0 || inorder.length==0) return null;
		0.5.创建根节点root
        TreeNode root = new TreeNode(preorder[0]);

        找出root节点在中序遍历数组的下标
        int rootIndex=0;
        在中序遍历表中寻找 根节点root 所在位置下标,并记录
        for (int i = 0;i<inorder.length;i++){
            if (inorder[i]==preorder[0]){
                rootIndex=i;
                break;
            }
        }
        2.左右子树的各自 中序遍历 子数组
        int[] left_inorder  = Arrays.copyOfRange(inorder,0,rootIndex);
        int[] right_inorder = Arrays.copyOfRange(inorder,rootIndex+1,inorder.length);

        3.找到左右子树的各自 前序遍历 子数组
        int[] left_pre  = Arrays.copyOfRange(preorder,1,1+rootIndex-0);
        int[] right_pre = Arrays.copyOfRange(preorder,rootIndex+1,preorder.length);
		
		4.根节点root的左右子树,开启递归
        root.left = buildTree(left_pre,left_inorder);
        root.right= buildTree(right_pre,right_inorder);

        return root;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值