算法必刷系列之二叉树

层次遍历模板

理解使用队列进行二叉树层次遍历的过程

public List<Integer> levelOrder(TreeNode root){
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while (!queue.isEmpty()){
        TreeNode node = queue.poll();
        res.add(node.val);
        if(node.left!=null){
            queue.offer(node.left);
        }
        if(node.right!=null){
            queue.offer(node.right);
        }
    }
    return res;
}

层次遍历分层存储模版

层次遍历分层存储是解决下面问题的基础

public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while(!queue.isEmpty()){
        List<Integer> list = new ArrayList<>();
        int size = queue.size();
        for(int i=0;i<size;i++){
            TreeNode node = queue.poll();
            list.add(node.val);
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
        res.add(list);
    }
    return res;
}

层次遍历自底向上

按照层次遍历模版,将每层数据遍历结束后,将当前层存储到列表头部

public List<List<Integer>> levelOrderBottom(TreeNode root) {
    List<List<Integer>> res = new LinkedList<List<Integer>>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while(queue.size()>0){
        int size = queue.size();
        List<Integer> treeList = new ArrayList<Integer>();
        for(int i=0;i<size;i++){
            TreeNode treeNode = queue.poll();
            treeList.add(treeNode.val);
            if(treeNode.left!=null){
                queue.add(treeNode.left);
            }
            if(treeNode.right!=null){
                queue.add(treeNode.right);
            }
        }
        res.add(0,treeList);
    }
    return res;
}

锯齿形层次遍历

设置一个遍历标识,用于判断是否是从左到右遍历,使用双端队列,根据遍历要求顺序,确定入队顺序

public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    boolean fromLeftToRight = true;
    while (!queue.isEmpty()) {
        int size = queue.size();
        Deque<Integer> treeList = new LinkedList<Integer>();
        for(int i = 0; i < size; i++){
            TreeNode treeNode = queue.poll();
            if(fromLeftToRight){
                treeList.offerLast(treeNode.val);
            }else{
                treeList.offerFirst(treeNode.val);
            }
            if(treeNode.left!=null){
                queue.offer(treeNode.left);
            }
            if(treeNode.right!=null){
                queue.offer(treeNode.right);
            }
        }
        fromLeftToRight = !fromLeftToRight;
        res.add(new LinkedList<>(treeList));
    }
    return res;
}

N叉树的层次遍历

与二叉树的层次遍历区别不大,区别在于二叉树的层次遍历是节点出队后,入队左右孩子,N叉树的层次遍历是节点出队后,入队所有子节点

public List<List<Integer>> levelOrder(Node root) {
    List<List<Integer>> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<Node> queue = new LinkedList<Node>();
    queue.offer(root);
    while(!queue.isEmpty()){
        int size = queue.size();
        List<Integer> nodeList = new ArrayList<>();
        for(int i=0;i<size;i++){
            Node node = queue.poll();
            nodeList.add(node.val);
            for(Node child:node.children){
                queue.offer(child);
            }
        }
        res.add(nodeList);
    }
    return res;
}

求二叉树每一层的最大值

遍历时,每一层设置一个保存最大值的变量,便利过程中不断更新该变量,最终得到每一层的最大值

public List<Integer> largestValues(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while(!queue.isEmpty()){
        int max = Integer.MIN_VALUE;
        int size = queue.size();
        for(int i=0;i<size;i++){
            TreeNode node = queue.poll();
            int val = node.val;
            max = max>val?max:val;
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
        res.add(max);
    }
    return res;
}

求二叉树每一层的平均值

与求每一层的最大值解法相同

public List<Double> averageOfLevels(TreeNode root) {
    List<Double> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while(!queue.isEmpty()){
        double sum = 0;;
        int size = queue.size();
        for(int i=0;i<size;i++){
            TreeNode node = queue.poll();
            sum += node.val;
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
        res.add(sum/size);
    }
    return res;
}

二叉树的右视图

便利每一层时,保存当前遍历的节点数等于队列size-1的节点,最终可以得到二叉树的右视图

public List<Integer> rightSideView(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    if(root==null){
        return res;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    while(!queue.isEmpty()){
        double sum = 0;;
        int size = queue.size();
        for(int i=0;i<size;i++){
            TreeNode node = queue.poll();
            if(i==size-1){
                res.add(node.val);
            }
            if(node.left!=null){
                queue.offer(node.left);
            }
            if(node.right!=null){
                queue.offer(node.right);
            }
        }
    }
    return res;
}

二叉树左下角的值

遍历时,一个节点出队时,先入队右孩子,在入队左孩子,这样最后一个访问到的节点就是二叉树左下角的值

public int findBottomLeftValue(TreeNode root) {
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    int res = 0;
    while(!queue.isEmpty()){
        TreeNode node = queue.poll();
        res = node.val;
        if(node.right!=null){
            queue.offer(node.right);
        }
        if(node.left!=null){
            queue.offer(node.left);
        }
    }
    return res;
}

二叉树前序遍历递归写法

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    preorderTraversal(root,res);
    return res;
}

public void preorderTraversal(TreeNode root,List<Integer> res){
    if(root==null){
        return;
    }
    res.add(root.val);
    preorderTraversal(root.left,res);
    preorderTraversal(root.right,res);
}

二叉树中序遍历递归写法

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    inorderTraversal(root,res);
    return res;
}

public void inorderTraversal(TreeNode root,List<Integer> res){
    if(root==null){
        return;
    }
    inorderTraversal(root.left,res);
    res.add(root.val);
    inorderTraversal(root.right,res);
}

二叉树后序遍历递归写法

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<Integer>();
    postOrderTraversal(root,res);
    return res;
}

