leetcode刷题记录

Posted xiuzhublog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leetcode刷题记录相关的知识,希望对你有一定的参考价值。

递归

104.二叉树的最大深度

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
    }
}

将问题转化为二叉树的深度等于1+左右子树中数值更大的深度

111.二叉树的最小深度

这个题遇上一个差不多,提交的时候1,2这个用例没有通过。题目中最小深度是从根节点到最近叶子节点的最短路径上的节点数量。所以这个用例应该输出的长度是2不是1.

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

 

 

110.平衡二叉树

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

上一个问题的进阶版,只需要在上一题的基础上判定两个子树深度之差的绝对值是否大于一。

 543.二叉树的直径

与之前的问题一脉相承,根据题目,树的直径其实就是使用深度优先搜索找到最长的两条路径进行拼接。有一个细节问题。求直径(即求路径长度的最大值)等效于求路径经过节点数的最大值减一。所以根节点的问题刚好可以忽略。

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

 226.翻转二叉树

思路是递归交换左右子节点

 

 

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null)return null;
        TreeNode left=root.left;
        root.left=invertTree(root.right);
        root.right=invertTree(left);
        return root;

    }
}

 617.合并二叉树

 官方题解:

我们可以对这两棵树同时进行前序遍历,并将对应的节点进行合并。在遍历时,如果两棵树的当前节点均不为空,我们就将它们的值进行相加,并对它们的左孩子和右孩子进行递归合并;如果其中有一棵树为空,那么我们返回另一颗树作为结果;如果两棵树均为空,此时返回任意一棵树均可(因为都是空)。

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

112.路径总和

 依旧是递归

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

437.路径总和III 

和上一题差不多,区别只是不一定包含起点和终点

class Solution {
    public int pathSum(TreeNode root, int sum) {
        if (root == null) return 0;
    int ret = pathSumStartWithRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum);
    return ret;
}

private int pathSumStartWithRoot(TreeNode root, int sum) {
    if (root == null) return 0;
    int ret = 0;
    if (root.val == sum) ret++;
    ret += pathSumStartWithRoot(root.left, sum - root.val) + pathSumStartWithRoot(root.right, sum - root.val);
    return ret;
    }
}

572.另一个树的子树

 关于树的题目是有套路的,这几个题都可以通过递归访问左子节点和右子节点解决。

  public boolean isSubtree(TreeNode s, TreeNode t) {
        if (s == null) return false;
    return isSubtreeWithRoot(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t);
}

private boolean isSubtreeWithRoot(TreeNode s, TreeNode t) {
    if (t == null && s == null) return true;
    if (t == null || s == null) return false;
    if (t.val != s.val) return false;
    return isSubtreeWithRoot(s.left, t.left) && isSubtreeWithRoot(s.right, t.right);
    }

101.对称二叉树

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

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

 404.左叶子之和

首先判断根节点的左子节点是不是叶子节点,是的话添加,不是的话向下一层搜索。深度优先搜索的方法。

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

private boolean isLeaf(TreeNode node){
    if (node == null) return false;
    return node.left == null && node.right == null;
    }
}

687.最长同值路径

最长的路径可能有三种情况:
1.在左子树内部
2.在右子树内部
3.在穿过左子树,根节点,右子树的一条路径中

设计一个递归函数,返回以该节点为根节点,向下走的最长同值路径
知道这个值以后
以某个节点为根节点的最长同值路径就是,
如果该节点的值等于其左子树的值,则最长同值路径要加上左子树的最长同值路径,如果不等,左子树的路径为0
如果该节点的值等于其右子树的值,则最长同值路径要加上右子树的最长同值路径,如果不等,右子树的路径为0

我们用一个全局变量记录这个最大值,不断更新

private int path = 0;

public int longestUnivaluePath(TreeNode root) {
    dfs(root);
    return path;
}

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;
    path = Math.max(path, leftPath + rightPath);
    return Math.max(leftPath, rightPath);
}

337.打家劫舍III

 这个题也有三种情况:

1.从根节点开始

2.从根节点的左子节点开始

3.从根节点的右子节点开始

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

(这种写法耗时过长,有待优化)。

671.二叉树中第二小的节点


class Solution {
    public int findSecondMinimumValue(TreeNode root) {
        if(root == null || (root.left == null && root.right == null)) return -1;//没有最小节点

        //找出候选数,默认就是子节点值,如果子节点值和root值相同,递归,在子树中寻找候选数
        int left = root.left.val;
        int right = root.right.val;
        if(root.left.val == root.val) left = findSecondMinimumValue(root.left);
        if(root.right.val == root.val) right = findSecondMinimumValue(root.right);

        //如果左右候选数都正常,返回较小值就可
        if(left != -1 && right != -1){
            return Math.min(left, right);
        }
        //如果候选数有-1,说明整个子树中没有可供候选的数
        if(left != -1)
        //左子树正常,答案就是左边的候选数
            return left;
            //右子树正常,返回答案
            //或者右子树也没有候选数,返回-1,即right
            return right;
    }
}

