二叉树算法

二叉树

遍历

前序遍历

class Solution {
    List<Integer> list = new ArrayList<>();
    public List<Integer> preorderTraversal(TreeNode root) {
        preOrder1(root);
        return list;
    }

    public void preOrder(TreeNode root){
        if(root == null) return;
        list.add(root.val);
        preOrder(root.left);
        preOrder(root.right);
    }

    // 非递归
    public void preOrder1(TreeNode root){
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            root = stack.pop(); // root先出栈,然后根据栈后入先出,所以下一个出的是左
            list.add(root.val);

            // 右入栈
            if(root.right != null) {
                stack.push(root.right);
            }
            // 左入栈
            if(root.left != null) {
                stack.push(root.left);
            }

        }
    }
}

中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
//        inorder(root, list); // 递归
        inorder1(root, list);  // 非递归
        return  list;

    }

    public void inorder(TreeNode root, List<Integer> list){
        if(root==null) return;
        inorder(root.left, list);
        list.add(root.val);// 递归 中序遍历
        inorder(root.right, list);
    }

    // 迭代的方式解决
    // 用栈  左中右(左中)
    public void inorder1(TreeNode root, List<Integer> list){
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode node = root;
        while (!stack.isEmpty() || node != null) {// || node != null
            while (node != null) {
                stack.push(node);
                node = node.left; // while循环:先填入所有左节点
            }
            node = stack.pop(); // 弹出时,值加入res
             res.add(node.val);
            node = node.right; // 右节点
        }
    }

}

后序遍历

class Solution {
    List<Integer> list = new ArrayList<>();
    public List<Integer> postorderTraversal(TreeNode root) {
        postOrder(root);
        return list;
    }

    public void postOrder(TreeNode root){
        if(root == null) return;
        postOrder(root.left);
        postOrder(root.right);
        list.add(root.val);
    }
    // 相对前序非递归遍历,要用两个栈
    // s1入栈: 头 左 右  s2入栈:头 右 左
    // 出栈:左,右,头
    public void postOrder1(TreeNode root){
        if(root == null) return;
        Stack<TreeNode> s1 = new Stack<TreeNode>();
        Stack<TreeNode> s2 = new Stack<TreeNode>();
        s1.push(root);
        while(!s1.isEmpty()){
            root = s1.pop();
            s2.push(root); // 弹出的节点压入另一个栈
            if(root.left != null){
                s1.push(root.left);// 与前序遍历压栈顺序相反
            }
            if(root.right != null){
                s1.push(root.right);
            }
        }

        while(!s2.isEmpty()){
            list.add(s2.pop().val);
        }

    }
}

层序遍历

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>(); // 层序遍历用队列
        deque.add(root);
        while(!deque.isEmpty()){
            int n = deque.size(); // 分层
            List<Integer> level = new ArrayList<>();
            for(int i=0;i<n;++i){
                TreeNode node = deque.poll();// 队列:poll,add或者offer
                level.add(node.val);
                if(node.left != null) deque.add(node.left);
                if(node.right != null) deque.add(node.right);
            }
            ans.add(level);
        }
        return ans;
    }
}

层序遍历Ⅱ

自底向上的层序遍历

输入:root = [3,9,20,null,null,15,7]
输出:[[15,7],[9,20],[3]]
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null) return ans;

        Stack<List<Integer>> stack = new Stack<>(); // 存
        Deque<TreeNode> deque = new LinkedList<>();
        deque.add(root);
        while(!deque.isEmpty()){
            int n = deque.size();
            List<Integer> level = new LinkedList<>();
            for(int i=0;i<n;i++){
                root = deque.pop();
                level.add(root.val);
                if(root.left != null) deque.add(root.left);
                if(root.right != null) deque.add(root.right);
            }
            stack.push(level);
        }
        while (!stack.isEmpty()) {// 用栈倒出来
            ans.add(stack.pop());
        }

        return ans;
    }
}
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>(); // 层序遍历用队列
        deque.add(root);
        while(!deque.isEmpty()){
            int n = deque.size(); // 分层
            List<Integer> level = new ArrayList<>();
            for(int i=0;i<n;++i){
                TreeNode node = deque.poll();// 队列:poll,add或者offer
                level.add(node.val);
                if(node.left != null) deque.add(node.left);
                if(node.right != null) deque.add(node.right);
            }
            ans.add(0, level);// 一直往最前面插入level
        }
        return ans;
    }
}

