categories: leetcode



会一直更新下去

22. Generate Parentheses

Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
For example, given n = 3, a solution set is:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]

code

  1. class Solution {
  2. public List<String> generateParenthesis(int n) {
  3. List<String> ans = new ArrayList();
  4. backtrack(ans, "", 0, 0, n);
  5. return ans;
  6. }
  7. public void backtrack(List<String>ans, String cur, int open, int close, int max) {
  8. if(cur.length() == max * 2) {
  9. ans.add(cur);
  10. return;
  11. }
  12. if(open < max) {
  13. backtrack(ans, cur+"(", open+1, close, max);
  14. }
  15. if(close < open) {
  16. backtrack(ans, cur+")", open, close+1, max);
  17. }
  18. }
  19. }

summary

通过以(开头,可以递归遍历所有括号匹配情况。左括号不可能在大于n的下标,右括号要与左括号匹配。每次只可能添加( or ),所以两个if条件足够,当不满足条件则向上回溯。回溯(也就是递归)的边界条件是 open < max 和close < open。

23. Merge k Sorted Lists

Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
Input:
[
1->4->5,
1->3->4,
2->6
]
Output: 1->1->2->3->4->4->5->6

code

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode(int x) { val = x; }
  7. * }
  8. */
  9. class Solution {
  10. public ListNode mergeKLists(ListNode[] lists) {
  11. Comparator<ListNode> cmp;
  12. cmp = new Comparator<ListNode>() {
  13. @Override
  14. public int compare(ListNode o1, ListNode o2) {
  15. return o1.val - o2.val;
  16. }
  17. };
  18. Queue<ListNode> q = new PriorityQueue<ListNode>(cmp);
  19. for(ListNode l : lists) {
  20. if(l != null) {
  21. q.add(l);
  22. }
  23. }
  24. ListNode head = new ListNode(0);
  25. ListNode point = head;
  26. while(!q.isEmpty()) {
  27. point.next = q.poll();
  28. point = point.next;
  29. ListNode next = point.next;
  30. if(next != null) {
  31. q.add(next);
  32. }
  33. }
  34. return head.next;
  35. }
  36. }

summary

用优先队列,还是要熟悉优先队列,两两合并的方法比较坑,[[2],[],[-1]] 这样的情况还需要多进行一层判断。参考 https://leetcode.wang/leetCode-23-Merge-k-Sorted-Lists.html

31. Next Permutation

Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
The replacement must be in-place and use only constant extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,31,3,2
3,2,11,2,3
1,1,51,5,1

code

  1. class Solution {
  2. public void nextPermutation(int[] nums) {
  3. int i = nums.length - 2;
  4. while (i >= 0 && nums[i+1] <= nums[i]) {//少了等号会发生越界
  5. i--;
  6. }
  7. if( i >= 0) {
  8. int j = nums.length - 1;
  9. while (j >= 0 && nums[j] <= nums[i]) {
  10. j--;
  11. }
  12. swap(nums, i, j);
  13. }
  14. reverse(nums, i + 1);
  15. }
  16. private void reverse(int [] nums, int start) {//不需要调用系统,必然有序,所以两两对调
  17. int i = start, j = nums.length - 1;
  18. while(i < j) {
  19. swap(nums, i, j);
  20. i++;
  21. j--;
  22. }
  23. }
  24. private void swap(int[] nums, int i, int j) {
  25. int temp = nums[i];
  26. nums[i] = nums[j];
  27. nums[j] = temp;
  28. }
  29. }

summary

官网总结的很精致,可以参考普通思路一点点来。无论数字怎么排列,找 next permutation 分成三步,1)从右往左找到第一个非递增的数字,2)将非递增的数字左边第一个数字与其右侧,从右往左第一个大于他的数字对调,3)将非递增数字右侧数字进行升序排序。这种计算机思考方式真的奇怪,它是对的,但你总感觉别扭,想去验证。只能说,计算机思维和从小养成的按部就班、投机取巧的数学思维习惯很不同吧。计算机思维讲究有逻辑的高度正确、简洁,我应试教育的数学思维还是习惯提心吊胆地推演。。。。可能一直学的都不是正确的方法?还是说要自己认识到并改正,这好像也让我感受到庸人和天才的云泥之别

32. Longest Valid Parentheses

Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: “(()”
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input:)()())
Output: 4
Explanation: The longest valid parentheses substring is "()()"

