二叉树

前后中遍历指的是中间节点遍历的顺序,使用栈来实现

递归算法考虑三要素

  • 确定递归函数的参数和返回值
  • 确定终止条件
  • 确定单层递归的逻辑

前序遍历(递归)

递归是调用自身函数,代码量少但可能会出现栈溢出;

public class FirstTest {
    static List<Integer> result=new ArrayList();
    public static List<Integer> preorderTraversal(TreeNode root) {
        if(root == null){
            return new ArrayList<>();
        }
        result.add(root.val);
        preorderTraversal(root.left);
        preorderTraversal(root.right);
        return result;
    }
}

迭代(借助栈)

迭代是重复执行某一段代码,代码量多但没有额外的内存消耗。

public static List<Integer> preorderTraversal(TreeNode root){
        List<Integer> result=new ArrayList<>();
        if (root == null){
            return result;
        }
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            result.add(node.val);
            if (node.right!=null){
                stack.push(node.right);
            }
            if (node.left!=null){
                stack.push(node.left);
            }
        }
        return result;

 

中序遍历(迭代)⭐

与前序的迭代不同在于: 中序一直要访问到树的最左边,当左边的节点是空时再出栈并将当前出栈节点的右节点入栈。   

cur代表当前进行判断的节点,而不是cur.left

public static List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> result=new ArrayList<>();
        if (root == null){
            return result;
        }
        TreeNode cur=root;
        Stack<TreeNode> stack = new Stack<>();
        while (!stack.isEmpty()  || cur != null){
            if (cur!=null){
                stack.push(cur);
                cur=cur.left;
            }else {
                TreeNode pop = stack.pop();
                result.add(pop.val);
                cur=pop.right;
            }
        }
        return result;
    }

 

后续遍历

将前序的迭代稍微修改一下,将左右节点入栈的顺序调转,最后结果逆转

public static List<Integer> postorderTraversal(TreeNode root){
        List<Integer> result=new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode peek = stack.pop();
            result.add(peek.val);
            if (peek.left!=null){
                stack.push(peek.left);
            }
            if (peek.right!=null){
                stack.push(peek.right);
            }
        }
        Collections.reverse(result);
        return result;
    }

 

总结:

  • 迭代法——前中后遍历的思路都是一样的,先判断root==null,然后根据顺序进行左右子树迭代;
  • 递归法——后序在前序的基础上改了左右节点入栈顺序,中序使用cur指向当前位置,即使stack==null,若cur!=null,也可以继续循环执行代码,在循环中,对cur==null和cur!=null进分别进行相应的代码逻辑执行。

 

层次遍历(借助队列)

使用了迭代法,关键点在于len,记录每层的节点个数。

public static List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> resList=new ArrayList<>();
        if (root == null){
            return resList;
        }

        Queue<TreeNode> queue=new LinkedList<>();
        queue.offer(root);

        while ((!queue.isEmpty())){
            List<Integer> itemList=new ArrayList<>();
            int len =queue.size();

            while (len>0){
                TreeNode tmpNode=queue.poll();
                itemList.add(tmpNode.val);
                if (tmpNode.left!=null){
                    queue.offer(tmpNode.left);
                }
                if (tmpNode.right!=null){
                    queue.offer(tmpNode.right);
                }
                len--;
            }
                resList.add(itemList);

        }
        return resList;
    }

总结:

  • 队列的增加和移除方法为offer(),poll();双端队列的增加和移除方法是add(),remove()
  • 栈是push()和pop()

 

翻转二叉树

使用前序遍历,遍历到左右子树的时候swap。

public TreeNode invertTree(TreeNode root) {
         if (root==null) return root;

        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node = stack.pop();
            swap(node);
            if (node.left!=null){
                stack.push(node.left);
            }
            if(node.right!=null){
                stack.push(node.right);
            }
        }
        return root;
    }
    public  void swap(TreeNode node){
        TreeNode temp=node.left;
        node.left=node.right;
        node.right=temp;
    }

