LeetCode——100热题对应方法一览(56~141)

284 阅读18分钟

56. 合并区间

重写排序Comparator,按内部的left大小排序,然后对比right的大小进行排序,注意compare的重写方式

给出一个区间的集合,请合并所有重叠的区间。

示例:
输入: [[1,3],[2,6],[8,10],[15,18]]
输出: [[1,6],[8,10],[15,18]]
解释: 区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].  

class Solution {
    class IntervalComparator implements Comparator<int[]>{
        @Override
        public int compare(int[] o1, int[] o2) {
            return o1[0] - o2[0];
        }
    }
    public int[][] merge(int[][] intervals) {
        List<int[]> res = new ArrayList<>();
        if (intervals == null || intervals.length == 0)
            return res.toArray(new int[0][]);
        Arrays.sort(intervals, new IntervalComparator());
        int i = 0;
        while (i < intervals.length) {
            int left = intervals[i][0];
            int right = intervals[i][1];
            while (i < intervals.length - 1 && right >= intervals[i + 1][0]) {
                right = Math.max(right, intervals[++i][1]);
            }
            res.add(new int[] {left, right});
            i++;
        }
        return res.toArray(new int[0][]);
    }
}


62. 不同路径

动态规划,dp[j]到达i, j格的步数,逐行更新dp

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 问总共有多少条不同的路径? 


 说明:mn的值均不超过 100。

示例:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右

class Solution {
    public int uniquePaths(int m, int n) {
        if (m == 0 || n == 0) return 0;
        int[] dp = new int[m];
        Arrays.fill(dp, 1);
        for (int i = 1; i < n; i++) {
            for (int j = 1; j < m; j++) {
                dp[j] += dp[j - 1];
            }
        }
        return dp[m - 1];
    }
}


64. 最小路径和

动态规划,注意边界的分类讨论,可直接在表格上更新

给定一个包含非负整数的mxn网格,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。

说明:每次只能向下或者向右移动一步。

示例:
输入:
[
  [1,3,1],
  [1,5,1],
  [4,2,1]
]
输出: 7
解释: 因为路径 1→3→1→1→1 的总和最小。

public class Solution {
    public int minPathSum(int[][] grid) {
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (i == 0 && j == 0) continue;
                else if (i == 0)
                    grid[i][j] += grid[i][j - 1];
                else if (j == 0)
                    grid[i][j] += grid[i - 1][j];
                else
                     grid[i][j] += Math.min(grid[i - 1][j], grid[i][j - 1]);
            }
        }
        return grid[grid.length - 1][grid[0].length - 1];
    }
}


70. 爬楼梯

斐波那契数, dp[i] = dp[i - 1] + dp[i - 2]

假设你正在爬楼梯。需要n阶你才能到达楼顶。

每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?

注意:给定n是一个正整数。

示例:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1.  1 阶 + 1 阶
2.  2 阶

class Solution {
    public int climbStairs(int n) {
        if (n < 2) return n;
        int first = 1;
        int second = 1;
        int third;
        for (int i = 2; i <= n; i++) {
            third = first + second;
            first = second;
            second = third;
        }
        return second;
    }
}


72. 编辑距离

动态规划,dp[i][j]表示word1的前i-1个与word2的前j-1个匹配需要的最少操作数,注意初始化dp[i][j] = j,若相同则直接直接等于[i-1][j-1],不同则三选一最小的+1

给定两个单词 word1 和 word2,计算出将 word1 转换成 word2 所使用的最少操作数 。  

你可以对一个单词进行如下三种操作: 

    1. 插入一个字符 
    2. 删除一个字符 
    3. 替换一个字符

输入: word1 = "horse", word2 = "ros"
输出: 3
解释: 
horse -> rorse (将 'h' 替换为 'r')
rorse -> rose (删除 'r')
rose -> ros (删除 'e')

