leetcode刷题思路-----二叉树

leetcode刷题思路-----二叉树

对于二叉树基本就两种遍历形式,bfs与dfs。

基本模板

1.dfs相对简单,但复杂的dfs都以其为基础:

//dfs
    private static void dfs(TreeNode root){
        if(root==null){
            return;
        }
        dfs(root.left);
        //中序遍历
        System.out.print(root.val+" ");
        dfs(root.right);
    }

对于遍历的顺序取决于你将其他代码写在何处,中间就是中序,其他依次类推。
2. bfs作为层次遍历也有其优势,尽管需要额外的空间来保证顺序,但本身的层次可以解决许多问题。

//bfs
private static List<List<Integer>> bfs(TreeNode root){	
		//队列存依次节点
        Deque<TreeNode> q  = new LinkedList<>();
        //可以加一个res输出层次顺序
        List<List<Integer>> res = new ArrayList<>();
        //根节点先入队
        q.offerLast(root);
        
        while (!q.isEmpty()){
            //记录每一层的节点
            List<Integer> list = new ArrayList<>();
            //这一步很重要,因为队列在变,要先存下大小
            int size = q.size();
            //对队列里的节点(本层的节点)记录
            for (int i = 0; i < size; i++) {
            	//取出当前节点
                TreeNode node = q.pollFirst();
                list.add(node.val);
                //将左右节点入队
                if(node.left!=null){
                    q.offerLast(node.left);
                }
                if(node.right!=null) {
                    q.offerLast(node.right);
                }
            }
            res.add(list);
        }
        return  res;
    }

1.构建二叉树

//构建一个二叉树节点类
class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode(){};
    TreeNode(int x){
         val = x;
    }
}

通过二叉树的层次遍历建立二叉树

//按照层次遍历建树
    public static TreeNode BuildTree(int[] n){
        Queue<TreeNode> q = new LinkedList<>();
        //根节点入队
        TreeNode root = new TreeNode(n[0]);
        q.offer(root);
        //实时记录创建到那个节点了
        int cur = 1;
        while(!q.isEmpty()){
            TreeNode node = q.poll();
            //看看是不是空节点,本次-1来标注
            if(cur<n.length&&n[cur]!=-1){
                System.out.println(n[cur]);
                TreeNode left = new TreeNode(n[cur]);
                q.offer(left);
                node.left = left;
            }
            cur++;
            //继续建立右子树
            if(cur<n.length&&n[cur]!=-1){
                System.out.println(n[cur]);
                TreeNode right = new TreeNode(n[cur]);
                q.offer(right);
                node.right = right;
            }
            cur++;
        }

        return root;
    }

2.力扣高频题

2.1 路径总和1与路径总和2

//dfs思路解决
class Solution {
    boolean res = false;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        dfs(root,targetSum);
        return res;
    }
    private void dfs(TreeNode root,int target){
        if(root==null){
            return;
        }
        //将target作为传递参数,到0作为满足的节点
        target -= root.val;
        //确定根节点时是不是到达0(总和为target)
        if(root.left==null&&root.right==null&&target==0){
            res = true;
        }
        //递归
        dfs(root.left,target);
        dfs(root.right,target);

    }
}

路径和2则要输出路径

class Solution {
     //存所有满足的路径
     List<List<Integer>> res = new ArrayList<>();
     //存当前路径
     List<Integer> list = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
        dfs(root,targetSum);
        return res;
    }
    private void dfs(TreeNode root,int target){
        if(root==null){
            return;
        }
        target -= root.val;
        //记入当前节点
        list.add(root.val);
        if(root.left==null&&root.right==null&&target==0){
            //记录满足路径
            res.add(new ArrayList(list));
            //记得回溯
            list.remove(list.size()-1);
            return;            
        }
        dfs(root.left,target);
        dfs(root.right,target);
        //回溯节点状态
        list.remove(list.size()-1);       
    }
}

2.2 最大路径和

对于这种不在根节点状态结束的问题,一般思路在于后根遍历,将状态上传,将遍历的节点作为当前的根节点,判断左右子树的状态来满足题意。

class Solution {
	//确保出现负值时的正确性
    int res = Integer.MIN_VALUE;
    public int maxPathSum(TreeNode root) {
        dfs(root);
        return res;
    }
    //返回值为int,为满足状态自底向上传递
    private int dfs(TreeNode root){
        if(root==null){
            return 0;
        }
        //通过记录左右子树的状态来选择
        //我们只选择能对临时这个子树的正贡献值作为这个子树分支的值
        int left = Math.max(dfs(root.left),0);
        int right =  Math.max(dfs(root.right),0);
        //实时记录这个子树的最大贡献值
        res = Math.max(res,left+right+root.val);
        //向上传递时选择贡献最大的子树
        return root.val+Math.max(left,right);
        
    }
}

总结: 对于路径和问题基本dfs都可以暴力解决,遍历下去。对于直至根节点的题,比较简单,判断到了根节点在考虑是否满足题意,比如是否到target的和。而对于随意的路径和,则要动态考虑每一个子树,对于这种问题一般选用带返回值的dfs,记录当前节点的状态,用后根遍历的方式传递参数直达root,动态取结果。

2.3 最近公共祖先

本题基本解答方法是使用hashmap来记录父子节点,但本文给出一个更符合思路的后根遍历模板

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return dfs(root,p,q);
    }
    private TreeNode dfs(TreeNode root,TreeNode p,TreeNode q){
    	//为空就上传空,有p,q也向上传递
        if(root==null||root==q||root==p){
            return root;
        }
        //记录当前左右子树传递值
        TreeNode left = dfs(root.left,p,q);
        TreeNode right = dfs(root.right,p,q);
        //左子树为空,直接返回右子树(右子树也空,传的就是空,为p,q就向上传递)
        //左子树不为空,说明有p,q,看看右子树也有的话。返回root则为其公共祖先,右子树没有就返回含有值的左子树
        return left == null ? right : right == null ? left : root; 
    }
}

总结: 二叉树的题最多在于dfs的路径上,用dfs的方法来一点点整理状态就可解决。本身是一种暴力遍历,基本都能遍历到,剪枝就是和回溯联系在一起,要考虑好临时的遍历的回退。而层次遍历的问题基本围绕基本模板开展,本身遍历顺序的局限性,解决类似路径问题稍微复杂一点。

你可能感兴趣的:(leetcode总结,java)