leetcode 树(java)

@[TOP](leetcode 树)

前中后序遍历

前序遍历:父节点 -> 左子节点 -> 右子节点
中序遍历:左子节点 -> 父节点 -> 右子节点
后序遍历:左子节点 -> 右子节点 -> 父节点

leetcode144 非递归实现二叉树的前序遍历(Medium)

https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

输入: [1,null,2,3]
1
\
2
/
3
输出: [1,2,3]

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

leetcode145 非递归实现二叉树的后序遍历(hard)

前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。

class Solution {
     
    public List<Integer> postorderTraversal(TreeNode root) {
     
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
     
            TreeNode node = stack.pop();
            if(node == null) continue;
            list.add(node.val);
            stack.push(node.left);
            stack.push(node.right);
        }
        Collections.reverse(list);  //反转List集合中的元素,返回值为void
        return list;
    }
}

leetcode94 非递归实现二叉树的中序遍历(Medium)

力扣

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

递归

leetcode104 树的高度(Easy)

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

leetcode110 平衡树(Easy)

一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过1。

class Solution {
     
    private boolean result = true;
    public boolean isBalanced(TreeNode root) {
     
        maxDepth(root);
        return result;
    }
    public int maxDepth(TreeNode root){
     
        if(root == null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        if(Math.abs(leftDepth - rightDepth) > 1) result = false;
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

leetcode543 两节点的最长路径(Easy)

一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。
两结点之间的路径长度是以它们之间边的数目表示。

class Solution {
     
    private int max = 0;
    public int diameterOfBinaryTree(TreeNode root) {
     
        maxDepth(root);
        return max;
    }
    public int maxDepth(TreeNode root){
     
        if(root == null) return 0;
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        max = Math.max(max, (leftDepth + rightDepth));
        return Math.max(leftDepth,rightDepth) + 1;
    }
}

leetcode226 翻转树(Easy)

class Solution {
     
    public TreeNode invertTree(TreeNode root) {
     
        if(root == null) return null;
        TreeNode left = root.left;  //后面的操作会改变 left 指针,因此先保存下来
        root.left = invertTree(root.right);
        root.right = invertTree(left);
        return root;
    }
}

leetcode617 归并两棵树(Easy)

力扣
如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

class Solution {
     
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
     
        if(t1 == null && t2 == null) return null;
        if(t1 == null) return t2;
        if(t2 == null) return t1;
        TreeNode root = new TreeNode(t1.val + t2.val);
        root.left = mergeTrees(t1.left, t2.left);
        root.right = mergeTrees(t1.right, t2.right);
        return root;
    }
}

leetcode112 判断路径和是否等于一个数(Easy)

力扣
要求根节点到叶子节点

class Solution {
     
    public boolean hasPathSum(TreeNode root, int sum) {
     
        if(root == null) return false;
        if(root.left == null && root.right == null && root.val == sum){
      //叶子节点
            return true;
        }
        return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
    }
}

leetcode437 统计路径和等于一个数的路径总数(Medium)

力扣
不要求从根节点到叶子节点,但方向向下(父 -> 子)

class Solution {
     
    public int pathSum(TreeNode root, int sum) {
     
        if(root == null) return 0;
        int res = pathSumWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
        return res;
    }
    public int pathSumWithRoot(TreeNode root, int sum){
     
        int count = 0;
        if(root == null) return 0;
        if(root.val == sum) count++;
        count += pathSumWithRoot(root.left, sum - root.val) + pathSumWithRoot(root.right, sum - root.val);
        return count;
    }
}

leetcode572 子树(Easy)

力扣

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

class Solution {
     
    public boolean isSubtree(TreeNode s, TreeNode t) {
     
        if(s == null) return false;
        return isSubtreeWithRoot(s,t) || isSubtree(s.left, t) || isSubtree(s.right, t);  //t是子树
    }
    public boolean isSubtreeWithRoot(TreeNode s, TreeNode t){
     
        if(s == null && t == null) return true;
        if(s == null || t == null) return false;
        if(s.val != t.val) return false;  //不相等立马退出
        return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
    }
}

leetcode101 对称二叉树(Easy)

力扣
给定一个二叉树,检查它是否是镜像对称的。

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

leetcode111 二叉树最小深度(Easy)

力扣

class Solution {
     
    public int minDepth(TreeNode root) {
     
        if(root == null) return 0;
        int left = minDepth(root.left);
        int right = minDepth(root.right);
        if(left == 0 || right == 0) return (left + right + 1);  //得到分支的深度
        return Math.min(left, right) + 1;
    }
}

leetcode404 统计左叶子节点的和(Easy)

力扣

class Solution {
     
    public int sumOfLeftLeaves(TreeNode root) {
     
        if(root == null) return 0;
        if(isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }
    public boolean isLeaf(TreeNode node){
     
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

leetcode687 相同节点值的最大路径长度(Easy)

力扣
给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

class Solution {
     
    private int sum;

    public int longestUnivaluePath(TreeNode root) {
     
        dfs(root);
        return sum;
    }
    private int dfs(TreeNode root){
     
        if(root == null) return 0;
        int left = dfs(root.left);
        int right = dfs(root.right);
        int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;
        int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;
        sum = Math.max(sum, leftPath + rightPath);
        return Math.max(leftPath, rightPath);
    }
}

leetcode337 间隔遍历(medium)

力扣

class Solution {
     
    public int rob(TreeNode root) {
     
        if(root == null) return 0;
        int val1 = root.val;
        if(root.left != null) val1 += rob(root.left.left) + rob(root.left.right);
        if(root.right != null) val1 += rob(root.right.left) + rob(root.right.right);
        int val2 = rob(root.left) + rob(root.right);
        return Math.max(val1,val2);
    }
}

leetcode671 找出二叉树中第二小的节点(easy)

力扣
根据题意可知根节点的值最小

class Solution {
     
    public int findSecondMinimumValue(TreeNode root) {
     
        return helper(root, root.val);
    }
    public int helper(TreeNode root, int minVal){
     
   		//叶子节点
        if(root == null) return -1;
        //如果当前结点值>根节点,那么不用再遍历它的子节点,直接返回该值
        if(root.val > minVal) return root.val;
        //否则,当前结点值==根节点,继续遍历,需要在两棵子树找目标值结点
        int l = helper(root.left, minVal);
        int r = helper(root.right, minVal);
        //如果两棵子树均存在大于最小值的节点,那么返回较小的那一个
        if(l != -1 && r !=-1) {
     
            return Math.min(l,r);
        }else {
       //否则,其余情况均返回较大的那一个
            return Math.max(l,r);
        }
    }
}

层次遍历

使用 BFS 进行层次遍历。不需要使用两个队列来分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数,只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。

leetcode637 一棵树每层节点的平均数(easy)

inkedList中的poll()和pop():
当头结点为空的时候,两个函数的处理方式不同:poll()选择返回null,pop()选择抛出异常。
也可以说,两个函数虽然实现效果一样,但他们字面意义不同。

class Solution {
     
    public List<Double> averageOfLevels(TreeNode root) {
     
        List<Double> avg = new ArrayList<>();
        if(root == null) return avg;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
     
            int cnt = queue.size();
            double sum = 0;   //注意数据类型
            for(int i = 0; i < cnt; i++){
     
                TreeNode node = queue.poll();
                sum += node.val;
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            avg.add(sum / cnt);
        }
        return avg;
    }
}

leetcode513 得到最下一层左下角的节点(medium)

力扣

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

BST(二叉查找树)

二叉查找树(BST):根节点大于等于左子树所有节点,小于等于右子树所有节点。

二叉查找树中序遍历有序。

leetcode669 修剪二叉查找树(easy)

力扣
给定最小边界L 和最大边界 R。只保留值在 L ~ R 之间的节点

class Solution {
     
    public TreeNode trimBST(TreeNode root, int L, int R) {
     
        if(root == null) return null;
        //当root过大则右子树全部修剪掉,遍历左子树,修改root
        if(root.val > R) return trimBST(root.left, L, R);
        //当root过小则左子树全部修剪掉,遍历右子树,修改root
        if(root.val < L) return trimBST(root.right, L, R);
        //处理在范围内的节点
        root.left = trimBST(root.left, L, R);
        root.right = trimBST(root.right, L, R);
        return root;
    }
}

leetcode230 二叉查找树的第 k 小的元素(medium)

力扣
中序遍历法

class Solution {
     
    int count;
    int val;
    public int kthSmallest(TreeNode root, int k) {
     
        Order(root, k);
        return val;
    }
    public void Order(TreeNode root, int k){
     
        if(root == null) return;
        Order(root.left, k);
        count++;
        if(count == k){
     
            val = root.val;
        }
        Order(root.right,k);
    }
}

leetcode538 把二叉查找树每个节点的值都加上比它大的节点的值(easy)

力扣
先遍历右子树

class Solution {
     
    int sum;
    public TreeNode convertBST(TreeNode root) {
     
        rightFirst(root);
        return root;
    }
    public void rightFirst(TreeNode root){
     
        if(root == null) return;
        rightFirst(root.right);
        sum += root.val;
        root.val = sum;
        rightFirst(root.left);
    }
}

leetcode235 二叉查找树的最近公共祖先(easy)

力扣

class Solution {
     
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
     
        if(root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);
        if(root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);
        return root;
    }
}

leetcode236 二叉树的最近公共祖先(medium)

力扣

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); 

        return left == null ? right : right == null ? left : root;
    }
}

参考leetcode上的解析:
leetcode 树(java)_第1张图片

leetcode108 从有序数组中构造二叉查找树(easy)

力扣

由于平衡二叉树是左右两个子树的高度差的绝对值不超过 1。因此一种简单的方法是选择中点作为根节点,根节点左侧的作为左子树,右侧的作为右子树即可。原因很简单,这样分配可以保证左右子树的节点数目差不超过 1。因此高度差自然也不会超过 1 了。
上面的操作同时也满足了二叉搜索树,原因就是题目给的数组是有序的。

class Solution {
     