层次遍历

637.二叉树层的平均值

使用了官方题解的广度优先搜索思路:官方题解

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> ret = new ArrayList<>();
        if (root == null) return ret;
        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);
        }
        ret.add(sum / cnt);
    }
    return ret;
    }
}

513.找树左下角的值

首先要找的最深的一层,然后找到最深一层最左边的叶子结点,可以采用从右向左层次遍历,把遍历的值放进一个队列。那么最后一个出队的值就是要找的节点。

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

前中后序遍历

遍历顺序复习:

    1
   / \\
  2   3
 / \\   \\
4   5   6

 

层次遍历顺序:[1 2 3 4 5 6]

前序遍历顺序:[1 2 4 5 3 6]

中序遍历顺序:[4 2 5 1 3 6]

后序遍历顺序:[4 5 2 6 3 1]

层次遍历使用 BFS 实现,利用的就是 BFS 一层一层遍历的特性;而前序、中序、后序遍历利用了 DFS 实现。

前序、中序、后序遍只是在对节点访问的顺序有一点不同,其它都相同。

① 前序

void dfs(TreeNode root) {
    visit(root);
    dfs(root.left);
    dfs(root.right);
}

② 中序

void dfs(TreeNode root) {
    dfs(root.left);
    visit(root);
    dfs(root.right);
}

③ 后序

void dfs(TreeNode root) {
    dfs(root.left);
    dfs(root.right);
    visit(root);
}

 144.二叉树的前序遍历

递归方法:

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

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

使用非递归方法:

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> res=new ArrayList<>();
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root); 
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if(node==null)continue;
            res.add(node.val);
            stack.push(node.right);
            stack.push(node.left);//先有后左,保证左子节点后进先出
        }
        return res;
    }
}

 94.中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
         List<Integer> ret = new ArrayList<>();
         if (root == null) return ret;
         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();
        ret.add(node.val);
        cur = node.right;
    }
        return ret;
    }
}

 

145.二叉树的后序遍历(非递归)

跟前序遍历区别不大

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

 BST

669.修剪二叉搜索树

根据二叉搜索树的特性,

当node.val > R,那么修剪后的二叉树必定出现在节点的左边。

当node.val < L,那么修剪后的二叉树出现在节点的右边。

否则,我们将会修剪树的两边。

class Solution {
    public TreeNode trimBST(TreeNode root, int L, int R) {
        if (root == null) return root;
        if (root.val > R) return trimBST(root.left, L, R);
        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;
    }
}

 230.二叉搜索树中第K小的元素

通过构建 BST 的中序遍历序列,则第 k-1 个元素就是第 k 小的元素。

class Solution {
        private int cnt = 0;
        private int val;

        public int kthSmallest(TreeNode root, int k) {
            inOrder(root, k);
            return val;
        }

        private void inOrder(TreeNode node, int k) {
            if (node == null) return;
            inOrder(node.left, k);
            cnt++;
            if (cnt == k) {
             val = node.val;
             return;
        }
        inOrder(node.right, k);
    }
}

递归解法

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        int leftCnt = count(root.left);
        if (leftCnt == k - 1) return root.val;
        if (leftCnt > k - 1) return kthSmallest(root.left, k);
        return kthSmallest(root.right, k - leftCnt - 1);
    }
    private int count(TreeNode node) {
        if (node == null) return 0;
        return 1 + count(node.left) + count(node.right);
    }
}

238.把二叉搜索树转换为累加树

在递归方法中,我们维护一些递归调用过程中可以访问和修改的全局变量。首先我们判断当前访问的节点是否存在,如果存在就递归右子树,递归回来的时候更新总和和当前点的值,然后递归左子树。如果我们分别正确地递归 root.right 和 root.left ,那么我们就能正确地用大于某个节点的值去更新此节点,然后才遍历比它小的值。

class Solution {
    private int sum = 0;

    public TreeNode convertBST(TreeNode root) {
        traver(root);
        return root;
    }

    private void traver(TreeNode node) {
        if (node == null) return;
         traver(node.right);
        sum += node.val;
        node.val = sum;
        traver(node.left);
    }
}

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

根据二叉搜索树的性质,如果两个节点都比根节点小,那就向下搜索根节点的左节点。向右同理。

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

 236.二叉树的最近公共祖先

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

108.从有序数组中构建二叉查找树

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
    return toBST(nums, 0, nums.length - 1);
}

private TreeNode toBST(int[] nums, int sIdx, int eIdx){
    if (sIdx > eIdx) return null;
    int mIdx = (sIdx + eIdx) / 2;
    TreeNode root = new TreeNode(nums[mIdx]);
    root.left =  toBST(nums, sIdx, mIdx - 1);
    root.right = toBST(nums, mIdx + 1, eIdx);
    return root;
     }
}