class Solution {
    public int minDistance(String word1, String word2) {
        int len1 = word1.length();
        int len2 = word2.length();
        int dp_ij, x;
        int[] dp = new int[len2 + 1];
        for (int j = 0; j <= len2; j++) {
            dp[j] = j;
        }
        for (int i = 1; i <= len1; i++) {
            dp_ij = dp[0];
            dp[0] = i;
            for (int j = 1; j <= len2; j++) {
                x = dp[j];
                if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
                    dp[j] = dp_ij;
                } else {
                    dp[j] = Math.min(Math.min(dp[j - 1], dp[j]), dp_ij) + 1;
                }
                dp_ij = x;
            }
        }
        return dp[len2];
    }
}


75. 颜色分类

三指针,curr, p0, p2,注意对不同情况下的指针移动判别

给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 

注意: 

不能使用代码库中的排序函数来解决这道题。

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

进阶: 

    • 一个直观的解决方案是使用计数排序的两趟扫描算法。 首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。 
    • 你能想出一个仅使用常数空间的一趟扫描算法吗? 

class Solution {
    public void sortColors(int[] nums) {
        int p0 = 0, curr = 0;
        int p2 = nums.length - 1;
        while (curr <= p2) {
            if (nums[curr] == 0) {    // 因交换的两数均已检查过,curr++
                nums[curr++] = nums[p0];
                nums[p0++] = 0;
            } else if (nums[curr] == 2) {    // 因交换过来的p2未检查过,curr继续检查
                nums[curr] = nums[p2];
                nums[p2--] = 2;
            } else {
                curr++;
            }
        }
    }
}


76. 最小覆盖字串

滑动窗口法,可将Map换为int[128]以提升速度

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字母的最小子串。

示例:
输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:

  • 如果 S 中不存这样的子串,则返回空字符串 ""
  • 如果 S 中存在这样的子串,我们保证它是唯一的答案。

class Solution {
    public String minWindow(String s, String t) {
        if (s == null || s.length() == 0) return s;
        int[] count_map = new int[128];
        int[] window_map = new int[128];
        int ansL = 0, ansR = 0, minLen = Integer.MAX_VALUE;
        int left = 0, right = 0;
        int count = t.length();
        for (int i = 0; i < t.length(); i++)
            count_map[t.charAt(i)]++;
        while (right < s.length()) {
            char c_r = s.charAt(right);
            if (count_map[c_r] > 0) {
                window_map[c_r]++;
                if (window_map[c_r] <= count_map[c_r])
                    count--;
            }
            while (count == 0) {
                if (minLen > (right - left + 1)) {
                    ansL = left;
                    ansR = right;
                    minLen = right - left + 1;
                }
                char c_l = s.charAt(left);
                if (count_map[c_l] > 0) {
                    window_map[c_l]--;
                    if (window_map[c_l] < count_map[c_l])
                        count++;
                }
                left++;
            }
            right++;
        }
        if (minLen == Integer.MAX_VALUE) return "";
        return s.substring(ansL, ansR + 1);
    }
}


78. 子集

回溯法,list.add(nums[i]); backtrack(...); list.remove(int index);

给定一组不含重复元素的整数数组nums,返回该数组所有可能的子集(幂集)。

说明:解集不能包含重复的子集。

示例:
输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        backtrack(0, nums, res, new ArrayList<>());
        return res;
    }
    private void backtrack(int index, int[] nums, List<List<Integer>> res, ArrayList<Integer> pre) {
        res.add(new ArrayList<Integer>(pre));
        for (int i = index; i < nums.length; i++) {
            pre.add(nums[i]);
            backtrack(i + 1, nums, res, pre);
            pre.remove(pre.size() - 1);
        }
    }
}


79. 单词搜索 ^_^

在每个单元格进行深度优先搜索,注意记录经过节点及判定新节点是否出界,不要忘记dfs到尽头的判断

给定一个二维网格和一个单词,找出该单词是否存在于网格中。 

单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。

示例:
board =
[
  ['A','B','C','E'],
  ['S','F','C','S'],
  ['A','D','E','E']
]

给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.  