    public TreeNode sortedArrayToBST(int[] nums) {
     
        return toBST(nums, 0, nums.length - 1);
    }
    public TreeNode toBST(int[] nums, int lIdx, int hIdx){
     
        if(lIdx > hIdx) return null;
        int mIdx = lIdx + (hIdx - lIdx) / 2;
        TreeNode root = new TreeNode(nums[mIdx]);
        root.left = toBST(nums, lIdx, mIdx - 1);
        root.right = toBST(nums, mIdx + 1, hIdx);
        return root;
    }
}

leetcode109 根据有序链表构造平衡的二叉查找树(medium)

力扣
思路 :找到中点,只需要使用经典的快慢指针即可。同时为了防止环的出现, 需要斩断指向 mid 的 next 指针,因此需要记录中点前的一个节点,这只需要用一个变量 pre 记录即可。

时间复杂度:由于每个节点最多被访问一次,因此总的时间复杂度为 O(N),其中 N 为链表长度。
空间复杂度:由于使用了递归,这里的空间复杂度的瓶颈在栈空间,因此空间复杂度为O(h),其中h 为树的高度。同时由于是平衡二叉树,因此 h 就是 logN。

class Solution {
     
    public TreeNode sortedListToBST(ListNode head) {
     
        if(head == null) return null;
        if(head.next == null) return new TreeNode(head.val);
        ListNode pre = preMid(head);
        ListNode mid = pre.next; 
        pre.next = null;
        TreeNode t = new TreeNode(mid.val);
        t.left = sortedListToBST(head);
        t.right = sortedListToBST(mid.next);
        return t;
    }
    public ListNode preMid(ListNode head){
     
        ListNode pre = head;
        ListNode slow = head;
        ListNode fast = head.next;
        while(fast != null && fast.next != null){
     
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        return pre;
    }
}

leetcode653 在二叉查找树中寻找两个节点,使它们的和为一个给定值(easy)

力扣
使用中序遍历得到有序数组之后,再利用双指针对数组进行查找。

应该注意到,这一题不能用分别在左右子树两部分来处理这种思想,因为两个待求的节点可能分别在左右子树中。

class Solution {
     