public void postOrderTraversal(TreeNode root,List<Integer> res){
    if(root==null){
        return;
    }
    postOrderTraversal(root.left,res);
    postOrderTraversal(root.right,res);
    res.add(root.val);
}

二叉树前序遍历的迭代写法

使用栈按照根左右的方式进行二叉树的前序遍历

public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<TreeNode>();
    while(root!=null || !stack.isEmpty()){
        while(root!=null){
            res.add(root.val);
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        root = root.right;
    }
    return res;
}

二叉树中序遍历的迭代写法

使用栈按照左根右的方式进行二叉树的中序遍历

public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<>();
    Deque<TreeNode> stack = new LinkedList<TreeNode>();
    while(root!=null || !stack.isEmpty()){
        while(root!=null){
            stack.push(root);
            root = root.left;
        }
        root = stack.pop();
        res.add(root.val);
        root = root.right;
    }
    return res;
}

二叉树后序遍历的迭代写法

使用栈按照根右左的方式进行二叉树的遍历,再将结果反转,即是二叉树的后续遍历

public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res = new ArrayList<Integer>();
    Deque<TreeNode> stack = new LinkedList<TreeNode>();
    while(root!=null || !stack.isEmpty()){
        while(root!=null){
            res.add(root.val);
            stack.push(root);
            root = root.right;
        }
        root = stack.pop();
        root = root.left;
    }
    Collections.reverse(res);
    return res;
}

判断两棵二叉树是否相同

比较两棵二叉树是否同时为空,同时为空相同,不同时为空不相同,同时不为空,在比较根节点和左右子树是否相同

public boolean isSameTree(TreeNode p, TreeNode q) {
    if(p==null&&q==null){
        return true;
    }
    if(p==null||q==null){
        return false;
    }
    if(p.val!=q.val){
        return false;
    }
    return isSameTree(p.left,q.left)&&isSameTree(p.right,q.right);
}

判断二叉树是否对称

判断二叉树是否为空,为空则对称,不为空比较左右子树是否对称

public boolean isSymmetric(TreeNode root) {
    if(root==null){
        return true;
    }
    return isSymmetric(root.left,root.right);
}

public boolean isSymmetric(TreeNode p,TreeNode q){
    if(p==null&&q==null){
        return true;
    }
    if(p==null||q==null){
        return false;
    }
    if(p.val!=q.val){
        return false;
    }
    return isSymmetric(p.left,q.right)&&isSymmetric(p.right,q.left);
}

合并二叉树

判断是否至少有一棵二叉树为空,为空直接返回,两棵二叉树同时不为空,先合并根节点,然后再合并左右子树

public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
    if(root1==null || root2==null){
        return root1==null?root2:root1;
    }
    TreeNode merged = new TreeNode(root1.val+root2.val);
    merged.left =  mergeTrees(root1.left,root2.left);
    merged.right =  mergeTrees(root1.right,root2.right);
    return merged;
}

二叉树的所有路径

定义一个变量记录当前路径,定义一个列表存储所有路径,当当前节点的左右子树均为空,表明该路径到达终点,将该路径添加到路径列表中,否则当前路径加入该节点,继续递归查找当前节点的左右子树

public List<String> binaryTreePaths(TreeNode root) {
    List<String> res = new ArrayList<>();
    searchPath(root,"",res);
    return res;
}

public void searchPath(TreeNode root, String path, List<String> res){
    if(root==null){
        return;
    }
    if(root.left==null&&root.right==null){
        path = path+root.val;
        res.add(path);
        return;
    }
    searchPath(root.left,path+root.val+"->",res);
    searchPath(root.right,path+root.val+"->",res);
}

路径总和

每遍历一个节点,目标总和减去该节点值,当当前遍历节点的左右子树均为空时,判断当前节点值是否等于目标总和

public boolean hasPathSum(TreeNode root, int targetSum) {
    if(root==null){
        return false;
    }
    targetSum-=root.val;
    if(root.left==null&&root.right==null&&targetSum==0){
        return true;
    }
    return hasPathSum(root.left,targetSum) || hasPathSum(root.right,targetSum);
}

反转二叉树