class Solution {
    char[][] board;
    String word;
    boolean[][] marked;
    int m;
    int n;
    int[][] direction = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};

        public boolean exist(char[][] board, String word) {
        this.board = board;
        this.word = word;
        m = board.length;
        n = board[0].length;
        marked = new boolean[m][n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (dfs(i, j, 0))
                    return true;
            }
        }
        return false;
    }
    private boolean dfs(int i, int j, int start) {
        if (start == word.length() - 1)
            return board[i][j] == word.charAt(start);
        if (board[i][j] == word.charAt(start)) {
            marked[i][j] = true;
            for (int k = 0; k < 4; k++) {
                int row = i + direction[k][0];
                int col = j + direction[k][1];
                if (inArea(row, col) && !marked[row][col]) {
                    if (dfs(row, col, start + 1))
                        return true;
                }
            }
            marked[i][j] = false;
        }
        return false;
    }
    private boolean inArea(int i, int j) {
        return (i >= 0) && (j >= 0) && (i < m) && (j < n);
    }
}


84. 柱状图中最大的矩形

栈,遍历过程中每当遇到即将push的元素高度下降,则可认为以当前栈顶元素为高的最大矩阵已经出现,则进行pop并计算以栈顶元素为高的最大矩形的面积,注意与42接雨水进行区分

给定n个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。


示例:
输入: [2,1,5,6,2,3]
输出: 10

class Solution {
    public int largestRectangleArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        int max_area = 0;
        stack.push(-1);
        for (int i = 0; i < heights.length; i++) {
            while (stack.peek() != -1 && heights[i] < heights[stack.peek()]) {
                int h = heights[stack.pop()];
                max_area = Math.max(max_area, h * (i - stack.peek() - 1));
            }
            stack.push(i);
        }
        while (stack.peek() != -1) {
            int h = heights[stack.pop()];
            max_area = Math.max(max_area, h * (heights.length - stack.peek() - 1));
        }
        return max_area;
    }
}


85. 最大矩形

逐行统计累计“1”的存在数量,抽象为多个84柱状图中最大矩形的问题

给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例:
输入:
[
  ["1","0","1","0","0"],
  ["1","0","1","1","1"],
  ["1","1","1","1","1"],
  ["1","0","0","1","0"]
]
输出: 6

class Solution {
    private int maxArea(int[] heights) {
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        int maxarea = 0;
        for (int i = 0; i < heights.length; i++) {
            while (stack.peek() != -1 && heights[i] <= heights[stack.peek()]) {
                maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
            }
            stack.push(i);
        }
        while (stack.peek() != -1) {
            maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() - 1));
        }
        return maxarea;
    }
    public int maximalRectangle(char[][] matrix) {
        if (matrix == null || matrix.length == 0) return 0;
        int m = matrix.length;
        int n = matrix[0].length;
        int max_area = 0;
        int[] heights = new int[n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                heights[j] = matrix[i][j] == '1' ? heights[j] + 1 : 0;
            }
            max_area = Math.max(max_area, maxArea(heights));
        }
        return max_area;
    }
}


94. 二叉树的中序遍历

中序:根节点在中间,左子树-根节点-右子树;递归建立helper函数,按序调用;迭代用栈存节点,有左孩子则一直左孩子,null后pop记录val并转向右孩子,而后还是持续左孩子

给定一个二叉树,返回它的中序遍历。

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

输出: [1,3,2]

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

// 递归
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        helper(root, res);
        return res;
    }
    private void helper(TreeNode root, List<Integer> res) {
        if (root != null) {
            if (root.left != null)
                helper(root.left, res);
            res.add(root.val);
            if (root.right != null)
                helper(root.right, res);
        }
    }
}

// 迭代
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        List<Integer> res = new ArrayList<>();
        while (!stack.empty() || root != null) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            res.add(root.val);
            root = root.right;
        }
        return res;
    }
}


96. 不同的二叉搜索树 ^_^

动态规划,探究扣除根节点后左右子树的数量与之前二叉树数量的关系

给定一个整数n,求以 1 ...n为节点组成的二叉搜索树有多少种?

示例:
输入: 3
输出: 5
解释:
给定 n = 3, 一共有 5 种不同结构的二叉搜索树:

   1         3     3      2      1
    \       /     /      / \      \
     3     2     1      1   3      2
    /     /       \                 \
   2     1         2                 3  