code

  1. //栈索引法
  2. class Solution {
  3. public int longestValidParentheses(String s) {
  4. int maxans = 0;
  5. Stack<Integer> stack = new Stack<>();
  6. stack.push(-1);
  7. for(int i =0; i < s.length(); i++) {
  8. if(s.charAt(i) == '(') {
  9. stack.push(i);
  10. } else{
  11. stack.pop();
  12. if(stack.empty()) {
  13. stack.push(i);//确保边界情况时,i - stack.peek()为0
  14. } else {
  15. maxans = Math.max(maxans, i - stack.peek());
  16. }
  17. }
  18. }
  19. return maxans;
  20. }
  21. }
  22. //Without extra space
  23. class Solution {
  24. public int longestValidParentheses(String s) {
  25. int left = 0, right = 0, maxLength = 0;
  26. for(int i = 0; i < s.length(); i++) {
  27. if(s.charAt(i) == '(') {
  28. left++;
  29. } else {
  30. right++;
  31. }
  32. if(left == right) {
  33. maxLength = Math.max(maxLength, 2 * right);
  34. } else if(right > left){
  35. left = right = 0;
  36. }
  37. }
  38. left = right = 0;
  39. for(int i = s.length() - 1; i >= 0; i--) {
  40. if(s.charAt(i) == '(') {
  41. left++;
  42. } else {
  43. right++;
  44. }
  45. if(left == right) {
  46. maxLength = Math.max(maxLength, 2 * right);
  47. } else if(right < left){
  48. left = right = 0;
  49. }
  50. }
  51. return maxLength;
  52. }
  53. }
  54. //Using Dynamic Programming
  55. class Solution {
  56. public int longestValidParentheses(String s) {
  57. int maxans = 0;
  58. int dp[] = new int[s.length()];
  59. for(int i = 1; i < s.length(); i++) {
  60. if(s.charAt(i) == ')') {
  61. if(s.charAt(i - 1) == '(') {
  62. dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
  63. } else if(i - dp[i - 1] > 0 && s.charAt(i - dp[i-1] - 1) == '(') {
  64. dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) +2;
  65. }
  66. maxans = Math.max(maxans, dp[i]);
  67. }
  68. }
  69. return maxans;
  70. }
  71. }

summary

栈可以避免一种括号多的情况。Without extra space 必须两侧都遍历,因为只一侧可能出现永远不相等的情况。前面感觉在划水,尤其是 Without extra space 方法。动态规划才是硬解法,不过是真的好难……..This problem can be solved by using Dynamic Programming.(手动狗头)

33. Search in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Your algorithm’s runtime complexity must be in the order of O(log n).
Example 1:
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4
Example 2:
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

code

  1. class Solution {
  2. public int search(int[] nums, int target) {
  3. int left = 0, mid = 0, right = nums.length - 1;
  4. while (left <= right) {
  5. mid = (left + right) / 2;
  6. if(nums[mid] == target) return mid;
  7. if(nums[mid] < nums[right]) {
  8. if(nums[mid] < target && target <= nums[right])
  9. left = mid + 1;
  10. else right = mid -1;
  11. }
  12. else {
  13. if(nums[left] <= target && target < nums[mid])
  14. right = mid - 1;
  15. else left = mid + 1;
  16. }
  17. }
  18. return -1;
  19. }
  20. }

summary

虽然有旋转,但是和旋转点没任何关系。旋转是为了打乱顺序,然后从中找到需要的点。所以二分法是很容易想到的。因为边界值可能出现想要的结果,所以要做等于的判断。可以参考我的参考

34. Find First and Last Position of Element in Sorted Array

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given targetvalue.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