    public boolean findTarget(TreeNode root, int k) {
     
        List<Integer> nums = new ArrayList<>();
        inOrder(root, nums);
        int i = 0, j = nums.size() - 1;
        while(i < j){
     
            int sum = nums.get(i) + nums.get(j);
            if(sum == k) return true;
            if(sum < k) i++;
            else j--;
        }
        return false;
    }
    public void inOrder(TreeNode root, List<Integer> nums){
     
        if(root == null) return;
        inOrder(root.left, nums);
        nums.add(root.val);
        inOrder(root.right,nums);    
    }
}

leetcode530 在二叉查找树中查找两个节点之差的最小绝对值(easy)

力扣
利用二叉查找树的中序遍历为升序的性质,计算中序遍历中临近的两个节点之差的绝对值,取最小值。

class Solution {
     
    private int minDiff = Integer.MAX_VALUE;
    TreeNode preNode = null;
    public int getMinimumDifference(TreeNode root) {
     
        inOrder(root);
        return minDiff;
    }
    public void inOrder(TreeNode root){
     
        if(root == null) return;
        inOrder(root.left);
        if(preNode != null) minDiff = Math.min(minDiff, root.val - preNode.val);
        preNode = root;
        inOrder(root.right);
    }
}

leetcode501 二叉搜索树中的众数(easy)

力扣
答案可能不止一个,也就是有多个值出现的次数一样多。

class Solution {
     
