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”)。 问总共有多少条不同的路径?
说明:m和n的值均不超过 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 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
- 插入一个字符
- 删除一个字符
- 替换一个字符
输入: 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]
输出: 10class 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"]
]
输出: 6class 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 7class 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
输出: 42class 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]
输出: 1class 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;
}
}