class Solution {
    public int numTrees(int n) {
        if (n < 2) return n;
        int[] dp = new int[n + 1];
        dp[0] = 1;
        dp[1] = 1;
        for (int i = 2; i <= n; i++) {
            for (int j = 0; j < i; j++) {
                dp[i] += dp[j] * dp[i - j - 1];
            }
        }
        return dp[n];
    }
}


98. 验证二叉搜索树

递归,建立helper函数,判断每个节点是否小于最大值,大于最小值;迭代,中序遍历二叉搜索树可以得到从小到大的数组

给定一个二叉树,判断其是否是一个有效的二叉搜索树。 

假设一个二叉搜索树具有如下特征: 

  • 节点的左子树只包含小于当前节点的数。 
  • 节点的右子树只包含大于当前节点的数。 
  • 所有左子树和右子树自身必须也是二叉搜索树。
输入:
    2
   / \
  1   3
输出: true

// 递归
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    private boolean helper(TreeNode root, Integer high, Integer low) {
        if (root == null) return true;
        int curr = root.val;
        if (high != null && curr > high) return false;
        if (low != null && curr < low) return false;
        if (!helper(root.left, low, curr)) return false;
        if (!helper(root.right, curr, high)) return false;
        return true;
    }
    public boolean isValidBST(TreeNode root) {
        return helper(root, null, null);
    }
}

// 迭代
class Solution {
    public boolean isValidBST(TreeNode root) {
        Stack<TreeNode> stack = new Stack<>();
        double inorder = -Double.MAX_VALUE;  // root值过大时int会溢出
        while (root != null || !stack.empty()) {
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            if (inorder >= root.val) return false;
            inorder = root.val;
            root = root.right;
        }
        return true;
    }
}


101. 对称二叉树

递归,左==右&&左左==右右&&左右==右左;迭代,队列储存,两两相等,两两存入

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

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

    1
   / \
  2   2
   \   \
   3    3

// 递归
class Solution {
    public boolean isSymmetric(TreeNode root) { 
       if (root == null) return true;
        return helper(root.left, root.right);
    }
    private boolean helper(TreeNode left, TreeNode right) {
        if (left == null && right == null) return true;
        if (left == null || right == null) return false;
        return left.val == right.val
                 && helper(left.left, right.right)
                 && helper(left.right, right.left);
    }
}

// 迭代
class Solution {
    public boolean isSymmetric(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        queue.add(root);
        while (!queue.isEmpty()) {
            TreeNode t1 = queue.poll();
            TreeNode t2 = queue.poll();
            if (t1 == null && t2 == null) continue;
            if (t1 == null || t2 == null) return false;
            if (t1.val != t2.val) return false;
            queue.add(t1.left);
            queue.add(t2.right);
            queue.add(t1.right);
            queue.add(t2.left);
        }
        return true;
    }
}


102. 二叉树的层次遍历

递归,建立helper,参数为root和层数;迭代,队列,每遍历一层就要进行“标记”

给定一个二叉树,返回其按层次遍历的节点值。 (即逐层地,从左到右访问所有节点)。

例如:   给定二叉树: [3,9,20,null,null,15,7],

  3
   / \
  9  20
    /  \
   15   7

返回其层次遍历结果:

[
  [3],
  [9,20],
  [15,7]
]

// 递归
class Solution {
    private List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> levelOrder(TreeNode root) {
        helper(root, 0);
        return res;
    }
    private void helper(TreeNode node, int level) {
        if (node == null) return;
        if (level == res.size())
            res.add(new ArrayList<>());
        res.get(level).add(node.val);
        helper(node.left, level + 1);
        helper(node.right, level + 1);
    }
}

// 迭代
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> res = new ArrayList<>();
        if (root == null) return res;
        Queue<TreeNode> q = new LinkedList<>();
        q.add(root);
        int level = 0;
        while (!q.isEmpty()) {
            res.add(new ArrayList<>());
            int level_len = q.size();
            for (int i = 0; i < level_len; i++) {
                root = q.poll();
                res.get(level).add(root.val);
                if (root.left != null) q.add(root.left);
                if (root.right != null) q.add(root.right);
            }
            level++;
        }
        return res;
    }
}


104. 二叉树的最大深度 ^_^