锯齿形层序遍历

先得到层序遍历,再手动调整

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>(); // 层序遍历用队列
        deque.add(root);
        while(!deque.isEmpty()){
            int n = deque.size(); // 分层
            List<Integer> level = new ArrayList<>();
            for(int i=0;i<n;++i){
                TreeNode node = deque.poll();// 队列:poll,add或者offer
                level.add(node.val);
                if(node.left != null) deque.add(node.left);
                if(node.right != null) deque.add(node.right);
            }
            ans.add(level);
        }
        //****************************2错误**********************************
        // list 不是像数组那样去大小
        // 数组.length
        // list.size()
        // String.length()
        for(int i=0;i<ans.length && (i & 1) == 0;i++){// 奇数时调整
            ans.get(i).reverse();// list 没有reverse方法
        }
        // (i & 1) == 0 位运算判断奇偶在for循环中不能使用,因为若判定为false,循环停止。
        return ans;
    }
}

		// Collections.reverse(list) 
        for(int i=1;i<ans.size();i += 2){
            Collections.reverse(ans.get(i));
        }

在这里插入图片描述

class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if(root == null) return ans;
        Deque<TreeNode> deque = new LinkedList<>(); // 层序遍历用队列
        deque.add(root);
        boolean leftOrder = true;
        while(!deque.isEmpty()){
            int n = deque.size(); // 分层
            // List<Integer> level = new ArrayList<>();
            Deque<Integer> level = new LinkedList<>();// 用双端队列存储
            for(int i=0;i<n;++i){
                TreeNode node = deque.poll();// 队列:poll,add或者offer
                
                if(leftOrder) {
                    level.addLast(node.val);
                }else {
                    level.addFirst(node.val);
                }

                if(node.left != null) deque.add(node.left);
                if(node.right != null) deque.add(node.right);
            }

            leftOrder = !leftOrder;
            ans.add(new LinkedList<Integer>(level));// **********转换类型*************
        }
        
        // for(int i=1;i<ans.size();i += 2){// 奇数时调整
        //     Collections.reverse(ans.get(i));
        // }
        return ans;
    }
}

二叉树最大宽度

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。

每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。

【又不能用满二叉树公式计算,有些例子】

输入: 
	   1
     /   \
    3     2
   / \     \  
  5   3     9 
  
输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。

输入: 
	  1
     /  
    3    
   / \       
  5   3   
输出: 2
解释: 最大值出现在树的第 3 层,宽度为 2 (5,3)。
// 两端点间的null节点不计入长度
class Solution {
    // 【简单粗暴】层序遍历,存每一层的size,取最大值
    // 却不能应对第一个例子,null也被记录了,无可奈何
    public int widthOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        Deque<TreeNode> deque = new LinkedList<>();
        // List<Integer> list = new ArrayList<>();
        int max = 1;
        deque.add(root);
        while(!deque.isEmpty()) {
            int size = deque.size();
            for(int i = 0;i < size;i++) {
                TreeNode node = deque.poll();
                if(node.left != null) deque.offer(node.left);
                if(node.right != null) deque.offer(node.right);
            }
            max = Math.max(max, size);
        }

        return max;
    }
}

这个问题中的主要想法是给每个节点一个 index值,如果我们走向左子树,那么 index-> index* 2,如果我们走向右子树,那么 index-> index* 2 + 1。当我们在看同一层深度的位置值 L 和 R 的时候,宽度就是 R - L + 1。

//不用注释也能看懂
class MyTreeNode {
    TreeNode node;
    int index;
  
    public MyTreeNode(TreeNode node, int index){
        this.node = node;
        this.index = index;// 将坐标与node绑定
    }
}

class Solution {
    public int widthOfBinaryTree(TreeNode root) {
        if(root == null) return 0;
        Deque<MyTreeNode> list = new ArrayDeque<>();
        list.add(new MyTreeNode(root, 1));
        int maxWidth = Integer.MIN_VALUE;          
        while(!list.isEmpty()){
            int size = list.size();
            int start = list.peekFirst().index;
            int end = list.peekLast().index;
            maxWidth = Math.max(maxWidth, end - start + 1);// 瞥
            for(int i = 0; i < size; i++){
                MyTreeNode cur = list.pollFirst();// 层序遍历
                if(cur.node.left != null) {
                    list.offerLast(new MyTreeNode(cur.node.left, 2 * cur.index));// 左在队列中的位置
                }
                if(cur.node.right != null) {
                    list.offerLast(new MyTreeNode(cur.node.right, 2 * cur.index + 1));// 右在队列中的位置
                }
            }
        } 
        return maxWidth;
    }   
}