    private TreeNode preNode = null;
    private int maxCount = 1;
    private int count = 1;
    public int[] findMode(TreeNode root) {
     
        List<Integer> maxCntNum = new ArrayList<>();
        inOrder(root, maxCntNum);
        int index = 0;
        int[] res = new int[maxCntNum.size()];
        for(int num : maxCntNum){
     
            res[index++] = num;
        }
        return res;
    }
    public void inOrder(TreeNode root, List<Integer> maxCntNum){
     
        if(root == null) return;
        inOrder(root.left, maxCntNum);
        if(preNode != null) {
     
            if(preNode.val == root.val){
     
                count++;
            }else{
     
                count = 1;
            }
        }
        if(count > maxCount){
     
            maxCount = count; 
            maxCntNum.clear();
            maxCntNum.add(root.val);
        }else if(count == maxCount){
     
            maxCntNum.add(root.val);
        }
        preNode = root;
        inOrder(root.right, maxCntNum);
    }
}

Trie

Trie,又称前缀树或字典树,用于判断字符串是否存在或者是否具有某种字符串前缀。

参考下面的文章学习字典树
https://mp.weixin.qq.com/s/uDar0F7x9w5F3sHOB5tIDA

leetcode208 实现 Trie (前缀树)(medium)

力扣

class Trie {
     
    Trie[] next = new Trie[26]; //每个节点都是一个包含26个字母的数组
    boolean isEnd = false;  //是否为最后一个字母
    /** Initialize your data structure here. */
    public Trie() {
     

    }
    
    /** Inserts a word into the trie. */
    public void insert(String word) {
     
        Trie curr = this;  //当前节点,为空节点
        for(char c : word.toCharArray()){
     
            if(curr.next[c - 'a'] == null){
      //若不存在字母创建Trie
                curr.next[c - 'a'] = new Trie();
            }
            curr = curr.next[c - 'a'];//移动指针
        }
        curr.isEnd = true;  //设置最后一个加入的字母为最后一个
    }
    
    /** Returns if the word is in the trie. */
    public boolean search(String word) {
     
        Trie curr = this;
        for(char c : word.toCharArray()){
     
            if(curr.next[c - 'a'] == null) return false;
            curr = curr.next[c - 'a'];
        }
        return curr.isEnd;  //这个词的最后一个字母必须是搜索的最后一个字母,否则是前缀
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    public boolean startsWith(String prefix) {
     
        Trie curr = this;
        for(char c : prefix.toCharArray()){
     
            if(curr.next[c - 'a'] == null) return false;
            curr = curr.next[c - 'a'];
        }
        return true;
    }
}

leetcode677 实现一个 Trie,用来求前缀和(medium)

力扣

class MapSum {
     
    //创建字典树节点类
    private class Node{
     
        public int value;
        public HashMap<Character, Node> next; //使用map存储Node信息,保存value,以便后续的求和操作
        public Node(){
      //无参构造,传值为0
            this(0);
        }
        public Node(int value){
     
            this.value = value;
            this.next = new HashMap<>();
        }

    }
    private Node root;  //由于插入单词时需要再同一个跟节点上进行,所以在外部初始化一个root
    /** Initialize your data structure here. */
    public MapSum() {
     
        root = new Node();
    }
    
    public void insert(String key, int val) {
     
        Node curr = root;
        for(char c : key.toCharArray()){
     
            if(curr.next.get(c) == null){
     
                curr.next.put(c, new Node());
            }
            curr = curr.next.get(c);
        }
        curr.value = val;
    }
    
    public int sum(String prefix) {
     
        Node curr = root;
        for(char c : prefix.toCharArray()){
     
            if(curr.next.get(c) == null) return 0;
            curr = curr.next.get(c);
        }
        return sum(curr);
    }
    public int sum(Node node){
     
        int sum = node.value;
        for(char c : node.next.keySet()){
     
            sum += sum(node.next.get(c));  //递归获得每个节点的值并相加
        }
        return sum;
    }
}

你可能感兴趣的:(leetcode 树(java))