递归,当前节点最大深度=max(左子树深度,右子树深度)+1;迭代,层序遍历返回level

给定一个二叉树,找出其最大深度。 

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。 

说明: 叶子节点是指没有子节点的节点。 

示例: 给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回它的最大深度 3 。

// 递归
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        int l = maxDepth(root.left);
        int r = maxDepth(root.right);
        return Math.max(l, r) + 1;
    }
}

// 迭代
class Solution {
    public int maxDepth(TreeNode root) {
        if (root == null) return 0;
        Queue<TreeNode> q = new LinkedList<>();
        int level = 0;
        q.add(root);
        while (!q.isEmpty()) {
            int level_len = q.size();
            for (int i = 0; i < level_len; i++) {
                root = q.poll();
                if (root.left != null) q.add(root.left);
                if (root.right != null) q.add(root.right);
            }
            level++;
        }
        return level;
    }
}


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

递归,按前序顺序建立节点,双指针定左右子树的中序范围,注意递归结束条件

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]

返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

class Solution {
    int[] preorder;
    int[] inorder;
    int pre_index = 0;
    Map<Integer, Integer> idx_map = new HashMap<>(); // 已知树中没有重复元素
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        this.inorder = inorder;
        for (int i = 0; i < inorder.length; i++) {
            idx_map.put(inorder[i], i);
        }
        return helper(0, inorder.length);
    }
    private TreeNode helper(int left_idx, int right_idx) {
        if (left_idx == right_idx) return null;
        int curr = preorder[pre_index++];
        TreeNode new_node = new TreeNode(curr);
        int index = idx_map.get(curr);
        new_node.left = helper(left_idx, index);
        new_node.right = helper(index + 1, right_idx);
        return new_node;
    }
}


114. 二叉树展开为链表

  • 法一:对于每一个节点都将其右子树接到其左子树最右边的叶子节点上,并将其改编后的左子树接到其右边,左边置空
  • 法二:基于栈的前序遍历,先入右子节点后入左子节点,建立一个pre指针将上一节点与新pop出的节点连接,不断循环向右

给定一个二叉树,原地将它展开为链表。

例如,给定二叉树

    1
   / \
  2   5
 / \   \
3   4   6

将其展开为:

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

// 法一,左子树插入到右边
class Solution {
    public void flatten(TreeNode root) {
        if (root == null) return;
        while (root != null) {
            if (root.left != null) {
                TreeNode l_right = root.left;
                while (l_right.right != null) {
                    l_right = l_right.right;
                }
                l_right.right = root.right;
                root.right = root.left;
                root.left = null;
            }
            root = root.right;
        }
    }
}

// 法二,基于栈
class Solution {
    public void flatten(TreeNode root) {
        if (root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        TreeNode pre = null;
        while (!stack.empty()) {
            TreeNode curr = stack.pop();
            if (curr.right != null) stack.push(curr.right);
            if (curr.left != null) stack.push(curr.left);
            if (pre != null) {
                pre.left = null;
                pre.right = curr;
            }
            pre = curr;
        }
    }
}


121. 买卖股票的最佳时机 ^_^

一旦亏钱则重新买入,赚钱则继续

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。 

如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。 

 注意你不能在买入股票前卖出股票。

输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
     注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

class Solution {
    public int maxProfit(int[] prices) {
        if (prices == null || prices.length == 0) return 0;
        int res = 0;
        int pri_buy = prices[0];
        for (int i = 1; i < prices.length; i++) {
            if ((prices[i] - pri_buy) < 0) {
                pri_buy = prices[i];
            } else {
                res = Math.max(res, prices[i] - pri_buy);
            }
        }
        return res;
    }
}


124. 二叉树中的最大路径和

递归,若子树路径和小于0则可以忽略,左+root+右 / 左or右+root+上一层

给定一个非空二叉树,返回其最大路径和。

本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。

示例:
输入: [-10,9,20,null,null,15,7]
   -10
   / \
  9  20
    /  \
   15   7
输出: 42

class Solution {
    int res = Integer.MIN_VALUE;
    private int backtrack(TreeNode root) {
        if (root == null) return 0;
        int left = Math.max(backtrack(root.left), 0);
        int right = Math.max(backtrack(root.right), 0);
        res = Math.max(res, left + right + root.val);
        return Math.max(left, right) + root.val;
    }
    public int maxPathSum(TreeNode root) {
        backtrack(root);
        return res;
    }
}


128. 最长连续序列