二叉树的右视图

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

    }
}

递归套路

对称二叉树

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

    }

    // 三个递归的终止条件
    public boolean isS(TreeNode left, TreeNode right) {

        if(left == null && right == null){ // 1.左右不存在
            return true;
        }
        // 排除了左右不存在:2.存在一个节点 3. 存在两个节点
        if(left == null || right == null){ // 有一个不存在
            return  false;
        }
        if(left.val != right.val){// 剩下是存在两个节点,所以判断,值不等则返回false,相等则往下判断
            return false;
        }

        return isS(left.left, right.right) && isS(left.right, right.left);
    }
}

验证搜索二叉树

验证搜索二叉树:1.中序遍历是一个升序 2.递归中序遍历判断

// 遍历后判断
class Solution {
    List<Integer> list = new ArrayList<>();
    public boolean isValidBST(TreeNode root) {
        inOrder(root);
        
        for(int i = 0;i < list.size();i++) {
            if(i>0 && list.get(i) <= list.get(i-1)) { // 如果有非升序 [2, 2, 2] 
                return false;
            }
        }
        return true;
    }
    
    // 中序遍历
    public void inOrder(TreeNode root){
        if(root == null) return;
        inOrder(root.left);
        list.add(root.val);
        inOrder(root.right);
    }
    
}
// 快速,在遍历时判断
class Solution {
    public boolean isValidBST(TreeNode root) {
        return process(root);
        
    }
	// 这里犯了大病,定义为static就不会被修改了
    // public static int preValue = Integer.MIN_VALUE; 
    public int preValue = Integer.MIN_VALUE; 

    // 中序遍历 升序结构
    public boolean process(TreeNode root){
        if(root == null) return true;

        if(!process(root.left)) return false; //不能漏了

        if(root.val <= preValue){
            return false;
        }else{
            preValue = root.val;
        }
        
        return process(root.right);

    }

}

// 别人的题解:
class Solution {
    long pre = Long.MIN_VALUE;
    public boolean isValidBST(TreeNode root) {
        if (root == null) {
            return true;
        }
        // 访问左子树
        if (!isValidBST(root.left)) {
            return false;
        }
        // 访问当前节点:如果当前节点小于等于中序遍历的前一个节点,说明不满足BST,返回 false;否则继续遍历。
        if (root.val <= pre) {
            return false;
        }
        pre = root.val;
        // 访问右子树
        return isValidBST(root.right);
    }
}

平衡二叉树

【递归套路】向左右子树要信息

class Solution {
    public boolean isBalanced(TreeNode root) {
        return isB(root).isB;
    }

    public Info isB(TreeNode root) {
        if(root == null) return new Info(0, true);

        Info leftInfo = isB(root.left);
        Info rightInfo  = isB(root.right);

        boolean isB = leftInfo.isB && rightInfo.isB;// 左右都是平衡树
        if(Math.abs(leftInfo.depth - rightInfo.depth) > 1) {// 左右高度差小于1
            isB = false;
        }

        int depth = Math.max(leftInfo.depth, rightInfo.depth) + 1;

        return new Info(depth, isB);
    }

    static class Info {
        int depth;// 高度
        boolean isB;// 是否平衡
        public Info(int depth, boolean isB) {
            this.depth = depth;
            this.isB = isB;
        }
    }
}

二叉树的最大深度

【递归套路】向左右子树要信息

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);

        return Math.max(leftDepth, rightDepth) + 1;
    }
}

完全二叉树

层序遍历
  • 每个节点,有right无left,return false
  • 一旦有左右孩子不双全的节点,后序遇到的所有节点必须为叶子节点