109.有序链表转换二叉搜索树中

class Solution {
   public TreeNode sortedListToBST(ListNode head) {
    if (head == null) return null;
    if (head.next == null) return new TreeNode(head.val);
    ListNode preMid = preMid(head);
    ListNode mid = preMid.next;
    preMid.next = null;  // 断开链表
    TreeNode t = new TreeNode(mid.val);
    t.left = sortedListToBST(head);
    t.right = sortedListToBST(mid.next);
    return t;
}

private ListNode preMid(ListNode head) {
    ListNode slow = head, fast = head.next;
    ListNode pre = head;
    while (fast != null && fast.next != null) {
        pre = slow;
        slow = slow.next;
        fast = fast.next.next;
    }
    return pre;
   }
}

653.两数之和4

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

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

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

private void inOrder(TreeNode root, List<Integer> nums) {
    if (root == null) return;
    inOrder(root.left, nums);
    nums.add(root.val);
    inOrder(root.right, nums);
}
}

530.二叉搜索树的最小绝对差

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

class Solution {
    private int minDiff = Integer.MAX_VALUE;
    private TreeNode preNode = null;

    public int getMinimumDifference(TreeNode root) {
        inOrder(root);
        return minDiff;
    }

    private void inOrder(TreeNode node) {
        if (node == null) return;
        inOrder(node.left);
        if (preNode != null) minDiff = Math.min(minDiff, node.val - preNode.val);
        preNode = node;
        inOrder(node.right);
    }
}

501.二叉搜索树中的众数

class Solution {
    private int curCnt = 1;
    private int maxCnt = 1;
    private TreeNode preNode = null;

    public int[] findMode(TreeNode root) {
        List<Integer> maxCntNums = new ArrayList<>();
        inOrder(root, maxCntNums);
        int[] ret = new int[maxCntNums.size()];
        int idx = 0;
        for (int num : maxCntNums) {
            ret[idx++] = num;
        }
        return ret;
    }

private void inOrder(TreeNode node, List<Integer> nums) {
    if (node == null) return;
    inOrder(node.left, nums);
    if (preNode != null) {
        if (preNode.val == node.val) curCnt++;
        else curCnt = 1;
    }
    if (curCnt > maxCnt) {
        maxCnt = curCnt;
        nums.clear();
        nums.add(node.val);
    } else if (curCnt == maxCnt) {
        nums.add(node.val);
    }
    preNode = node;
    inOrder(node.right, nums);
    }
}

Trie

介绍

208.实现Trie

class Trie {

    private class Node {
        Node[] childs = new Node[26];
        boolean isLeaf;
    }

    private Node root = new Node();

    public Trie() {
    }

    public void insert(String word) {
        insert(word, root);
    }

    private void insert(String word, Node node) {
        if (node == null) return;
        if (word.length() == 0) {
            node.isLeaf = true;
            return;
        }
        int index = indexForChar(word.charAt(0));
        if (node.childs[index] == null) {
            node.childs[index] = new Node();
        }
        insert(word.substring(1), node.childs[index]);
    }

    public boolean search(String word) {
        return search(word, root);
    }

    private boolean search(String word, Node node) {
        if (node == null) return false;
        if (word.length() == 0) return node.isLeaf;
        int index = indexForChar(word.charAt(0));
        return search(word.substring(1), node.childs[index]);
    }

    public boolean startsWith(String prefix) {
        return startWith(prefix, root);
    }

    private boolean startWith(String prefix, Node node) {
        if (node == null) return false;
        if (prefix.length() == 0) return true;
        int index = indexForChar(prefix.charAt(0));
        return startWith(prefix.substring(1), node.childs[index]);
    }

    private int indexForChar(char c) {
        return c - \'a\';
    }
}

677.键值映射

实现一个Trie,用来求前缀和

class MapSum {

    private class Node {
        Node[] child = new Node[26];
        int value;
    }

    private Node root = new Node();

    public MapSum() {

    }

    public void insert(String key, int val) {
        insert(key, root, val);
    }

    private void insert(String key, Node node, int val) {
        if (node == null) return;
        if (key.length() == 0) {
            node.value = val;
            return;
        }
        int index = indexForChar(key.charAt(0));
        if (node.child[index] == null) {
            node.child[index] = new Node();
        }
        insert(key.substring(1), node.child[index], val);
    }

    public int sum(String prefix) {
        return sum(prefix, root);
    }

    private int sum(String prefix, Node node) {
        if (node == null) return 0;
        if (prefix.length() != 0) {
            int index = indexForChar(prefix.charAt(0));
            return sum(prefix.substring(1), node.child[index]);
        }
        int sum = node.value;
        for (Node child : node.child) {
            sum += sum(prefix, child);