⭐重点:交换时要将root传入而不是两个子节点,传入子节点相当于新建了两个局部变量,只改变了这两个局部变量,没有改变结构。

 

二叉树是否对称

需要对左右节点是否为null进行判断

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

 

最大深度

当变量定义为局部变量时,每次递归都会把结果存到对应栈层里,这样才有效,如果是全局变量则会被覆盖。

public static int maxDepth(TreeNode root) {
        if (root ==null) return 0;
        int l_depth=maxDepth(root.left)+1;
        int r_depth=maxDepth(root.right)+1;
        return Math.max(l_depth,r_depth);
    }

 

二叉树的最小深度(节点数)⭐

二叉树的最小深度不会因为左子树为空就是1,当左子树为空时取右子树的深度+1

如果左子树为null,直接判断右子树;如果右子树为null,直接判断左子树;若两个子树都不为null,则都判断取最小值

public int minDepth(TreeNode root) {
        if (root==null) return 0;
        if (root.left==null) {
             return minDepth(root.right)+1;
        }else if (root.right==null){
            return minDepth(root.left)+1;
        }
        int l_legth=minDepth(root.left);
        int r_legth=minDepth(root.right);
        return Math.min(l_legth,r_legth)+1;
    }

 

平衡二叉树

获得左右子树的高度差,如果高度差大于1直接递归向上返回,如果高度差小于=1,返回该节点的最大长度。

public boolean isBalanced(TreeNode root) {
        if(root ==null){
            return true;
        }
        return Depth(root)!=-1;
    }
    public Integer Depth(TreeNode node){
        int depthL=0;
        int depthR=0;
        if(node.left!=null){
            depthL=Depth(node.left);
        }
        if(node.right!=null){
            depthR=Depth(node.right);
        }
        if(Math.abs(depthL-depthR)>1|| depthL==-1 || depthR==-1){
            return -1;
        }
        return Math.max(depthL,depthR)+1;
    }

 

二叉树的所有路径⭐

别想着直接用StringBuffer,搞不明白,用ArrayList存结果,然后进行转换

逻辑:终止条件是左右节点都为null

          单层逻辑是将当前节点的值存入到list中

   回溯对左右节点分别是否为null进行判断,如果不为null各自执行完后都需要回溯,必须要有判断

class Solution {
    List<String> result=new ArrayList<>();
    List<Integer> temp=new ArrayList<>();
    public List<String> binaryTreePaths(TreeNode root) {
        PathNode(root);
        return result;
    }
    public void PathNode(TreeNode root){
        //单层处理逻辑
        if (root==null){
            return;
        }
        temp.add(root.val);
        //终止条件
        if (root.left==null && root.right==null){
            //将路径传入结果
            StringBuffer sb=new StringBuffer();
            for (int i = 0; i < temp.size()-1; i++) {
                sb.append(temp.get(i)+"->");
            }
            sb.append(temp.get(temp.size()-1));
            result.add(sb.toString());
            return;
        }
        if (root.left!=null){
            PathNode(root.left);
            temp.remove(temp.size()-1);

        }
        if (root.right!=null){
            PathNode(root.right);
            temp.remove(temp.size()-1);
        }
    }
}

 

二叉树的最近公共祖先

分情况讨论:如果根节点为空或者p或q,直接返回root;

      如果递归左右子树得到的结果都不为null,也返回root;

      如果左子树结果不为null右子树为null,最近公共祖先是左节点,反过来为右节点

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || root==p || root ==q){
            return root;
        }
        TreeNode left = lowestCommonAncestor(root.left, p,q);
        TreeNode right=lowestCommonAncestor(root.right,p,q);
        if (left!=null && right!=null){
            return root;
        }else if(left !=null){
            return left;
        }
        return right;
    }

 

完全二叉树的节点个数

没有结合完全二叉树的特点,而是使用了普遍适用的

posted @ 2025-03-11 16:37  Dyj07  阅读(11)  评论(0)    收藏  举报