public static boolean isCBT1(Node head) {
    if (head == null) {
        return true;
    }
    LinkedList<Node> queue = new LinkedList<>();
    // 是否遇到过左右两个孩子不双全的节点
    boolean leaf = false;
    Node l = null;
    Node r = null;
    queue.add(head);
    while (!queue.isEmpty()) {
        head = queue.poll();
        l = head.left;
        r = head.right;
        if (
            // 如果遇到了不双全的节点之后,又发现当前节点不是叶节点
            (leaf && (l != null || r != null)) || (l == null && r != null) // 两个条件

        ) {
            return false;
        }
        if (l != null) {
            queue.add(l);
        }
        if (r != null) {
            queue.add(r);
        }
        if (l == null || r == null) {
            leaf = true;
        }
    }
    return true;
}
递归
public static boolean isCBT2(Node head) {
		return process(head).isCBT;
	}

	public static class Info {
		public boolean isFull;// 满
		public boolean isCBT;// 完全二叉树
		public int height;// 高度

		public Info(boolean full, boolean cbt, int h) {
			isFull = full;
			isCBT = cbt;
			height = h;
		}
	}

	public static Info process(Node x) {
		if (x == null) {
			return new Info(true, true, 0);
		}
		Info leftInfo = process(x.left);
		Info rightInfo = process(x.right);
		int height = Math.max(leftInfo.height, rightInfo.height) + 1;// 高度加工
		boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;      
		boolean isCBT = false;
		if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height) {
			isCBT = true;
		} else if (leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
			isCBT = true;
		} else if (leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
			isCBT = true;
		} else if (leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
			isCBT = true;
		}
		return new Info(isFull, isCBT, height);
	}

合并二叉树

class Solution {
	public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
		if(t1==null || t2==null) {
			return t1==null? t2 : t1;
		}
		return dfs(t1,t2);
	}
	
	TreeNode dfs(TreeNode r1, TreeNode r2) {
		// 如果 r1和r2中,只要有一个是null,函数就直接返回
		if(r1==null || r2==null) {
			return r1==null? r2 : r1;
		}
		//让r1的值 等于  r1和r2的值累加,再递归的计算两颗树的左节点、右节点
		r1.val += r2.val;
		r1.left = dfs(r1.left,r2.left);
		r1.right = dfs(r1.right,r2.right);
		return r1;
	}
}

祖先

二叉树的最近公共祖先

class Solution {
    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;
        }

        return  left == null ? right : left;

    }
}

二叉搜索树的最近公共祖先

// 法一:
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    //如果小于等于0,说明p和q位于root的两侧,直接返回即可
    // 等于很妙,p==root, p是q的父节点
    if ((root.val - p.val) * (root.val - q.val) <= 0)
        return root;
    //否则,p和q位于root的同一侧,就继续往下找
    // 用p的值/q的值,去判断在root的哪一侧
    return lowestCommonAncestor(p.val < root.val ? root.left : root.right, p, q);
}
// 法二:同二叉树的最近公共祖先

构造二叉树

将二叉搜索树转化为排序的双向链表

vip题

package com.lzc.test;

public class Play {
    public static void main(String[] args) {
      Node root = new Node(-1);
      Info x = treeNode2listNode(root);
      // 返回x.start;
    }

    public static Info treeNode2listNode(Node root) {
        if(root == null) return new Info(null, null);

        Info left = treeNode2listNode(root.left);
        Info right = treeNode2listNode(root.right);
        // left
        if(left.end != null) left.end.right = root;
        root.left = left.end;
        // right
        if(right.start != null) right.start.left = root;
        root.right = right.start;

        return new Info(left.start!=null?left.start:root, right.end!=null?right.end:root);
    }

    static class Info {
        Node start;
        Node end;

        public Info(Node start, Node end) {
            this.start = start;
            this.end = end;
        }
    }

    static class Node {
        int val;
        Node left;
        Node right;

        public Node(int val) {
            this.val = val;
        }
    }

}

二叉搜索树循环双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。

class Solution {
    Node pre, head;
    public Node treeToDoublyList(Node root) {
        if(root == null) return null;
        dfs(root);
        head.left = pre;
        pre.right = head;
        return head;
    }
    void dfs(Node cur) {
        if(cur == null) return;
        dfs(cur.left);
        if(pre != null) pre.right = cur;
        else head = cur;
        cur.left = pre;
        pre = cur;
        dfs(cur.right);
    }
}

有序链表转换二叉搜索树

给定一个单链表的头节点 head ,其中的元素 按升序排序 ,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。

在这里插入图片描述
在这里插入图片描述