如果二叉树为空,直接返回。否则交换左右子节点,递归交换左右子树

public TreeNode invertTree(TreeNode root) {
    if(root==null){
        return root;
    }
    TreeNode temp = root.left;
    root.left = root.right;
    root.right = temp;
    invertTree(root.left);
    invertTree(root.right);
    return root;
}

二叉树的最大深度

二叉树的最大深度等于左右子树的最大深度加一

public int maxDepth(TreeNode root) {
    if(root==null){
        return 0;
    }
    int left = maxDepth(root.left);
    int right = maxDepth(root.right);
    return Math.max(left,right)+1;
}
public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }
        Queue<TreeNode> queue = new LinkedList<TreeNode>();
        queue.offer(root);
        int maxDepth = 0;
        while(!queue.isEmpty()){
            maxDepth++;
            int size = queue.size();
            for(int i=0;i<size;i++){
                root = queue.poll();
                if(root.left!=null){
                    queue.offer(root.left);
                }
                if(root.right!=null){
                    queue.offer(root.right);
                }
            }
        }
        return maxDepth;
    }

二叉树的高度

二叉树的最大高度等于左右子树的最大高度加一

public int height(TreeNode root){
    if(root==null){
        return 0;
    }
    int leftHeight = height(root.left);
    int rightHeight = height(root.right);
    return Math.max(leftHeight,rightHeight)+1;
}

判断平衡树

计算左右子树的高度,判断高度差是否小于1

public boolean isBalanced(TreeNode root) {
    if(root==null){
        return true;
    }
    boolean rootBalanced = Math.abs(height(root.left)-height(root.right))<=1;
    boolean leftBalanced = isBalanced(root.left);
    boolean rightBalanced = isBalanced(root.right);
    return rootBalanced&&leftBalanced&&rightBalanced;
}

public int height(TreeNode root){
    if(root==null){
        return 0;
    }
    int leftHeight = height(root.left);
    int rightHeight = height(root.right);
    return Math.max(leftHeight,rightHeight)+1;
}
public boolean isBalanced(TreeNode root) {
    return height(root)>=0;
}

public int height(TreeNode root){
    if(root==null){
        return 0;
    }
    int leftHeight = height(root.left);
    int rightHeight = height(root.right);
    if(leftHeight==-1 || rightHeight== -1 || Math.abs(leftHeight-rightHeight)>1){
        return -1;
    }else{
        return Math.max(leftHeight,rightHeight)+1;
    }
}

最小深度

找到不为空的左子树和右子树的最小深度

public int minDepth(TreeNode root) {
    if(root==null){
        return 0;
    }
    if(root.left==null&&root.right==null){
        return 1;
    }
    int leftDepth = minDepth(root.left);
    int rightDepth = minDepth(root.right);
    if(root.left==null || root.right==null){
        return leftDepth+rightDepth+1;
    }
    return Math.min(leftDepth,rightDepth)+1;
}
public int minDepth(TreeNode root) {
    if(root==null){
        return 0;
    }
    Queue<TreeNode> queue = new LinkedList<TreeNode>();
    queue.offer(root);
    int minDepth = 0;
    while(!queue.isEmpty()){
        minDepth++;
        int size = queue.size();
        for(int i=0;i<size;i++){
            root = queue.poll();
            if(root.left==null&&root.right==null){
                return minDepth;
            }
            if(root.left!=null){
                queue.offer(root.left);
            }
            if(root.right!=null){
                queue.offer(root.right);
            }
        }
    }
    return minDepth;
}

N叉树的最大深度

与二叉树的最大深度原理相同

public int maxDepth(Node root) {
    if(root==null){
        return 0;
    }
    int maxDepth = 0;
    for(Node node:root.children){
        int depth = maxDepth(node);
        if(depth>maxDepth){
            maxDepth = depth;
        }
    }
    return maxDepth+1;
}

最近公共祖先

在左右子树中寻找两个节点,如果两个节点在不同侧,则root就是最近公共祖先,在同侧,判断当前侧的根节点是否等于两个节点其中之一,相等则返回,否则在该侧的左右子树中继续寻找

private TreeNode res = null;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    dfs(root,p,q);
    return res;
}

public boolean dfs(TreeNode root, TreeNode p,TreeNode q){
    if(root==null){
        return false;
    }
    boolean left = dfs(root.left,p,q);
    boolean right = dfs(root.right,p,q);
    if(left&&right){
        res = root;
    }
    if(root == p || root ==q){
        res = root;
    }
    return left || right || root==p || root==q;
}
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if(root==p || root==q || root==null){
        return root;
    }
    TreeNode left = lowestCommonAncestor(root.left,p,q);
    TreeNode right = lowestCommonAncestor(root.right,p,q);
    if(left==null&&right==null){
        return null;
    }
    if(left==null){
        return right;
    }
    if(right==null){
        return left;
    }
    return root;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

今天不coding

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值