code

  1. //我的
  2. class Solution {
  3. public int[] searchRange(int[] nums, int target) {
  4. int[] a = {-1, -1};
  5. int left = 0, mid = 0, right = nums.length - 1;
  6. while(left <= right) {
  7. mid = (left + right) / 2;
  8. if(nums[mid] == target) {
  9. a = sorted(mid, a, nums,left, right);
  10. return a;
  11. } else if(nums[mid] < target) {
  12. left = mid + 1;
  13. } else {
  14. right = mid - 1;
  15. }
  16. }
  17. return a;
  18. }
  19. private int[] sorted(int mid, int[] a, int[] nums, int left, int right) {
  20. for(int i = left; i <= right; i++) {
  21. if(nums[i] == nums[mid]) {
  22. a[0] = i;
  23. break;
  24. }
  25. }
  26. for(int j = right; j >= a[0]; j--) {
  27. if(nums[j] == nums[mid]) {
  28. a[1] = j;
  29. break;
  30. }
  31. }
  32. return a;
  33. }
  34. }
  35. //官网
  36. class Solution {
  37. public int[] searchRange(int[] nums, int target) {
  38. int[] targetRange = {-1, -1};
  39. int leftIdx = extremeInsertionIndex(nums, target, true);
  40. if(leftIdx == nums.length || nums[leftIdx] != target) {
  41. return targetRange;
  42. }
  43. targetRange[0] = leftIdx;
  44. targetRange[1] = extremeInsertionIndex(nums, target, false) - 1;
  45. return targetRange;
  46. }
  47. private int extremeInsertionIndex(int[] nums, int target, boolean left) {
  48. int lo = 0;
  49. int hi = nums.length;
  50. while (lo < hi) {
  51. int mid = (lo + hi) / 2;
  52. if(nums[mid] > target || (left && target == nums[mid])) {
  53. hi = mid;//缩短右端距离,防止出现最左侧无效的情况
  54. }
  55. else {
  56. lo = mid + 1;//缩短左端距离
  57. }
  58. }
  59. return lo;
  60. }
  61. }

summary

先用二分查找,再用遍历,数学不好,不知道我那个复杂度是多少,应该离 O(logn) 不远,当距离缩短,遍历可能更快速。官网的巧妙在用boolean来控制,显而易见,其复杂度是 O(logn) 。

39. Combination Sum

Given a set of candidate numbers (candidates) (without duplicates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target.
The same repeated number may be chosen from candidates unlimited number of times.
Note:

  • All numbers (including target) will be positive integers.
  • The solution set must not contain duplicate combinations.

Example 1:
Input: candidates = [2,3,6,7],target = 7,
A solution set is:
[
[7],
[2,2,3]
]
Example 2:
Input: candidates = [2,3,5],target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

code

  1. //recursive
  2. class Solution {
  3. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  4. Arrays.sort(candidates);
  5. List<List<Integer>> result = new ArrayList<List<Integer>>();
  6. getResult(result, new ArrayList<Integer>(), candidates, target, 0);
  7. return result;
  8. }
  9. private void getResult(List<List<Integer>> result, List<Integer> cur, int[] candidates, int target, int start) {
  10. if(target > 0) {
  11. for(int i = start; i < candidates.length && target >= candidates[i]; i++) {
  12. cur.add(candidates[i]);
  13. getResult(result, cur, candidates, target - candidates[i], i);
  14. cur.remove(cur.size() - 1);//排除错误情况,移除后推进
  15. }
  16. }
  17. else if(target == 0) {
  18. result.add(new ArrayList<Integer>(cur));//确保有正确则立即保存
  19. }
  20. }
  21. }
  22. //DP solution
  23. public List<List<Integer>> combinationSum(int[] candidates, int target) {
  24. // sort candidates to try them in asc order
  25. Arrays.sort(candidates);
  26. // dp[t] stores all combinations that add up to t
  27. List<List<Integer>>[] dp = new ArrayList[target+1];
  28. // build up dp
  29. for(int t=0; t<=target; t++) {
  30. // initialize
  31. dp[t] = new ArrayList();
  32. // initialize
  33. List<List<Integer>> combList = new ArrayList();
  34. // for each t, find possible combinations
  35. for(int j=0; j<candidates.length && candidates[j] <= t; j++) {
  36. if(candidates[j] == t) {
  37. combList.add(Arrays.asList(candidates[j])); // itself can form a list
  38. } else {
  39. for(List<Integer> prevlist: dp[t-candidates[j]]) { // here use our dp definition
  40. // i thought it makes more sense to compare with the last element
  41. // only add to list when the candidates[j] >= the last element
  42. // so the list remains ascending order, can prevent duplicate (ex. has [2 3 3], no [3 2 3])
  43. // equal is needed since we can choose the same element many times
  44. if(candidates[j] >= prevlist.get(prevlist.size()-1)){
  45. List temp = new ArrayList(prevlist); // temp is needed since
  46. temp.add(candidates[j]); // cannot edit prevlist inside 4eeach looop
  47. combList.add(temp);
  48. }
  49. }
  50. }
  51. }
  52. dp[t] = combList;
  53. }
  54. return dp[target];
  55. }

summary

从 Discuss 里面找了递归和动规两个解法,递归里面,List>[]是指数组的链表的链表,即三层