// list找中点
class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head == null) return null;
        if(head.next == null) return new TreeNode(head.val);
        List<Integer> list = new ArrayList<>();
        while(head != null) {
            list.add(head.val);
            head = head.next;
        }

        return buildTree(0, list.size() - 1, list);
    }

    public TreeNode buildTree(int l, int r, List<Integer> list) {
        if(l > r) return null;
        int mid = l + ((r - l) >> 1);
        TreeNode root = new TreeNode(list.get(mid));// 中点做root
        root.left = buildTree(l, mid-1, list);// 从左边list组成左子树,返回左子树的root
        root.right = buildTree(mid+1, r, list);// 从右边list组成右子树,返回右子树的root
        return root;
    }
}
// 快慢指针找中点
class Solution {
    public TreeNode sortedListToBST(ListNode head) {
      if(head == null)return null;
      if(head.next == null)return new TreeNode(head.val);

      ListNode slow = head, fast = head, pre = head;
      while(fast != null && fast.next != null){
        pre = slow;
        slow = slow.next;
        fast = fast.next.next;
      }
      
      ListNode right = slow.next;
      pre.next = null;
      TreeNode root = new TreeNode(slow.val);
      root.left = sortedListToBST(head);
      root.right = sortedListToBST(right);

      return root;
    }   
}

从前序与中序遍历序列构造二叉树

取前序遍历的第一个元素,遍历中序遍历中的元素,获取中序遍历中根的位置pIndex。递归每次都遍历耗时间。

优化:把中序遍历的节点及下标存储到map<node, index>,这样通过map直接取pIndex

在这里插入图片描述

class Solution {
    Map<Integer, Integer> map = new HashMap<>();

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        int n = inorder.length;
        if(n < 2) return new TreeNode(inorder[0]);
        for(int i = 0;i < n;i++) {
            map.put(inorder[i], i);// // 遍历中序数组,存储位置信息
        }
        return myBuildTree(preorder, inorder, 0, preorder.length-1, 0, inorder.length-1);
    }
	// 前序数组,中序数组,[前序左,前序右],[中序左,中序右]
    public TreeNode myBuildTree(int[] preorder, int[] inorder, int preL, int preR, int inL, int inR) {
        if(preL > preR) return null;

        int rootVal = preorder[preL];// 第一个值即为rootVal
        TreeNode root = new TreeNode(rootVal);// 用rootVal建立root
        int pIndex = map.get(rootVal);// 通过map找到划分坐标
        int leftSize = pIndex - inLeft; // pIndex - 1 - inL + 1 左子树中的节点数目
        root.left = myBuildTree(preorder, inorder, preL+1, preL + leftSize, inL, pIndex-1); // 构建左子树
        root.right = myBuildTree(preorder, inorder, preL + leftSize + 1, preR, pIndex+1, inR); // 构建右子树
        return root;// 返回根
    }
}

路径

二叉树的所有路径

输入:root = [1,2,3,null,5]
输出:["1->2->5","1->3"]
List<> String paths
class Solution {
    public List<String> binaryTreePaths(TreeNode root) {
        List<String> paths = new ArrayList<>();
        process(root, "", paths);
        return paths;
    }
	// 递归
    public void process(TreeNode root, String path, List<String> paths){
        if(root!=null){
            StringBuilder pathSB = new StringBuilder(path);// pathSB用于存某一条路径
            pathSB.append(Integer.toString(root.val));// Integer.toString() 将int转为String,并加入到pathSB
            if(root.left==null && root.right==null){
                paths.add(pathSB.toString());// 无路可走了,则将该路径存入到paths中
            }else{
                // 注意 -> 的位置
                pathSB.append("->");// 否则,加一个符号,继续往后走。pathSB转化为String
                process(root.left, pathSB.toString(), paths);// root.left
                process(root.right, pathSB.toString(), paths);// root.right
            }

        }
    }
}
List<> Integer

参考下面的:确定有哪些路径

路径和等于target

是否存在

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。

class Solution {
    boolean ret = false;
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root == null) return false;
        process(root, targetSum);
        return ret;

    }

    public void process(TreeNode root, int target) {
        if(root == null) return;
        target -= root.val;
        if(target == 0 && root.left == null && root.right == null) {
            ret = true;
        }
        process(root.left, target);
        process(root.right, target);
    }
}
确定有哪些路径

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。

class Solution {
    List<List<Integer>> paths = new ArrayList<>();
    List<Integer> path = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {  
        if(root == null) return paths;
        process(root, target);
        return paths;
    }