  • O(n), HashMap,对应value为连续序列的长度,且当新元素出现时只更新首尾的value值
  • O(n), HashSet,先储存所有num,后遍历set,每当遇到第一个元素(即没有比它小1的元素)时,不断向后寻找连续序列 
  • O(nlongn), 先排序,而后顺序寻找连续序列,注意跳过相同元素

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为O(n)

示例:
输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

// HashMap
class Solution {
    public int longestConsecutive(int[] nums) {
        int longestStreak = 0;
        Map<Integer, Integer> nums_map = new HashMap<Integer, Integer>();
        for (int num : nums) {
            if (nums_map.containsKey(num)) continue;
            int left = nums_map.getOrDefault(num - 1, 0);
            int right = nums_map.getOrDefault(num + 1, 0);
            int sum = left + right + 1;
            longestStreak = Math.max(longestStreak, sum);
            nums_map.put(num, -1);
            nums_map.put(num - left, sum);
            nums_map.put(num + right, sum);
        }
        return longestStreak;
    }
}

// HashSet
class Solution {
    public int longestConsecutive(int[] nums) {
        int longestStreat = 0;
        Set<Integer> nums_set = new HashSet<Integer>();
        for (int num : nums) {
            nums_set.add(num);
        }
        for (int num : nums_set) {
            if (!nums_set.contains(num - 1)) {
                int cur_num = num;
                int cur_len = 1;
                while (nums_set.contains(++cur_num)) {
                    cur_len++;
                }
                longestStreat = Math.max(longestStreat, cur_len);
            }
        }
        return longestStreat;
    }
}

// 排序
class Solution {
    public int longestConsecutive(int[] nums) {
        if (nums.length == 0) return 0;
        Arrays.sort(nums);
        int longestStreak = 1;
        int currentStreak = 1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] != nums[i-1]) {
                if (nums[i] == nums[i-1]+1) {
                    currentStreak += 1;
                } else {
                    longestStreak = Math.max(longestStreak, currentStreak);
                    currentStreak = 1;
                }
            }
        }
        return Math.max(longestStreak, currentStreak);
    }
}


136. 只出现一次的数字

异或XOR:a ^ 0 = a; a ^ a = 0; 异或满足交换律和结合律

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。

说明: 

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

示例:
输入: [2,2,1]
输出: 1

class Solution {
    public int singleNumber(int[] nums) {
        int res = 0;
        for (int num : nums) {
            res ^= num;
        }
        return res;
    }
}


139. 单词拆分

动态规划,从左到右遍历s,s[0, i]有多种拆分方式,双指针进行拆分,若指针j前的子字符串s[0, j]可匹配,若s[j+1, i]也可找到匹配则认为拆分成功,一旦某种拆分方式成功则可认为dp[i]=T

给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 

说明: 

  • 拆分时可以重复使用字典中的单词。 
  • 你可以假设字典中没有重复的单词。

示例:
输入: s = "leetcode", wordDict = ["leet", "code"]
输出: true
解释: 返回 true 因为 "leetcode" 可以被拆分成 "leet code"

class Solution {
    public boolean wordBreak(String s, List<String> wordDict) {
        Set<String> set = new HashSet(wordDict);
        boolean[] dp = new boolean[s.length() + 1];
        dp[0] = true;
        for (int i = 1; i <= s.length(); i++) {
            for (int j = 0; j < i; j++) {
                if (dp[j] && set.contains(s.substring(j, i))) {
                    dp[i] = true;
                    break;
                }
            }
        }
        return dp[s.length()];
    }
}


141. 环形链表 ^_^

快慢指针,考虑head==null的特殊情况

给定一个链表,判断链表中是否有环。 

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。


/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null) return false;
        ListNode fast = head.next;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            if (fast == slow) return true;
            fast = fast.next.next;
            slow = slow.next;
        }
        return false;
    }
}