力扣Hot100(目前进度89/100)
HOT100
简单题
HashMap
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++){
if (map.containsKey(nums[i])){
return new int[]{map.get(nums[i]), i};
} else {
map.put(target - nums[i], i);
}
}
return new int[2];
}
}
数组模拟栈
class Solution {
public boolean isValid(String s) {
int top = 1;
char[] stack = new char[s.length() + 1];
for (char c : s.toCharArray()){
if (c == '(' || c == '[' || c == '{'){
stack[top++] = c;
} else if (c == ')' && stack[--top] != '('){
return false;
} else if (c == ']' && stack[--top] != '['){
return false;
} else if (c == '}' && stack[--top] != '{'){
return false;
}
}
return top == 1;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode pre = res;
while (l1 != null && l2 != null){
if (l1.val <= l2.val){
//不额外申请新的ListNode,在原有链表基础上进行操作
ListNode p = l1;
l1 = l1.next;
p.next = null;
pre.next = p;
} else {
ListNode q = l2;
l2 = l2.next;
q.next = null;
pre.next = q;
}
pre = pre.next;
}
pre.next = l1 != null ? l1 : l2;
return res.next;
}
}
class Solution {
// public int maxSubArray(int[] nums) {
// int n = nums.length;
// //dp[i]代表以nums[i]结尾的连续子数组的最大和
// int[] dp = new int[n];
// int maxSum = nums[0];
// dp[0] = nums[0];
// for (int i = 1; i < n; i++){
// dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
// maxSum = Math.max(maxSum, dp[i]);
// }
// return maxSum;
// }
//状态压缩
public int maxSubArray(int[] nums) {
int n = nums.length;
int dp_0 = nums[0], dp_1 = 0;
int maxSum = dp_0;
for (int i = 1; i < n; i++){
dp_1 = Math.max(dp_0 + nums[i], nums[i]);
dp_0 = dp_1;
maxSum = Math.max(maxSum, dp_1);
}
return maxSum;
}
}
class Solution {
// public int climbStairs(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++){
// dp[i] = dp[i - 1] + dp[i - 2];
// }
// return dp[n];
// }
//状态压缩
public int climbStairs(int n) {
if (n <= 2) return n;
int dp_0 = 1, dp_1 = 1;
for (int i = 2; i <= n; i++){
int sum = dp_1 + dp_0;
dp_0 = dp_1;
dp_1 = sum;
}
return dp_1;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null) return true;
return dfs(root.left, root.right);
}
public boolean dfs(TreeNode p, TreeNode q){
//都为null,树仅有一个根节点
if (p == null && q == null) return true;
//仅有一个为空(由上面的情况排除都为空)
if (p == null || q == null) return false;
if (p.val != q.val) return false;
return dfs(p.left, q.right) && dfs(p.right, q.left);
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
}
}
class Solution {
// public int maxProfit(int[] prices) {
// //dp[i][0]代表第i天手中未持股时的最大利润,dp[i][1]代表持股的
// int n = prices.length;
// int[][] dp = new int[n][2];
// dp[0][0] = 0;
// dp[0][1] = -prices[0];
// for (int i = 1; i < n; i++){
// dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
// //只交易一次,之前未持股,说明还未交易,所以dp[i - 1][0]必等于0
// dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
// }
// return dp[n - 1][0];
// }
//状态压缩
public int maxProfit(int[] prices) {
int n = prices.length;
int dp_0 = 0, dp_1 = -prices[0];
for (int i = 1; i < n; i++){
dp_0 = Math.max(dp_0, dp_1 + prices[i]);
dp_1 = Math.max(dp_1, -prices[i]);
}
return dp_0;
}
}
巧用异或运算
—— 所有出现次数为两次的元素做异或结果为0,和0异或结果为本身
class Solution {
public int singleNumber(int[] nums) {
int sum = 0;
for (int num : nums){
sum ^= num;
}
return sum;
}
}
/**
* 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) {
ListNode fastPtr = head, slowPtr = head;
while (fastPtr != null && fastPtr.next != null){
fastPtr = fastPtr.next.next;
slowPtr = slowPtr.next;
if (fastPtr == slowPtr) return true;
}
return false;
}
}
class MinStack {
Deque<Integer> stk1;
Deque<Integer> stk2;
/** initialize your data structure here. */
public MinStack() {
stk1 = new ArrayDeque<>();
stk2 = new ArrayDeque<>();
//防止stk2.peek()出现空指针异常
stk2.push(Integer.MAX_VALUE);
}
public void push(int val) {
stk1.push(val);
stk2.push(Math.min(stk2.peek(), val));
}
public void pop() {
if (!stk1.isEmpty() && !stk2.isEmpty()){
stk1.pop();
stk2.pop();
} else {
return;
}
}
public int top() {
return stk1.peek();
}
public int getMin() {
return stk2.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p = headA, q = headB;
while (p != q){
p = p == null ? headB : p.next;
q = q == null ? headA : q.next;
}
return p;
}
}
投票法
—— 投票抵消
class Solution {
public int majorityElement(int[] nums) {
int candidate = nums[0];
int vote = 1;
for (int num : nums){
if (num == candidate){
vote++;
} else if (--vote == 0) {
candidate = num;
vote = 1;
}
}
return candidate;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
ListNode res = new ListNode(0);
res.next = head;
ListNode pre = res, p = head;
while (p != null && p.next != null){
ListNode temp = pre.next;
pre.next = p.next;
p.next = p.next.next;
pre.next.next = temp;
}
return res.next;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
Deque<Integer> stk = new ArrayDeque<>();
ListNode fastPtr = head, slowPtr = head;
//快指针走到最后一个节点或者空节点时,慢指针正好走到链表的中间节点
while (fastPtr != null && fastPtr.next != null){
stk.push(slowPtr.val);
fastPtr = fastPtr.next.next;
slowPtr = slowPtr.next;
}
//奇长链表
if (fastPtr != null){
stk.push(slowPtr.val);
}
//此时slowptr指向链表的中间节点
while (slowPtr != null && !stk.isEmpty()){
if (slowPtr.val != stk.pop()) return false;
slowPtr = slowPtr.next;
}
return true;
}
}
class Solution {
public void moveZeroes(int[] nums) {
int n = nums.length;
int nonZeroCnt = 0;
for (int num : nums){
if (num != 0){
nums[nonZeroCnt++] = num;
}
}
//零填充
Arrays.fill(nums, nonZeroCnt, n, 0);
}
}
class Solution {
public List<Integer> findDisappearedNumbers(int[] nums) {
List<Integer> res = new ArrayList<>();
int i = 0;
while (i < nums.length){
//当前数字应该在的位置上是该数字,则不交换
int idx = nums[i];
if (nums[i] == nums[idx - 1]) {
i++;
continue;
}
//若当前数字应该在的位置上不是该数字,则交换
int temp = nums[i];
nums[i] = nums[temp - 1];
nums[temp - 1] = temp;
}
for (int k = 0; k < nums.length; k++){
if (nums[k] != k + 1){
res.add(k + 1);
}
}
return res;
}
}
class Solution {
public int hammingDistance(int x, int y) {
int distance = 0;
//汉明距离==异或和中1的个数
int sum = x ^ y;
while (sum != 0){
distance++;
//消除最右边的1
sum &= (sum - 1);
}
return distance;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
getHeight(root);
return res;
}
public int getHeight(TreeNode node){
if (node == null) return 0;
int lHeight = getHeight(node.left);
int rHeight = getHeight(node.right);
//更新最大直径
res = Math.max(res, lHeight + rHeight);
//返回当前节点的最大高度
return 1 + Math.max(lHeight, rHeight);
}
}
class Solution {
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
// 如果root1和root2中,只要有一个是null,函数就直接返回
if(root1 == null || root2 == null) {
return root1 == null ? root2 : root1;
}
//让root1的值等于root1和root2的值累加,再递归地计算两颗树的左节点、右节点
//把第二棵树直接加到第一棵树上
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
}
中等题
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode res = new ListNode(0);
ListNode pre = res;
int add = 0;
while (l1 != null || l2 != null){
int sum = add;
if (l1 != null) {
sum += l1.val;
l1 = l1.next;
}
if (l2 != null) {
sum += l2.val;
l2 = l2.next;
}
pre.next = new ListNode(sum % 10);
add = sum / 10;
pre = pre.next;
}
if (add != 0){
pre.next = new ListNode(add);
}
return res.next;
}
}
滑动窗口
class Solution {
public int lengthOfLongestSubstring(String s) {
if (s.length() <= 1) return s.length();
HashMap<Character, Integer> map = new HashMap<>();
int left = 0, right = 0, maxLen = Integer.MIN_VALUE;
while (right < s.length()){
char c = s.charAt(right);
map.put(c, map.getOrDefault(c, 0) + 1);
//收缩窗口
while (map.get(c) > 1){
char d = s.charAt(left);
left++;
map.put(d, map.get(d) - 1);
}
//更新窗口最大长度
maxLen = Math.max(maxLen, right - left + 1);
right++;
}
return maxLen;
}
}
动态规划
class Solution {
public String longestPalindrome(String s) {
int n = s.length();
if (n <= 1) return s;
//dp[i][j]指示从chars[i]到chars[j]这段子串是否是回文的
char[] chars = s.toCharArray();
boolean [][] dp = new boolean[n][n];
//记录最大回文子串的起止位置
int begin = 0, end = 0;
//动态规划
for (int j = 0; j < n; j++){
dp[j][j] = true;
for (int i = 0; i < j; i++){
//子串长为2
if (i == j - 1){
//回文取决于这两个字符是否相等
dp[i][j] = chars[i] == chars[j];
} else {
//回文取决于dp[i + 1][j - 1]以及最外侧两侧的字符是否相等
dp[i][j] = dp[i + 1][j - 1] && chars[i] == chars[j];
}
//更新最大回文子串的起止位置
if (dp[i][j] && j - i > end - begin){
begin = i;
end = j;
}
}
}
return s.substring(begin, end + 1);
}
}
双指针
class Solution {
public int maxArea(int[] height) {
int n = height.length;
int maxVolume = 0;
int leftPtr = 0, rightPtr = n - 1;
while (leftPtr < rightPtr){
int h = Math.min(height[leftPtr], height[rightPtr]);
maxVolume = Math.max(maxVolume, h * (rightPtr - leftPtr));
//由于移动指针导致宽的减小,必须把指针移向更高处才能使容量增大
while (height[leftPtr] <= h && leftPtr < rightPtr) leftPtr++;
while (height[rightPtr] <= h && leftPtr < rightPtr) rightPtr--;
}
return maxVolume;
}
}
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
List<List<Integer>> res = new ArrayList<>();
Arrays.sort(nums);
for (int k = 0; k < n - 2; k++){
//nums[j] > nums[i] > nums[k] > 0, nums[k] + nums[i] + nums[j] = 0无解
if (nums[k] > 0) break;
//nums[k] == nums[k - 1](已经把nums[k - 1]的全部组合解考虑完了,因此可以跳过重复解)
if (k > 0 && nums[k] == nums[k - 1]) {
continue;
}
int i = k + 1, j = n - 1;
while (i < j){
int sum = nums[k] + nums[i] + nums[j];
if (sum < 0) {
//指针i右移,增大sum进行搜索,对于相同的组合,跳过就行
while (i < j && nums[i] == nums[++i]);
} else if(sum > 0){
//指针j左移,减小sum进行搜索,对于相同的组合,跳过就行
while (i < j && nums[j] == nums[--j]);
} else {
res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
//还是要跳过重复的解
while (i < j && nums[i] == nums[++i]);
while (i < j && nums[j] == nums[--j]);
}
}
}
return res;
}
}
class Solution {
private static String[] map = new String[]{"abc", "def", "ghi", "jkl", "mno","pqrs", "tuv", "wxyz"};
public List<String> letterCombinations(String digits) {
List<String> res = new ArrayList<>();
if (digits.length() == 0) return res;
helper(res, digits, "");
return res;
}
public void helper(List<String> res, String digits, String ans){
if (ans.length() == digits.length()){
res.add(ans);
return;
}
for (char c : map[digits.charAt(ans.length()) - '2'].toCharArray()){
helper(res, digits, ans + c);
}
}
}
双指针
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode res = new ListNode(0);
res.next = head;
ListNode p = res, q = res;
while (q.next != null){
//让q多走n步,等到q走到链表的末尾,此时p也走到了要删除节点的前一个节点处
if (n > 0){
q = q.next;
n--;
continue;
}
p = p.next;
q = q.next;
}
//删除节点
p.next = p.next.next;
//返回头节点
return res.next;
}
}
回溯
class Solution {
private List<String> res = new ArrayList<>();
public List<String> generateParenthesis(int n) {
String cur = "";
backTrack(cur, n, n);
return res;
}
//left right,左右括号的剩余量
public void backTrack(String s, int left, int right){
//终止条件
if (left == 0 && right == 0){
res.add(s);
return;
}
//做选择,还有左括号,就可以把左括号加到s中
if (left > 0) {
backTrack(s + "(", left - 1, right);
}
//当前右括号剩余个数多于左括号(右括号的添加必须在左括号之后),才可以把右括号加到s中
if (right > left) {
backTrack(s + ")", left, right - 1);
}
}
}
class Solution {
public void nextPermutation(int[] nums) {
int n = nums.length;
int i = n - 2;
//从后往前找第一个比其右邻居小的元素
while (i >= 0 && nums[i] >= nums[i + 1]){
i--;
}
//找到后,从最后一个元素开始,找第一个比nums[i]大的元素
if (i >= 0){
int j = n - 1;
while (j >= 0 && nums[j] <= nums[i]){
j--;
}
swap(nums, i, j);
}
//把nums[i]后面的元素升序排序,这样保证增幅最小,也即得到下一个更大的排列
Arrays.sort(nums, i + 1, n);
}
public void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
二分
class Solution {
public int search(int[] nums, int target) {
int n = nums.length;
int left = 0, right = n - 1;
while (left <= right){
int mid = (right - left) / 2 + left;
if (nums[mid] == target) return mid;
//nums[mid]在左半部分
if (nums[mid] >= nums[left]){
//target的值也在左半部分之间
if (nums[mid] > target && nums[left] <= target){
right = mid - 1;
} else {
left = mid + 1;
}
} else {
if (nums[mid] < target && target <= nums[right]){
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1;
}
}
二分
—— 边界搜索
class Solution {
public int[] searchRange(int[] nums, int target) {
int res1 = getLeftBorder(nums, target);
int res2 = getRightBorder(nums, target);
return new int[]{res1, res2};
}
//搜索左侧边界
public int getLeftBorder(int[] nums, int target){
int n = nums.length;
int left = 0, right = n - 1;
while (left <= right){
int mid = (left + right) / 2;
if (nums[mid] >= target){
right = mid - 1;
} else {
left = mid + 1;
}
}
//不存在目标值的情况
if (left >= n || nums[left] != target){
return -1;
}
return left;
}
//搜索右侧边界
public int getRightBorder(int[] nums, int target){
int n = nums.length;
int left = 0, right = n - 1;
while (left <= right){
int mid = (left + right) / 2;
if (nums[mid] > target){
right = mid - 1;
} else {
left = mid + 1;
}
}
//不存在目标值的情况
if (right < 0 || nums[right] != target){
return -1;
}
return right;
}
}
回溯
—— 题目给的数组是无重复元素的,所以只要从前往后遍历就不会有重复解。
class Solution {
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) return res;
backTrack(candidates, new ArrayList<Integer>(), 0, target);
return res;
}
public void backTrack(int[] nums, List<Integer> temp, int index, int k){
//终止条件
if (k < 0) return;
if (k == 0){
res.add(new ArrayList<>(temp));
return;
}
//遍历选择列表,分层进行选择
for (int i = index; i < nums.length; i++){
//累积和还没达到target,才需要继续组合
if (k > 0){
//做选择
temp.add(nums[i]);
//回溯,i不自增的原因是,candidates中的元素可以无限制重复被选取
backTrack(nums, temp, i, k - nums[i]);
//撤销选择
temp.remove(temp.size() - 1);
}
}
}
}
常规解法
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
int[] visited = new int[nums.length];
backtrack(res, nums, new ArrayList<Integer>(), visited);
return res;
}
private void backtrack(List<List<Integer>> res, int[] nums, ArrayList<Integer> tmp, int[] visited) {
if (tmp.size() == nums.length) {
res.add(new ArrayList<>(tmp));
return;
}
for (int i = 0; i < nums.length; i++) {
if (visited[i] == 1) continue;
visited[i] = 1;
tmp.add(nums[i]);
backtrack(res, nums, tmp, visited);
visited[i] = 0;
tmp.remove(tmp.size() - 1);
}
}
}
交换的思想
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> permuteList = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
for (int i = 0; i < nums.length; i++){
permuteList.add(nums[i]);
}
backTrack(0, nums.length);
return res;
}
public void backTrack(int begin, int last){
//终止条件
if (begin == last){
res.add(new ArrayList<>(permuteList));
}
for (int i = begin; i < last; i++){
//做选择,即交换
//Collections.swap(permuteList, begin, i);
swap(begin, i);
//继续回溯
backTrack(begin + 1, last);
//撤销选择,换回来
//Collections.swap(permuteList, begin, i);
swap(begin, i);
}
}
//交换
public void swap(int j, int k){
int temp = permuteList.get(j);
permuteList.set(j, permuteList.get(k));
permuteList.set(k, temp);
}
}
class Solution {
public void rotate(int[][] matrix) {
int m = matrix.length, n = matrix[0].length;
//只需要一半进行顺时针轮转交换
for (int i = 0; i < (n + 1) / 2; i++){
for (int j = 0; j < n / 2; j++){
// temp = 7
int temp = matrix[n - 1 - j][i];
// 9 -> 7(同一行)
matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
// 3 -> 9(同一列))
matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i];
// 1 -> 3(同一行)
matrix[j][n - 1 - i] = matrix[i][j];
// temp = 7 -> 1
matrix[i][j] = temp;
}
}
}
}
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String, List> map = new HashMap<>();
for (String s : strs){
char[] chars = s.toCharArray();
Arrays.sort(chars);
String sorted = String.valueOf(chars);
if (!map.containsKey(sorted)){
map.put(sorted, new ArrayList<String>());
}
map.get(sorted).add(s);
}
return new ArrayList(map.values());
}
}
贪心
class Solution {
public boolean canJump(int[] nums) {
int farthest = nums[0];
for (int i = 0; i <= farthest; i++){
//更新最远能到达的位置
farthest = Math.max(i + nums[i], farthest);
if (farthest >= nums.length - 1) return true;
}
return false;
}
}
class Solution {
public int[][] merge(int[][] intervals) {
int n = intervals.length;
//只有一个区间
if (n == 1) return intervals;
//将区间排序
Arrays.sort(intervals, (a, b) -> {
//区间起点相同,按区间长度由大到小排序
if (a[0] == b[0]){
return b[1] - a[1];
}
//按起始位置从小到大排序
return a[0] - b[0];
});
int cnt = 0;
int[][] res = new int[n][2];
int start = intervals[0][0], end = intervals[0][1];
for (int i = 1; i < n; i++){
//有区间交叉,合并它们
if (end >= intervals[i][0] && end <= intervals[i][1]){
end = intervals[i][1];
}
//存区间
res[cnt][0] = start;
res[cnt][1] = end;
//没有区间交叉
if (end < intervals[i][0]){
//产生新区间
cnt++;
//新区间的起止位置
start = intervals[i][0];
end = intervals[i][1];
//对最后一个区间还未做处理
if (end == intervals[n - 1][1]){
res[cnt][0] = start;
res[cnt][1] = end;
}
}
}
//返回数组的前cnt个处理好的合并区间
return Arrays.copyOf(res, cnt + 1);
}
}
动态规划
class Solution {
public int uniquePaths(int m, int n) {
//dp[i][j]代表从(0,0)到(i,j)的方法数
int[][] dp = new int[m][n];
//base case
for (int i = 0; i < m; i++){
dp[i][0] = 1;
}
for (int j = 0; j < n; j++){
dp[0][j] = 1;
}
for (int i = 1; i < m; i++){
for (int j = 1; j < n; j++){
//位置(i, j)只能由上方位置(i - 1, j)和左方位置(i, j - 1)到达
dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
}
}
return dp[m - 1][n - 1];
}
}
动态规划
class Solution {
public int minPathSum(int[][] grid) {
//dp[i][j]代表(0, 0)到(i, j)的最短路径
int m = grid.length, n = grid[0].length;
int[][] dp = new int[m][n];
dp[0][0] = grid[0][0];
for (int i = 1; i < m; i++){
dp[i][0] = dp[i - 1][0] + grid[i][0];
}
for (int j = 1; j < n; j++){
dp[0][j] = dp[0][j - 1] + grid[0][j];
}
for (int i = 1; i < m; i++){
for (int j = 1; j < n; j++){
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[m - 1][n - 1];
}
}
要求原地方法
——空O(1)
class Solution {
public void sortColors(int[] nums) {
int n0 = 0, n1 = 0;
for (int i = 0; i < nums.length; i++){
//暂存原位置的值
int num = nums[i];
//第一遍刷漆,刷上2
nums[i] = 2;
//第二遍刷漆,把2覆盖
if (num < 2) {
nums[n1++] = 1;
}
//第三遍刷漆,把1覆盖,上一次刷漆相当于只进行了n1++
if (num < 1) {
nums[n0++] = 0;
}
}
}
}
回溯
class Solution {
private List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
int n = nums.length;
//
for (int k = 0; k < n + 1; k++){
backTrack(new ArrayList<Integer>(), nums, 0, k);
}
return res;
}
//回溯
public void backTrack(List<Integer> cur, int[] arr, int idx, int len){
//终止条件
if (cur.size() == len){
res.add(new ArrayList<Integer>(cur));
return;
}
for (int i = idx; i < arr.length; i++){
//做选择
cur.add(arr[i]);
//下一层
backTrack(cur, arr, i + 1, len);
//撤销选择
cur.remove(cur.size() - 1);
}
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private List<Integer> res = new ArrayList<>();
public List<Integer> inorderTraversal(TreeNode root) {
inorder(root);
return res;
}
public void inorder(TreeNode node){
if (node == null) return;
inorder(node.left);
res.add(node.val);
inorder(node.right);
}
}
动态规划
class Solution {
public int numTrees(int n) {
if (n <= 2) return n;
//dp[i]代表节点个数为i的二叉搜索树的种数
int[] dp = new int[n + 1];
//base case
dp[0] = 1;
dp[1] = 1;
dp[2] = 2;
/**
根结点不同,可能有n种情况,以1为根结点,以2为根结点…类推到以n为根结点,共有n种情况.
区分了根节点不同后,剩下的就是左右子树不同了,而左右子树的不同二叉树则是一个相同的问题
比如收当前有n个结点,以第k个元素作为根结点,那么左子树就会有k-1个元素,它是一个规模为k-1的子问题
而右子树有n-k个结点,它也是一个规模为n-k的子问题
*/
for (int i = 3; i <= n; i++){
for (int j = 1; j <= i; j++) {
dp[i] += dp[j - 1] * dp[i - j];
}
}
return dp[n];
}
}
中序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
private 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
pre = root.val;
//再检查右子树是否满足二叉搜索树的性质
return isValidBST(root.right);
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if (root == null) return res;
Deque<TreeNode> q = new ArrayDeque<>();
q.offer(root);
while (!q.isEmpty()){
int size = q.size();
List<Integer> temp = new ArrayList<>();
for (int i = 0; i < size; i++){
TreeNode cur = q.poll();
temp.add(cur.val);
if (cur.left != null) q.offer(cur.left);
if (cur.right != null) q.offer(cur.right);
}
res.add(temp);
}
return res;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return helper(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
}
public TreeNode helper(int[] preorder, int preBegin, int preEnd, int[] inorder, int inBegin, int inEnd){
if (preBegin > preEnd) return null;
//前序序列用作确定根节点
int rootVal = preorder[preBegin];
//确定当前根节点在中序序列中的下标,用于分隔左右子树
int idx = 0;
for (int i = 0; i < inorder.length; i++){
if (rootVal == inorder[i]){
idx = i;
break;
}
}
//创建root
TreeNode root = new TreeNode(rootVal);
//中序遍历序列用于确定左右子树节点个数
//左子树节点个数
int leftSize = idx - inBegin;
//递归创建左右子树
root.left = helper(preorder, preBegin + 1, preBegin + leftSize, inorder, inBegin, idx - 1);
root.right = helper(preorder, preBegin + leftSize + 1, preEnd, inorder, idx + 1, inEnd);
return root;
}
}
链表需要满足先序顺序 + left置空 + right作链接指针
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public void flatten(TreeNode root) {
if (root == null) return;
//先递归把左右子树拉平
flatten(root.left);
flatten(root.right);
//暂存左右子树的根节点
TreeNode left = root.left;
TreeNode right = root.right;
//将root的左指针置空
root.left = null;
//root右指针指向原来的左孩子
root.right = left;
//迭代到叶子节点
TreeNode p = root;
while (p.right != null){
p = p.right;
}
//把右子树接上
p.right = right;
}
}
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fastPtr = head, slowPtr = head;
while (fastPtr != null && fastPtr.next != null){
fastPtr = fastPtr.next.next;
slowPtr = slowPtr.next;
//快慢指针第一次相遇
if (fastPtr == slowPtr) break;
}
//无环的情况
if (fastPtr == null || fastPtr.next == null){
return null;
}
slowPtr = head;
while (slowPtr != fastPtr){
fastPtr = fastPtr.next;
slowPtr = slowPtr.next;
}
return slowPtr;
}
}
class LRUCache {
private int capacity = 0;
private Map<Integer, Integer> map = new LinkedHashMap<>();
public LRUCache(int capacity) {
this.capacity = capacity;
}
public int get(int key) {
if (map.containsKey(key)){
int val = map.get(key);
//先移除再添加,保证每次查询后的key都在双向链表的末尾
map.remove(key);
map.put(key, val);
return val;
} else {
return -1;
}
}
//put操作也会访问key,所以也要先移除再更新
public void put(int key, int value) {
if (map.containsKey(key)) {
map.remove(key);
} else if (map.size() == capacity) {
//移除链表头节点,即最近最久未使用的节点
int oldestKey = map.keySet().iterator().next();
map.remove(oldestKey);
}
//更新map中键key对应的value
map.put(key, value);
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
归并排序
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
//归并排序O(NlogN)
public ListNode sortList(ListNode head) {
//归并排序
if (head == null || head.next == null) return head;
//快慢指针找中间节点
ListNode slowPtr = head, fastPtr = head.next;
while (fastPtr != null && fastPtr.next != null){
fastPtr = fastPtr.next.next;
slowPtr = slowPtr.next;
}
//记录后半部分链表的表头指针
ListNode lastHalf = slowPtr.next;
//将slowPtr所在位置后面的链表全部断开
slowPtr.next = null;
//递归
ListNode leftHalf = sortList(head);
ListNode rightHalf = sortList(lastHalf);
return merge(leftHalf, rightHalf);
}
public ListNode merge(ListNode p, ListNode q){
ListNode res = new ListNode(0);
ListNode pre = res;
while (p != null && q != null){
if (p.val <= q.val){
pre.next = p;
p = p.next;
} else {
pre.next = q;
q = q.next;
}
pre = pre.next;
}
pre.next = p != null ? p : q;
return res.next;
}
}
动态规划
class Solution {
public int maxProduct(int[] nums) {
int n = nums.length;
int maxRes = Integer.MIN_VALUE;
int imax = 1, imin = 1;
for (int i = 0; i < n; i++){
// nums[i] < 0,交换imax和imin
if (nums[i] < 0){
int temp = imax;
imax = imin;
imin = temp;
}
// 维护的最小值乘完nums[i]变成最大值,而最大值乘完nums[i]变成最小值
imax = Math.max(imax * nums[i], nums[i]);
imin = Math.min(imin * nums[i], nums[i]);
maxRes = Math.max(maxRes, imax);
}
return maxRes;
}
}
动态规划
class Solution {
public int rob(int[] nums) {
// dp[i]表示从第i家开始偷窃能获得的最高金额
int n = nums.length;
int[] dp = new int[n + 2];
// 由后往前遍历
for (int i = n - 1; i >= 0; i--){
dp[i] = Math.max(dp[i + 1], dp[i + 2] + nums[i]);
}
return dp[0];
}
}
回溯
class Solution {
public int numIslands(char[][] grid) {
int m = grid.length, n = grid[0].length;
int islands = 0;
for (int i = 0; i < m; i++){
for (int j = 0; j < n; j++){
//陆地
if (grid[i][j] == '1'){
islands++;
//深度优先搜索,把和(i, j)所在岛相连的陆地的都变为0
dfs(grid, i , j);
}
}
}
return islands;
}
public void dfs(char[][] arr, int i, int j){
//停止搜索的条件
if (arr[i][j] == '0') return;
//置“0”操作
arr[i][j] = '0';
//向上搜索
if (i - 1 >= 0) dfs(arr, i - 1, j);
//向下搜索
if (i + 1 <= arr.length - 1) dfs(arr, i + 1, j);
//向左搜索
if (j - 1 >= 0) dfs(arr, i, j - 1);
//向右搜索
if (j + 1 <= arr[0].length - 1) dfs(arr, i, j + 1);
}
}
优先权队列
class Solution {
int res = 0;
public int findKthLargest(int[] nums, int k) {
int n = nums.length;
if (k > n / 2){
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(n, (a, b) -> b - a);
getRes(maxHeap, nums, k);
} else {
PriorityQueue<Integer> minHeap = new PriorityQueue<>(n);
getRes(minHeap, nums, n - k + 1);
}
return res;
}
//从最大堆或者最小堆中获取结果
public void getRes(PriorityQueue<Integer> heap, int[] nums, int cnt){
for (int num : nums){
heap.offer(num);
}
while (cnt-- > 0){
res = heap.poll();
}
}
}
class Solution {
public int maximalSquare(char[][] matrix) {
int m = matrix.length, n = matrix[0].length;
// dp[i + 1][j + 1]代表以(i, j)为右下角元素的方阵的最大维度
int maxD = Integer.MIN_VALUE;
int[][] dp = new int[m + 1][n + 1];
for (int i = 1; i <= m ; i++){
for (int j = 1; j <= n; j++){
if (matrix[i - 1][j - 1] == '1'){
//状态转移,维度受限于左边、左对角线、正上方的维度(取三者中最小再加1)
dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i - 1][j], dp[i][j - 1])) + 1;
}
maxD = Math.max(maxD, dp[i][j]);
}
}
return maxD * maxD;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
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 ? left : right;
}
}
前缀数组/后缀数组
class Solution {
public int[] productExceptSelf(int[] nums) {
int n = nums.length;
int[] preProduct = new int[n];
int[] postProduct = new int[n];
preProduct[0] = 1;
postProduct[n - 1] = 1;
//前面的积
for (int i = 1; i < n; i++){
preProduct[i] = preProduct[i - 1] * nums[i - 1];
}
//后面的积
for (int j = n - 2; j >= 0; j--){
postProduct[j] = postProduct[j + 1] * nums[j + 1];
}
for (int k = 0; k < n; k++){
nums[k] = preProduct[k] * postProduct[k];
}
return nums;
}
}
双指针
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int left = 0, right = n - 1;
while (left <= m - 1 && right >= 0){
if (matrix[left][right] == target){
return true;
} else if (matrix[left][right] < target){
left++;
} else {
right--;
}
}
return false;
}
}
class Solution {
public int numSquares(int n) {
// dp[i] —— 和为i的完全平方数的最少数目
// dp[i] = Math.min(dp[i - 1], dp[i - 4], ...,) + 1
int[] dp = new int[n + 1];
Arrays.fill(dp, Integer.MAX_VALUE);
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++){
int ps = 1;
while (ps * ps <= i){
dp[i] = Math.min(dp[i], dp[i - ps * ps]);
ps++;
}
// 加1
dp[i]++;
}
return dp[n];
}
}
二分
class Solution {
public int findDuplicate(int[] nums) {
// //HashSet
// int n = nums.length;
// HashSet<Integer> set = new HashSet<>();
// for (int num : nums){
// if (set.contains(num)){
// return num;
// } else {
// set.add(num);
// }
// }
// return -1;
//二分法
int n = nums.length;
int left = 1, right = n;
while (left < right){
int mid = (right - left) / 2 + left;
int cnt = 0;
// 统计小于mid的数有多少个
for (int num : nums){
if (num <= mid){
cnt++;
}
}
//小于等于mid的数的个数大于mid,说明重复数在[left,mid]之间
if (cnt > mid){
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
动态规划
class Solution {
public int lengthOfLIS(int[] nums) {
// dp[i] —— 以nums[i]结尾的严格递增子序列的最大长度
int n = nums.length;
int[] dp = new int[n];
Arrays.fill(dp, 1);
int maxLen = Integer.MIN_VALUE;
for (int i = 0; i < n; i++){
for (int j = 0; j < i; j++){
if (nums[j] < nums[i]){
// 状态更新
dp[i] = Math.max(dp[i], dp[j] + 1);
}
}
maxLen = Math.max(maxLen, dp[i]);
}
return maxLen;
}
}
动态规划
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
if (n == 0) return 0;
//base case
int dp_0 = 0, dp_1 = Integer.MIN_VALUE;
int dp_2 = 0;
//state update
for (int i = 0; i < n; i++){
int temp = dp_0;
dp_0 = Math.max(dp_0, dp_1 + prices[i]);
// 昨天就有或者是前天买入的
dp_1 = Math.max(dp_1, dp_2 - prices[i]);
dp_2 = temp;
}
return dp_0;
}
}
动态规划
class Solution {
public int coinChange(int[] coins, int amount) {
// dp[amount]代表凑齐总金额amount需要的最小硬币个数
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
// base case
dp[0] = 0;
for (int i = 0; i <= amount; i++){
for (int coin : coins){
// sub problem has no solution
if (i - coin < 0) continue;
//state update
dp[i] = Math.min(dp[i], 1 + dp[i - coin]);
}
}
// 凑齐金额amount至多需要amount枚硬币
return dp[amount] == amount + 1 ? -1 : dp[amount];
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int rob(TreeNode root) {
int[] res = dfs(root);
return res[0] > res[1] ? res[0] : res[1];
}
public int[] dfs(TreeNode node){
if (node == null) {
return new int[]{0, 0};
}
int[] left = dfs(node.left);
int[] right = dfs(node.right);
//当前节点抢,则左右孩子节点肯定不能抢
int rob = node.val + left[1] + right[1];
// 当前节点不抢,则左右孩子节点看情况(按收益最大)选择抢或不抢
int noRob = (left[0] > left[1] ? left[0] : left[1])
+(right[0] > right[1] ? right[0] : right[1]);
return new int[]{rob, noRob};
}
}
位运算 —— 与&
class Solution {
public int[] countBits(int num) {
int[] res = new int[num + 1];
for (int i = 0; i <= num; i++){
res[i] = getOneCnt(i);
}
return res;
}
public int getOneCnt(int n){
int cnt = 0;
while (n != 0){
n &= (n - 1);
cnt++;
}
return cnt;
}
}
HashMap + PriorityQueue
class Solution {
public int[] topKFrequent(int[] nums, int k) {
int[] res = new int[k];
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
PriorityQueue<Map.Entry<Integer, Integer>> pq = new PriorityQueue<>((a, b) -> (b.getValue() - a.getValue()));
map.entrySet().forEach((entry) -> pq.add(entry));
int temp = k;
while (k-- > 0 && !pq.isEmpty()){
res[temp - k - 1] = pq.poll().getKey();
}
return res;
}
}
辅助栈
class Solution {
public String decodeString(String s) {
int n = s.length();
Deque<Integer> stkDigit = new ArrayDeque<>();
Deque<String> stkStr = new ArrayDeque<>();
StringBuilder res = new StringBuilder();
int repeatedTimes = 0;
for (char c : s.toCharArray()){
if (Character.isDigit(c)){
repeatedTimes = repeatedTimes * 10 + Integer.parseInt(c + "");
} else if (c == '['){
stkDigit.push(repeatedTimes);
stkStr.push(res.toString());
repeatedTimes = 0;
res = new StringBuilder();
} else if (c == ']'){
StringBuilder temp = new StringBuilder();
int times = stkDigit.pop();
while(times-- > 0) temp.append(res);
res = new StringBuilder(stkStr.pop() + temp);
} else {
res.append(c);
}
}
return res.toString();
}
}
贪心
—— 个子低的对个子高的之间的相对站位没有影响,可以先按身高降序排好(相同身高按k升序排列),再从前往后把身高为hk的人插到队列的第k个位置上
class Solution {
public int[][] reconstructQueue(int[][] people) {
int m = people.length;
int[][] res = new int[m][2];
Arrays.sort(people, (o1, o2) -> o1[0] == o2[0] ? o1[1] - o2[1] : o2[0] - o1[0]);
LinkedList<int[]> list = new LinkedList<>();
for(int[] pp : people){
list.add(pp[1], pp);
}
return list.toArray(new int[m][2]);
}
}
动态规划
class Solution {
public boolean canPartition(int[] nums) {
int sum = Arrays.stream(nums).sum();
if (sum % 2 != 0) return false;
int target = sum / 2;
//dp[i]代表和为i的子集是否存在
boolean[] dp = new boolean[target + 1];
Arrays.fill(dp, false);
dp[0] = true;
for (int i = 0; i < nums.length; i++){
for (int j = target; j >=0; j--){
if (j >= nums[i]){
dp[j] = dp[j] || dp[j - nums[i]];
}
}
}
return dp[target];
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) return 0;
// 递归,对树中每个节点都做dfs(只能由父到子)
return dfs(root, targetSum) + pathSum(root.left, targetSum) + pathSum(root.right, targetSum);
}
public int dfs(TreeNode cur, int target) {
int cnt = 0;
if (cur == null) return 0;
//统计符合路径和的路径条数
if (target == cur.val) {
cnt++;
}
cnt += dfs(cur.left, target - cur.val);
cnt += dfs(cur.right, target - cur.val);
return cnt;
}
}
滑动窗口
class Solution {
public List<Integer> findAnagrams(String s, String p) {
List<Integer> res = new ArrayList<>();
if (s.length() < p.length()) return res;
int[] counts = new int[26];
int[] window = new int[26];
// 统计字符串p中所有字母出现的次数
for (char c : p.toCharArray()){
counts[c - 'a']++;
}
int left = 0, right = 0, cnt = 0;
while (right < s.length()){
int curRight = s.charAt(right) - 'a';
window[curRight]++;
while (window[curRight] > counts[curRight]){
int curLeft = s.charAt(left) - 'a';
window[curLeft]--;
left++;
}
if (right - left + 1 == p.length()){
res.add(left);
}
right++;
}
return res;
}
}
回溯
class Solution {
int cnt = 0;
public int findTargetSumWays(int[] nums, int target) {
backTrack(nums, 0, target);
return cnt;
}
public void backTrack(int[] arr, int idx, int k){
// 终止条件
if (idx == arr.length){
if (k == 0){
cnt++;
}
return;
}
// 减去当前元素
k -= arr[idx];
backTrack(arr, idx + 1, k);
// 加回来(撤销选择)
k += arr[idx];
// 加上当前元素
k += arr[idx];
backTrack(arr, idx + 1, k);
k -= arr[idx];
}
}
转化为背包问题
class Solution {
/*
假设nums中,正数的和为x,负数的和为y,则S=x-y,sum(nums) = x + y,由此推出x = (S + sum) / 2,问题转化为在nums中寻找若干数使得和为(S+sum)/2,这时问题转化为了01背包问题。
dp[j] —— 填满容量为j的背包,有dp[j]种方法。
状态转移 —— dp[j] = dp[j] + dp[j - num]; 当前填满容量为j的包的方法数 = 之前填满容量为j的包的方法数 + 之前填满容量为j - num的包的方法数。也就是当前数num的加入,可以把之前和为j - num的方法数加入进来。
*/
public int findTargetSumWays(int[] nums, int S) {
int sum = 0;
for (int num : nums) sum += num;
//不可能有解的情况
//1. 数组中全部数字(正)的和小于S
//2. (sum + S) / 2 为奇数,此时x不是整数
if (sum < S || (sum + S) % 2 == 1){
return 0;
}
return subset(nums, (sum + S) / 2);
}
public int subset(int[] arr, int target){
int[] dp = new int[target + 1];
dp[0] = 1;
for (int i = 1; i <= arr.length; i++){
for (int j = target; j >= 0; j--){
if (j >= arr[i - 1]){
dp[j] = dp[j] + dp[j - arr[i - 1]];
} else {
dp[j] = dp[j];
}
}
}
return dp[target];
}
}
改造中序遍历
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int newVal = 0;
public TreeNode convertBST(TreeNode root) {
return modifiedInorder(root);
}
public TreeNode modifiedInorder(TreeNode node){
if (node == null) return null;
modifiedInorder(node.right);
newVal += node.val;
node.val = newVal;
modifiedInorder(node.left);
return node;
}
}
前缀和+HashMap
class Solution {
public int subarraySum(int[] nums, int k) {
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, 1);
int sum = 0, cnt = 0;
for (int num : nums){
sum += num;
if (map.containsKey(sum - k)){
cnt += map.get(sum - k);
}
map.put(sum, map.getOrDefault(sum, 0) + 1);
}
return cnt;
}
}
class Solution {
public int findUnsortedSubarray(int[] nums) {
int n = nums.length;
int[] ori = nums.clone();
int left = Integer.MAX_VALUE;
int right = Integer.MIN_VALUE;
Arrays.sort(nums);
for (int i = 0; i < n; i++){
if (nums[i] != ori[i]){
left = Math.min(left, i);
right = Math.max(right, i);
}
}
return right > left ? right - left + 1 : 0;
}
}
class Solution {
public int countSubstrings(String s) {
// dp[j][i]——子串s.substring(j, i + 1)的回文性
int n = s.length();
int cnt = 0;
boolean[][] dp = new boolean[n][n];
for (int i = 0; i < n; i++){
for (int j = 0; j <= i; j++){
// 长度为1
if (i == j) {
dp[j][i] = true;
} else if (j == i - 1){
// 长度为2
dp[j][i] = s.charAt(j) == s.charAt(i);
} else {
dp[j][i] = dp[j + 1][i - 1] && s.charAt(j) == s.charAt(i);
}
if (dp[j][i] == true) cnt++;
}
}
return cnt;
}
}
单调栈
class Solution {
public int[] dailyTemperatures(int[] temperatures) {
int n = temperatures.length;
Deque<Integer> stk = new ArrayDeque<>();
int[] res = new int[n];
for (int i = 0; i < n; i++){
// 维护一个单调递减的栈
while (!stk.isEmpty() && temperatures[i] > temperatures[stk.peek()]){
int pre = stk.pop();
res[pre] = i - pre;
}
stk.push(i);
}
return res;
}
}
困难题
优先权队列
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int tLen = nums1.length + nums2.length;
PriorityQueue<Integer> pq = new PriorityQueue<>(tLen);
int cnt = 0;
for (int num : nums1){
pq.offer(num);
}
for (int num : nums2){
pq.offer(num);
}
int k = 0;
while (k < tLen){
double tmp = pq.poll() * 1.0;
//长度为奇
if (tLen % 2 != 0){
if (k == tLen / 2) return tmp;
} else {
if (k == tLen / 2 - 1) return (tmp + pq.poll() * 1.0) / 2;
}
k++;
}
return -1.0;
}
}
递归
class Solution {
public boolean isMatch(String s, String p) {
if (p.equals("")) return s.equals("");
// 当前位置是否匹配
boolean firstMatch = !s.equals("") && (p.charAt(0) == s.charAt(0) || p.charAt(0) == '.');
// 处理"*"的匹配,匹配0次,则跳过该字符和"*",匹配1次,往后移动s接着匹配,
if (p.length() >= 2 && p.charAt(1) == '*') {
return isMatch(s, p.substring(2)) || firstMatch && isMatch(s.substring(1), p);
} else {
return firstMatch && isMatch(s.substring(1), p.substring(1));
}
}
}
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if (lists == null || lists.length == 0) return null;
return merge(lists, 0, lists.length - 1);
}
// 分治
public ListNode merge(ListNode[] lists, int left, int right) {
if (left > right) return null;
if (left == right) return lists[left];
int mid = (right - left) / 2 + left;
ListNode l1 = merge(lists, left, mid);
ListNode l2 = merge(lists, mid + 1, right);
return mergeTowList(l1, l2);
}
// 合并两个链表
public ListNode mergeTowList(ListNode l1, ListNode l2) {
if (l1 == null || l2 == null) {
return l1 != null ? l1 : l2;
}
ListNode res = new ListNode(0);
ListNode temp = res;
while (l1 != null && l2 != null) {
if (l1.val <= l2.val) {
temp.next = l1;
l1 = l1.next;
} else {
temp.next = l2;
l2 = l2.next;
}
temp = temp.next;
}
temp.next = l1 != null ? l1 : l2;
return res.next;
}
}
O(n), O(n)
class Solution {
public int trap(int[] height) {
int n = height.length;
int[] maxLeft = new int[n];
int[] maxRight = new int[n];
int res = 0;
// 左侧的最大高度
for (int i = 1; i < n; i++){
maxLeft[i] = Math.max(maxLeft[i - 1], height[i - 1]);
}
// 右侧的最大高度
for (int i = n - 2; i >= 1; i--){
maxRight[i] = Math.max(maxRight[i + 1], height[i + 1]);
}
// 最左(0)最右(n - 1)的位置都存不了水
for (int i = 1; i < n - 1; i++){
int minHeight = Math.min(maxLeft[i], maxRight[i]);
if (height[i] < minHeight) {
res += (minHeight - height[i]);
}
}
return res;
}
}
O(n), O(1)
class Solution {
public int trap(int[] height) {
int left = 0, right = height.length - 1;
int leftMax = 0, rightMax = 0;
int res = 0;
while (left < right) {
// 左右指针指向的柱子中更低的柱子
if (height[left] < height[right]) {
// 存不了水,更新左侧最大高度即可
if (height[left] >= leftMax) {
leftMax = height[left];
} else {
res += (leftMax - height[left]);
}
left++;
} else {
// 存不了水,更新右侧最大高度
if (height[right] >= rightMax) {
rightMax = height[right];
} else {
res += (rightMax - height[right]);
}
right--;
}
}
return res;
}
}
动态规划
—— dp[i - 1][j - 1]代表s1[0,...,i]到s2[0,...,j]的最小编辑距离
状态转换:
- word1.charAt(i - 1) == word2.charAt(j - 1) —> 跳过
- 否则,从增删替中选需要最小操作的,然后操作数+1
dp[i - 1][j] —— 删
dp[i][j - 1] —— 增
dp[i - 1][j - 1] —— 替
class Solution {
public int minDistance(String word1, String word2) {
int n1 = word1.length(), n2 = word2.length();
int[][] dp = new int[n1 + 1][n2 + 1];
// base case
for (int i = 1; i <= n1; i++){
dp[i][0] = i;
}
for (int i = 1; i <= n2; i++){
dp[0][i] = i;
}
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2; j++) {
// update state
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
dp[i][j] = minHelper(dp[i - 1][j] + 1, dp[i - 1][j - 1] + 1, dp[i][j - 1] + 1);
}
}
}
return dp[n1][n2];
}
public int minHelper(int a, int b, int c) {
return Math.min(a, Math.min(b, c));
}
}
滑动窗口
class Solution {
public String minWindow(String s, String t) {
int left = 0, right = 0;
int meet = 0;
int start = 0, end = 0;
int minLen = Integer.MAX_VALUE;
Map<Character, Integer> cover = new HashMap<>();
Map<Character, Integer> window = new HashMap<>();
// cover统计s中各字符出现的次数
for (char c : t.toCharArray()) {
cover.put(c, cover.getOrDefault(c, 0) + 1);
}
// 滑动窗口
while (right < s.length()) {
char c = s.charAt(right);
right++;
if (cover.containsKey(c)) {
window.put(c, window.getOrDefault(c, 0) + 1);
if (cover.get(c).equals(window.get(c))) {
meet++;
}
}
// 满足覆盖,开始缩小窗口求最小的覆盖子串
while (meet == cover.size()) {
// 更新最小窗口大小
if (right - left < minLen) {
start = left;
minLen = right - left;
}
// 缩小窗口
char d = s.charAt(left);
left++;
// 更新窗口内字符的出现次数
if (cover.containsKey(d)) {
// left右移后将不再满足覆盖条件,会退出内循环
if (cover.get(d).equals(window.get(d))) {
meet--;
}
// d在窗口内的出现次数-1
window.put(d, window.getOrDefault(d, 0) - 1);
}
}
}
return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen);
}
}
暴力方法O(n^2) —— 超时
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
int maxRecArea = 0;
for (int i = 0; i < n; i++) {
int left = i;
// 往左边扩展
while (left > 0 && heights[left - 1] >= heights[i]) {
left--;
}
int right = i;
// 往右边扩展
while (right < n - 1 && heights[right + 1] >= heights[i]) {
right++;
}
int width = (right - left + 1);
maxRecArea = Math.max(maxRecArea, width * heights[i]);
}
return maxRecArea;
}
}
单调递增栈——空间换时间 O(n),O(n)
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
Deque<Integer> stk = new ArrayDeque<>();
int[] newHeights = new int[n + 2];
int maxRecArea = 0;
// 新构造的数组首位添0
newHeights[0] = 0;
newHeights[n + 1] = 0;
for (int i = 1; i <= n; i++) {
newHeights[i] = heights[i - 1];
}
for (int i = 0; i < n + 2; i++) {
while (!stk.isEmpty() && newHeights[i] < newHeights[stk.peek()]) {
int cur = stk.pop();
int curH = newHeights[cur];
int left = stk.peek();
int right = i;
int recWidth = right - left - 1;
maxRecArea = Math.max(maxRecArea, recWidth * curH);
}
// 单调栈存放的是高度的下标
stk.push(i);
}
return maxRecArea;
}
}
求出每一层的heights,利用上一题的解法去解即可
class Solution {
public int maximalRectangle(char[][] matrix) {
if (matrix.length == 0) return 0;
int m = matrix.length, n = matrix[0].length;
int[] heights = new int[n];
int maxArea = 0;
for (int row = 0; row < m; row++) {
for (int col = 0; col < n; col++) {
if (matrix[row][col] == '1') {
heights[col] += 1;
} else {
heights[col] = 0;
}
}
maxArea = Math.max(maxArea, largestRectangleArea(heights));
}
return maxArea;
}
// 利用LC84-柱状图中最大的矩形的单调栈解法
public int largestRectangleArea(int[] heights) {
int n = heights.length;
Deque<Integer> stk = new ArrayDeque<>();
int[] newHeights = new int[n + 2];
int maxRecArea = 0;
// 新构造的数组首位添0
newHeights[0] = 0;
newHeights[n + 1] = 0;
for (int i = 1; i <= n; i++) {
newHeights[i] = heights[i - 1];
}
for (int i = 0; i < n + 2; i++) {
while (!stk.isEmpty() && newHeights[i] < newHeights[stk.peek()]) {
int cur = stk.pop();
int curH = newHeights[cur];
int left = stk.peek();
int right = i;
int recWidth = right - left - 1;
maxRecArea = Math.max(maxRecArea, recWidth * curH);
}
// 单调栈存放的是高度的下标
stk.push(i);
}
return maxRecArea;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
int res = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
//只有一个根节点
if (root.left == null && root.right == null) return root.val;
dfs(root);
return res;
}
public int dfs(TreeNode node){
if (node == null) return 0;
int leftSum = dfs(node.left);
int rightSum = dfs(node.right);
// 单边和用于返回
int cur = Math.max(node.val, Math.max(leftSum + node.val, rightSum + node.val));
//更新最大路径和
res = Math.max(res, Math.max(cur, node.val + leftSum + rightSum));
return cur;
}
}
并查集
import java.util.HashMap;
import java.util.Map;
public class Solution {
private class UnionFind {
private Map<Integer, Integer> parent;
private Map<Integer, Integer> size;
public UnionFind(int[] nums) {
int len = nums.length;
parent = new HashMap<>(len);
size = new HashMap<>(len);
for (int num : nums) {
parent.put(num, num);
size.put(num, 1);
}
}
//拼接树,将"重量小"的树根加到"重量大"的树下
public int union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
//同一棵树,不需要拼接
if (rootX == rootY) {
return 0;
}
//分别计算两棵树重量
int sizeX = size.get(rootX), sizeY = size.get(rootY);
int sum = sizeX + sizeY;
//将x的树根rootX拼接到rootY下
if (sizeX <= sizeY){
parent.put(rootX, rootY);
//更新拼接后的树的"重量"
size.put(rootY, sum);
} else {
//将y的树根rootY拼接到rootX下
parent.put(rootY, rootX);
//更新拼接后的树的"重量"
size.put(rootX, sum);
}
return sum;
}
//找当前节点的根节点,用到了路径压缩
public int find(int x) {
while (x != parent.get(x)) {
parent.put(x, parent.get(parent.get(x)));
x = parent.get(x);
}
return x;
}
public boolean contains(int x) {
return parent.containsKey(x);
}
}
public int longestConsecutive(int[] nums) {
int len = nums.length;
if (len == 0) {
return 0;
}
UnionFind unionFind = new UnionFind(nums);
int res = 1;
for (int num : nums) {
if (unionFind.contains(num - 1)) {
res = Math.max(res, unionFind.union(num, num - 1));
}
if (unionFind.contains(num + 1)) {
res = Math.max(res, unionFind.union(num, num + 1));
}
}
return res;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if (root == null) return "[]";
//层次遍历
StringBuilder sb = new StringBuilder("[");
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while (!q.isEmpty()){
int size = q.size();
for (int i = 0; i < size; i++){
TreeNode node = q.poll();
if (node != null) {
sb.append(node.val);
q.offer(node.left);
q.offer(node.right);
} else {
sb.append("null");
}
sb.append(",");
}
}
return sb.deleteCharAt(sb.length() - 1).append("]").toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
//data为null或者"[]"的情况
if (data == null || "[]".equals(data)) {
return null;
}
//去[],按","分割字符串
String[] nodes = data.substring(1, data.length() - 1).split(",");
int n = nodes.length;
Deque<TreeNode> queue = new ArrayDeque<>();
TreeNode root = new TreeNode(Integer.parseInt(nodes[0]));
queue.offer(root);
int cnt = 1;
while (!queue.isEmpty() && cnt < n){
TreeNode node = queue.poll();
if (!"null".equals(nodes[cnt])){
node.left = new TreeNode(Integer.parseInt(nodes[cnt]));
queue.offer(node.left);
}
cnt++;
if (!"null".equals(nodes[cnt])){
node.right = new TreeNode(Integer.parseInt(nodes[cnt]));
queue.offer(node.right);
}
cnt++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec ser = new Codec();
// Codec deser = new Codec();
// TreeNode ans = deser.deserialize(ser.serialize(root));