    public void process(TreeNode root, int target) {
        if(root == null) return;
        // 减去当前node的值,并加入路径
        path.add(root.val);
        target -= root.val;
        // 递归终止条件
        if(target == 0 && root.left == null && root.right == null) {
            paths.add(new ArrayList<>(path));// 细节:为什么我要通过构造方法传入path,不能直接res.add(path)
            //  因为直接加入,加入的是引用(指向的堆中数据会变化),需要克隆一份加入
        }
        // 这还不能在else中
        process(root.left, target);
        process(root.right, target);
        path.remove(path.size() - 1);
    }
}
任意路径

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }

        int res = rootSum(root, targetSum);
        res += pathSum(root.left, targetSum);
        res += pathSum(root.right, targetSum);
        return res;
    }

    public int rootSum(TreeNode root, int targetSum) {
        int res = 0;

        if (root == null) {
            return 0;
        }
        int val = root.val;
        if (val == targetSum) {
            res++;
        } 

        res += rootSum(root.left, targetSum - val);
        res += rootSum(root.right, targetSum - val);
        return res;
    }
}

二叉树中的最大路径和

路径 被定义为**一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。**同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。路径和 是路径中各节点值的总和。

给你一个二叉树的根节点 root ,返回其 最大路径和 。

【递归套路,向左右子树要信息】

在这里插入图片描述

class Solution {
    int maxSum = Integer.MIN_VALUE;// 不能是0

    public int maxPathSum(TreeNode root) {
        maxGain(root);
        return maxSum;
    }

    public int maxGain(TreeNode node) {
        if (node == null) {
            return 0;
        }
        
        // 递归计算左右子节点的最大贡献值
        // 只有在最大贡献值大于 0 时,才会选取对应子节点
        int leftGain = Math.max(maxGain(node.left), 0);// 如果负数,则丢弃
        int rightGain = Math.max(maxGain(node.right), 0);

        // 节点的最大路径和取决于该节点的值与该节点的左右子节点的最大贡献值
        int priceNewpath = node.val + leftGain + rightGain;

        // 更新答案
        maxSum = Math.max(maxSum, priceNewpath);

        // 返回节点的最大贡献值
        return node.val + Math.max(leftGain, rightGain);
    }
}

二叉树的直径

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。路径长度 = 路径节点数 - 1。

【递归套路】

class Solution {
    int max = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        process(root);
        return max - 1;
    }
    public int process(TreeNode root) {
        if(root == null ) return 0;

        int leftDepth = process(root.left);// 左子树的深度
        int rightDepth = process(root.right);// 右子树的深度
        max = Math.max(max, leftDepth + rightDepth + 1);

        return Math.max(leftDepth, rightDepth) + 1;  
    }
}

求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。

所有路径之和

class Solution {

    int res = 0;
    public int sumNumbers(TreeNode root) {
        dfs(root, 0);
        return res;
    }
    public void dfs(TreeNode root, int number)
    {
        number = number * 10 + root.val;
        if(root.left == null && root.right == null)  res += number; //遍历到叶节点,将number加入res中
        if(root.left != null)  dfs(root.left,number);				//递归左子树
        if(root.right != null) dfs(root.right,number);				//递归右子树
    }
}
class Solution {
    List<Integer> path = new ArrayList<>();
    List<List<Integer>> paths = new ArrayList<>();

    public int sumNumbers(TreeNode root) {
        if(root == null) return 0;
        process(root);
        int totalSum = 0;
        for(int i = 0;i < paths.size();i++) {
            List<Integer> path = paths.get(i);
            int len = path.size();
            int pathSum = 0;
            for(int j = len - 1;j >= 0 ;j--) {
                int x = (len - j);
                int multi = 1;
                while(x > 1) { // 十进制
                    multi *= 10;
                    x--;
                }
                pathSum = pathSum + (path.get(j) * multi);
            }
            totalSum += pathSum;
        }
        return totalSum;
    }

    // 存下所有路径
    public void process(TreeNode root) {
        if(root == null) return;

        path.add(root.val);
        if(root.left == null && root.right == null) {
            paths.add(new ArrayList<>(path));
           //  path.remove(path.size() - 1);  不能在这里删除最后一个
        }
        process(root.left);
        process(root.right);
        path.remove(path.size() - 1);// 应该在这个位置
    }
}

在这里插入图片描述
在这里插入图片描述

二叉树的序列化与反序列化

序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。服务器关机保存,开启再加载。

先序

import java.util.*;

public class Solution {
    // 序列化
    String Serialize(TreeNode root) {
        StringBuilder strSB = new StringBuilder("");
        SerializeHelp(root, strSB);
        return strSB.toString();
    }
    
    // 前序遍历
    void SerializeHelp(TreeNode root, StringBuilder strSB) {
        if(root == null) {
            strSB.append("#!");
        } else {
            strSB.append(root.val).append("!");
            SerializeHelp(root.left, strSB);
            SerializeHelp(root.right, strSB);
        }
    }
    
    // 反序列化
    TreeNode Deserialize(String str) {
       if(str == "#!" || str == null || str == "") {
           return null;
       }
        String[] strs = str.split("!");
        Queue<String> queue = new LinkedList<>();
        for(String s : strs) {
            queue.add(s);
        }
        
        return DeserializeHelp(queue);
    } 
    
    TreeNode DeserializeHelp (Queue<String> queue) {
        String s = queue.poll();
        if(s.equals("#")) {
            return null;
        }
        int val = Integer.parseInt(s);
        TreeNode root= new TreeNode(val);
        root.left = DeserializeHelp(queue);
        root.right = DeserializeHelp(queue);
        
        return root;   
    }
}

中序

    /*
     * 二叉树可以通过先序、后序或者按层遍历的方式序列化和反序列化,
     * 以下代码全部实现了。
     * 但是,二叉树无法通过中序遍历的方式实现序列化和反序列化
     * 因为不同的两棵树,可能得到同样的中序序列,即便补了空位置也可能一样。
     * 比如如下两棵树
     *         __2
     *        /
     *       1
     *       和
     *       1__
     *          \
     *           2
     * 补足空位置的中序遍历结果都是{ null, 1, null, 2, null}
     *
     * */
// 序列化
public static Queue<String> inSerial(Node head) {
    Queue<String> ans = new LinkedList<>();
    ins(head, ans);
    return ans;
}

public static void ins(Node head, Queue<String> ans) {
    if (head == null) {
        ans.add(null);
    } else {
        ins(head.left, ans);
        ans.add(String.valueOf(head.value));
        ins(head.right, ans);
    }
}

后序

// 序列化
public static Queue<String> posSerial(Node head) {
    Queue<String> ans = new LinkedList<>();
    poss(head, ans);
    return ans;
}

public static void poss(Node head, Queue<String> ans) {
    if (head == null) {
        ans.add(null);
    } else {
        poss(head.left, ans);
        poss(head.right, ans);
        ans.add(String.valueOf(head.value));
    }
}
// 反序列化
public static Node buildByPosQueue(Queue<String> poslist) {
        if (poslist == null || poslist.size() == 0) {
            return null;
        }
        // 左右中  ->  stack(中右左)
        Stack<String> stack = new Stack<>();
        while (!poslist.isEmpty()) {
            stack.push(poslist.poll());
        }
        return posb(stack);
    }

    public static Node posb(Stack<String> posstack) {
        String value = posstack.pop();
        if (value == null) {
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.right = posb(posstack);
        head.left = posb(posstack);
        return head;
    }

层序

// 序列化
public static Queue<String> levelSerial(Node head) {
    Queue<String> ans = new LinkedList<>();
    if (head == null) {
        ans.add(null);
    } else {
        ans.add(String.valueOf(head.value));
        Queue<Node> queue = new LinkedList<Node>();
        queue.add(head);
        while (!queue.isEmpty()) {
            head = queue.poll(); // head 父   子
            if (head.left != null) {
                ans.add(String.valueOf(head.left.value));
                queue.add(head.left);
            } else {
                ans.add(null);
            }
            if (head.right != null) {
                ans.add(String.valueOf(head.right.value));
                queue.add(head.right);
            } else {
                ans.add(null);
            }
        }
    }
    return ans;
}
// 反序列化
public static Node buildByLevelQueue(Queue<String> levelList) {
    if (levelList == null || levelList.size() == 0) {
        return null;
    }
    Node head = generateNode(levelList.poll());
    Queue<Node> queue = new LinkedList<Node>();
    if (head != null) {
        queue.add(head);
    }
    Node node = null;
    while (!queue.isEmpty()) {
        node = queue.poll();
        node.left = generateNode(levelList.poll());
        node.right = generateNode(levelList.poll());
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
    return head;
}

子树

最大二叉搜索子树的大小,节点

// 返回节点
public static Node maxSubBSTHead1(Node head) {
    if (head == null) {
        return null;
    }
    if (getBSTSize(head) != 0) {
        return head;
    }
    Node leftAns = maxSubBSTHead1(head.left);
    Node rightAns = maxSubBSTHead1(head.right);
    return getBSTSize(leftAns) >= getBSTSize(rightAns) ? leftAns : rightAns;
}

// 获取大小	
public static int getBSTSize(Node head) {
    if (head == null) {
        return 0;
    }
    ArrayList<Node> arr = new ArrayList<>();
    in(head, arr);// 先中序把节点都存入list中
    for (int i = 1; i < arr.size(); i++) {
        if (arr.get(i).value <= arr.get(i - 1).value) {// 不满足升序则返回
            return 0;
        }
    }
    return arr.size();
}

public static void in(Node head, ArrayList<Node> arr) {
    if (head == null) {
        return;
    }
    in(head.left, arr);
    arr.add(head);
    in(head.right, arr);
}
二叉树节点数
  • 遍历,求集合的大小
  • 递归
public int countNodes(TreeNode root) {
    if (root == null){
        return 0;
    }
    return countNodes(root.left) + countNodes(root.right) + 1;
}
二叉树层数
private int countLevel(TreeNode root){
        if(root == null){
            return 0;
        }
        return Math.max(countLevel(root.left),countLevel(root.right)) + 1;
}
递归套路
public static Node maxSubBSTHead2(Node head) {
    if (head == null) {
        return null;
    }
    return process(head).maxSubBSTHead;
    //  return process(head).maxSubBSTSize; // 这样返回大小
}

// 每一棵子树
public static class Info {
    public Node maxSubBSTHead;
    public int maxSubBSTSize;
    public int min;
    public int max;

    public Info(Node h, int size, int mi, int ma) {
        maxSubBSTHead = h;
        maxSubBSTSize = size;
        min = mi;
        max = ma;
    }
}

public static Info process(Node X) {
    if (X == null) {
        return null;
    }
    Info leftInfo = process(X.left);
    Info rightInfo = process(X.right);
    int min = X.value;
    int max = X.value;
    Node maxSubBSTHead = null;
    int maxSubBSTSize = 0;
    if (leftInfo != null) {
        min = Math.min(min, leftInfo.min);
        max = Math.max(max, leftInfo.max);
        maxSubBSTHead = leftInfo.maxSubBSTHead;
        maxSubBSTSize = leftInfo.maxSubBSTSize;
    }
    if (rightInfo != null) {
        min = Math.min(min, rightInfo.min);
        max = Math.max(max, rightInfo.max);
        if (rightInfo.maxSubBSTSize > maxSubBSTSize) {
            maxSubBSTHead = rightInfo.maxSubBSTHead;
            maxSubBSTSize = rightInfo.maxSubBSTSize;
        }
    }
    if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.value))
        && (rightInfo == null ? true : (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.value))) {
        maxSubBSTHead = X;
        maxSubBSTSize = (leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
            + (rightInfo == null ? 0 : rightInfo.maxSubBSTSize) + 1;
    }
    return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
}

二叉树的堂兄弟节点

class Solution {
    // x 的信息
    int x;
    TreeNode xParent;
    int xDepth;
    boolean xFound = false;

    // y 的信息
    int y;
    TreeNode yParent;
    int yDepth;
    boolean yFound = false;

    public boolean isCousins(TreeNode root, int x, int y) {
        this.x = x;
        this.y = y;
        dfs(root, 0, null);
        return xDepth == yDepth && xParent != yParent;
    }

    public void dfs(TreeNode node, int depth, TreeNode parent) {
        if (node == null) {
            return;
        }

        if (node.val == x) {
            xParent = parent;
            xDepth = depth;
            xFound = true;
        } else if (node.val == y) {
            yParent = parent;
            yDepth = depth;
            yFound = true;
        }

        // 如果两个节点都找到了,就可以提前退出遍历
        // 即使不提前退出,对最坏情况下的时间复杂度也不会有影响
        if (xFound && yFound) {
            return;
        }

        dfs(node.left, depth + 1, node);

        if (xFound && yFound) {
            return;
        }

        dfs(node.right, depth + 1, node);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值