-
382. Linked List Random Node
Given a singly linked list, return a random node's value from the linked list. Each node must have the same probability of being chosen. Follow up: What if the linked list is extremely large and its length is unknown to you? Could you solve this efficiently without using extra space? Example: // Init a singly linked list [1,2,3]. ListNode head = new ListNode(1); head.next = new ListNode(2); head.next.next = new ListNode(3); Solution solution = new Solution(head); // getRandom() should return either 1, 2, or 3 randomly. Each element should have equal probability of returning. solution.getRandom();
class Solution { ListNode head; Random random = new Random(); /** @param head The linked list's head. Note that the head is guaranteed to be not null, so it contains at least one node. */ public Solution(ListNode head) { this.head = head; } /** Returns a random node's value. */ public int getRandom() { ListNode curr = head; int r = curr.val; for(int i=1; curr.next != null; i++){ curr = curr.next; if(random.nextInt(i + 1) == i) r = curr.val; } return r; } }
-
478. Generate Random Point in a Circle
478. Generate Random Point in a Circle
Given the radius and x-y positions of the center of a circle, write a function randPoint which generates a uniform random point in the circle. Note: input and output values are in floating-point. radius and x-y position of the center of the circle is passed into the class constructor. a point on the circumference of the circle is considered to be in the circle. randPoint returns a size 2 array containing x-position and y-position of the random point, in that order. Example 1: Input: ["Solution","randPoint","randPoint","randPoint"] [[1,0,0],[],[],[]] Output: [null,[-0.72939,-0.65505],[-0.78502,-0.28626],[-0.83119,-0.19803]] Example 2: Input: ["Solution","randPoint","randPoint","randPoint"] [[10,5,-7.5],[],[],[]] Output: [null,[11.52438,-8.33273],[2.46992,-16.21705],[11.13430,-12.42337]]
- Give up illegal data points
class Solution { double r, xc, yc; Random random = new Random(); public Solution(double radius, double x_center, double y_center) { r = radius; xc = x_center; yc = y_center; } public double[] randPoint() { while(true) { double x = random.nextDouble() * r * 2 - r; double y = random.nextDouble() * r * 2 - r; if(x * x + y * y <= r * r) { return new double[]{xc + x, yc + y}; } } } }
-
519. Random Flip Matrix
You are given the number of rows n_rows and number of columns n_cols of a 2D binary matrix where all values are initially 0. Write a function flip which chooses a 0 value uniformly at random, changes it to 1, and then returns the position [row.id, col.id] of that value. Also, write a function reset which sets all values back to 0. Try to minimize the number of calls to system's Math.random() and optimize the time and space complexity. Note: 1 <= n_rows, n_cols <= 10000 0 <= row.id < n_rows and 0 <= col.id < n_cols flip will not be called when the matrix has no 0 values left. the total number of calls to flip and reset will not exceed 1000. Example 1: Input: ["Solution","flip","flip","flip","flip"] [[2,3],[],[],[],[]] Output: [null,[0,1],[1,2],[1,0],[1,1]] Example 2: Input: ["Solution","flip","flip","reset","flip"] [[1,2],[],[],[],[]] Output: [null,[0,0],[0,1],null,[0,0]]
- Virtual Mapping
class Solution { Map<Integer, Integer> V = new HashMap<>(); int nr, nc, rem; Random rand = new Random(); public Solution(int n_rows, int n_cols) { nr = n_rows; nc = n_cols; rem = nr * nc; } public int[] flip() { int r = rand.nextInt(rem--); int x = V.getOrDefault(r, r); V.put(r, V.getOrDefault(rem, rem)); return new int[]{x / nc, x % nc}; } public void reset() { V.clear(); rem = nr * nc; } }
-
754. Reach a Number
You are standing at position 0 on an infinite number line. There is a goal at position target. On each move, you can either go left or right. During the n-th move (starting from 1), you take n steps. Return the minimum number of steps required to reach the destination. Example 1: Input: target = 3 Output: 2 Explanation: On the first move we step from 0 to 1. On the second step we step from 1 to 3. Example 2: Input: target = 2 Output: 3 Explanation: On the first move we step from 0 to 1. On the second move we step from 1 to -1. On the third move we step from -1 to 2. Note: target will be a non-zero integer in the range [-10^9, 10^9].
- Greedy
- harder than easy
class Solution { public int reachNumber(int target) { target = Math.abs(target); int distance = 0, step = 0; while (distance < target) { distance += ++step; } int delta = target - distance; if(delta % 2 == 0) return step; else if((delta + step + 1) % 2 == 0) return step + 1; else return step + 2; } }
- Greedy
-
470. Implement Rand10() Using Rand7()
470. Implement Rand10() Using Rand7()
Given a function rand7 which generates a uniform random integer in the range 1 to 7, write a function rand10 which generates a uniform random integer in the range 1 to 10. Do NOT use system's Math.random(). Example 1: Input: 1 Output: [7] Example 2: Input: 2 Output: [8,4] Example 3: Input: 3 Output: [8,1,10] Note: rand7 is predefined. Each testcase has one argument: n, the number of times that rand10 is called.
- Give up illegal data points
class Solution extends SolBase { public int rand10() { int row = 0, col = 0, index = 41; while(index > 40) { row = rand7(); col = rand7(); index = col + (row - 1) * 7; } return 1 + (index - 1) % 10; } }
-
436. Find Right Interval
Given a set of intervals, for each of the interval i, check if there exists an interval j whose start point is bigger than or equal to the end point of the interval i, which can be called that j is on the "right" of i. For any interval i, you need to store the minimum interval j's index, which means that the interval j has the minimum start point to build the "right" relationship for interval i. If the interval j doesn't exist, store -1 for the interval i. Finally, you need output the stored value of each interval as an array. Note: You may assume the interval's end point is always bigger than its start point. You may assume none of these intervals have the same start point. Example 1: Input: [ [1,2] ] Output: [-1] Explanation: There is only one interval in the collection, so it outputs -1. Example 2: Input: [ [3,4], [2,3], [1,2] ] Output: [-1, 0, 1] Explanation: There is no satisfied "right" interval for [3,4]. For [2,3], the interval [3,4] has minimum-"right" start point; For [1,2], the interval [2,3] has minimum-"right" start point. Example 3: Input: [ [1,4], [2,3], [3,4] ] Output: [-1, 2, -1] Explanation: There is no satisfied "right" interval for [1,4] and [3,4]. For [2,3], the interval [3,4] has minimum-"right" start point. NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature.
- Binary Search
public int[] findRightInterval(int[][] intervals) { int[][] starts = new int[intervals.length][2]; for(int i = 0; i < starts.length; i++) starts[i] = new int[] {intervals[i][0], i}; Arrays.sort(starts, (a,b)->Integer.compare(a[0],b[0])); int[] result = new int[intervals.length]; Arrays.fill(result, -1); for(int i = 0; i < intervals.length; i++) { int end = intervals[i][1]; int l = 0, r = starts.length-1; while(l < r) { int mid = l + (r-l) / 2; int start = starts[mid][0]; if(start >= end) { r = mid; } else { l = mid + 1; } } if(starts[l][0] >= end && i != starts[l][1]) { result[i] = starts[l][1]; } } return result; } }
-
826. Most Profit Assigning Work
826. Most Profit Assigning Work
We have jobs: difficulty[i] is the difficulty of the ith job, and profit[i] is the profit of the ith job. Now we have some workers. worker[i] is the ability of the ith worker, which means that this worker can only complete a job with difficulty at most worker[i]. Every worker can be assigned at most one job, but one job can be completed multiple times. For example, if 3 people attempt the same job that pays $1, then the total profit will be $3. If a worker cannot complete any job, his profit is $0. What is the most profit we can make? Example 1: Input: difficulty = [2,4,6,8,10], profit = [10,20,30,40,50], worker = [4,5,6,7] Output: 100 Explanation: Workers are assigned jobs of difficulty [4,4,6,6] and they get profit of [20,20,30,30] seperately. Notes: 1 <= difficulty.length = profit.length <= 10000 1 <= worker.length <= 10000 difficulty[i], profit[i], worker[i] are in range [1, 10^5]
- Sorting
public int maxProfitAssignment(int[] difficulty, int[] profit, int[] worker) { int[][] arr = new int[profit.length][2]; for(int i = 0; i < arr.length; i++) { arr[i] = new int[] {difficulty[i], profit[i]}; } Arrays.sort(arr, (a,b)->(a[0]-b[0])); Arrays.sort(worker); int result = 0, maxP = 0, i = 0; for(int w : worker) { while(i < arr.length && arr[i][0] <= w) { maxP = Math.max(maxP, arr[i++][1]); } result += maxP; } return result; }
-
1044. Longest Duplicate Substring
1044. Longest Duplicate Substring
Given a string S, consider all duplicated substrings: (contiguous) substrings of S that occur 2 or more times. (The occurrences may overlap.) Return any duplicated substring that has the longest possible length. (If S does not have a duplicated substring, the answer is "".) Example 1: Input: "banana" Output: "ana" Example 2: Input: "abcd" Output: "" Note: 2 <= S.length <= 10^5 S consists of lowercase English letters.
- BinarySearch + Rolling Hash
class Solution { int a; long modulus; public String longestDupSubstring(String S) { a = 26; modulus = (long)Math.pow(2, 32); int l = 1, r = S.length()-1; String result = ""; while(l <= r) { int mid = l + (r-l) / 2; String temp = search(S, mid); if(temp != null) { result = temp; l = mid + 1; } else { r = mid - 1; } } return result; } public String search(String S, int L) { HashSet<Long> set = new HashSet<>(); long hash = 0, aL = 1; for (int i = 1; i <= L; ++i) aL = (aL * a) % modulus; for(int i = 0; i < L; ++i) hash = (hash * a + (S.charAt(i)-'a')) % modulus; set.add(hash); for(int i = 1; i+L-1 < S.length(); i++) { int j = i+L-1; hash = (hash * a - (S.charAt(i-1)-'a') * aL % modulus + modulus) % modulus; hash = (hash + (S.charAt(j)-'a')) % modulus; if(set.contains(hash)) return S.substring(i, j+1); set.add(hash); } return null; } }
-
1000. Minimum Cost to Merge Stones
1000. Minimum Cost to Merge Stones
There are N piles of stones arranged in a row. The i-th pile has stones[i] stones. A move consists of merging exactly K consecutive piles into one pile, and the cost of this move is equal to the total number of stones in these K piles. Find the minimum cost to merge all piles of stones into one pile. If it is impossible, return -1. Example 1: Input: stones = [3,2,4,1], K = 2 Output: 20 Explanation: We start with [3, 2, 4, 1]. We merge [3, 2] for a cost of 5, and we are left with [5, 4, 1]. We merge [4, 1] for a cost of 5, and we are left with [5, 5]. We merge [5, 5] for a cost of 10, and we are left with [10]. The total cost was 20, and this is the minimum possible. Example 2: Input: stones = [3,2,4,1], K = 3 Output: -1 Explanation: After any merge operation, there are 2 piles left, and we can't merge anymore. So the task is impossible. Example 3: Input: stones = [3,5,1,2,6], K = 3 Output: 25 Explanation: We start with [3, 5, 1, 2, 6]. We merge [5, 1, 2] for a cost of 8, and we are left with [3, 8, 6]. We merge [3, 8, 6] for a cost of 17, and we are left with [17]. The total cost was 25, and this is the minimum possible. Note: 1 <= stones.length <= 30 2 <= K <= 30 1 <= stones[i] <= 100
- DP
public int mergeStones(int[] stones, int K) { int N = stones.length; if((N-1) % (K-1) != 0) { return -1; } int[] sums = new int[N+1]; for(int i = 1; i < sums.length; i++) { sums[i] = sums[i-1] + stones[i-1]; } // sum(i,j) == sums[j+1]-sums[i] int[][][] dp = new int[N+1][N+1][K+1]; for (int i = 1; i <= N; i++) { for (int j = 1; j <= N; j++) { for (int k = 1; k <= K; k++) { dp[i][j][k] = Integer.MAX_VALUE; } } } for (int i = 1; i <= N; i++) { dp[i][i][1] = 0; } for(int w = 2; w <= N; w++) { for(int i = 1; i+w-1 < N+1; i++) { int j = i+w-1; for(int k = 2; k <= K; k++) { for(int t = i; t < j; t++) { if(dp[i][t][k-1] == Integer.MAX_VALUE || dp[t+1][j][1] == Integer.MAX_VALUE) continue; dp[i][j][k] = Math.min(dp[i][j][k], dp[i][t][k-1] + dp[t+1][j][1]); } } if(dp[i][j][K] != Integer.MAX_VALUE) dp[i][j][1] = dp[i][j][K] + sums[j] - sums[i-1]; } } return dp[1][N][1]; }
-
456. 132 Pattern
Given a sequence of n integers a1, a2, ..., an, a 132 pattern is a subsequence ai, aj, ak such that i < j < k and ai < ak < aj. Design an algorithm that takes a list of n numbers as input and checks whether there is a 132 pattern in the list. Note: n will be less than 15,000. Example 1: Input: [1, 2, 3, 4] Output: False Explanation: There is no 132 pattern in the sequence. Example 2: Input: [3, 1, 4, 2] Output: True Explanation: There is a 132 pattern in the sequence: [1, 4, 2]. Example 3: Input: [-1, 3, 2, 0] Output: True Explanation: There are three 132 patterns in the sequence: [-1, 3, 2], [-1, 3, 0] and [-1, 2, 0].
- Monotonic Stack
- k is ak, is “2” in “132”
- elements in the stack are valid “3” in “132”
- the while loop increases both “3” and “2”
public boolean find132pattern(int[] nums) { Stack<Integer> stack = new Stack<>(); int k = Integer.MIN_VALUE; for(int i = nums.length-1; i >= 0; i--) { int v = nums[i]; if(v < k) return true; while(!stack.isEmpty() && v > stack.peek()) { k = stack.pop(); // this is a better ak < aj pair } stack.push(v); } return false; }
- TreeSet
class Solution { public boolean find132pattern(int[] nums) { if(nums.length < 3) return false; int[] mins = new int[nums.length]; mins[1] = nums[0]; for(int i = 2; i < nums.length; i++) mins[i] = Math.min(nums[i-1], mins[i-1]); TreeSet<Integer> set = new TreeSet<>(); for(int i = nums.length-1; i >= 1; i--) { int min = mins[i]; int max = nums[i]; if(min < max && !set.subSet(min, false, max, false).isEmpty()) { return true; } set.add(max); } return false; } }
- Monotonic Stack
-
878. Nth Magical Number
A positive integer is magical if it is divisible by either A or B. Return the N-th magical number. Since the answer may be very large, return it modulo 10^9 + 7. Example 1: Input: N = 1, A = 2, B = 3 Output: 2 Example 2: Input: N = 4, A = 2, B = 3 Output: 6 Example 3: Input: N = 5, A = 2, B = 4 Output: 10 Example 4: Input: N = 3, A = 6, B = 4 Output: 8 Note: 1 <= N <= 10^9 2 <= A <= 40000 2 <= B <= 40000
- BinarySearch
class Solution { public int nthMagicalNumber(int N, int A, int B) { int lcm = lcm(A, B); long l = Math.min(A, B); long r = Long.MAX_VALUE; int MOD = 1_000_000_007; while(l < r) { long mid = l + (r-l) / 2; long v = mid / A + mid / B - mid / lcm; if(v > N) { r = mid - 1; } else if(v < N) { l = mid + 1; } else { r = mid; } } return (int) (l % MOD); } int lcm(int a, int b) { int gcd = gcd(a, b); return a * (b / gcd); } int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); } }
-
1250. Check If It Is a Good Array
1250. Check If It Is a Good Array
Given an array nums of positive integers. Your task is to select some subset of nums, multiply each element by an integer and add all these numbers. The array is said to be good if you can obtain a sum of 1 from the array by any possible subset and multiplicand. Return True if the array is good otherwise return False. Example 1: Input: nums = [12,5,7,23] Output: true Explanation: Pick numbers 5 and 7. 5*3 + 7*(-2) = 1 Example 2: Input: nums = [29,6,10] Output: true Explanation: Pick numbers 29, 6 and 10. 29*1 + 6*(-3) + 10*(-1) = 1 Example 3: Input: nums = [3,6] Output: false Constraints: 1 <= nums.length <= 10^5 1 <= nums[i] <= 10^9
- Math
- https://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity
- pure math
public boolean isGoodArray(int[] nums) { int result = nums[0]; for(int i = 1; i < nums.length; i++) { result = gcd(result, nums[i]); } return result == 1; } public int gcd(int a, int b) { if(b == 0) return a; return gcd(b, a % b); }
- Math
-
1062. Longest Repeating Substring
1062. Longest Repeating Substring
Given a string S, find out the length of the longest repeating substring(s). Return 0 if no repeating substring exists. Example 1: Input: "abcd" Output: 0 Explanation: There is no repeating substring. Example 2: Input: "abbaba" Output: 2 Explanation: The longest repeating substrings are "ab" and "ba", each of which occurs twice. Example 3: Input: "aabcaabdaab" Output: 3 Explanation: The longest repeating substring is "aab", which occurs 3 times. Example 4: Input: "aaaaa" Output: 4 Explanation: The longest repeating substring is "aaaa", which occurs twice. Note: The string S consists of only lowercase English letters from 'a' - 'z'. 1 <= S.length <= 1500
- Binary Search + HashSet
public int longestRepeatingSubstring(String S) { int l = 0, r = S.length()-1; while(l+1 < r) { int mid = l + (r-l) / 2; boolean valid = search(S, mid); if(valid) { l = mid; } else { r = mid; } } if(search(S, r)) return r; return l; } public boolean search(String S, int L) { HashSet<Integer> set = new HashSet<>(); for(int i = 0; i+L-1 < S.length(); i++) { int j = i+L-1; int hash = S.substring(i, j+1).hashCode(); if(set.contains(hash)) return true; set.add(hash); } return false; } }
-
1243. Array Transformation
Given an initial array arr, every day you produce a new array using the array of the previous day. On the i-th day, you do the following operations on the array of day i-1 to produce the array of day i: If an element is smaller than both its left neighbor and its right neighbor, then this element is incremented. If an element is bigger than both its left neighbor and its right neighbor, then this element is decremented. The first and last elements never change. After some days, the array does not change. Return that final array. Example 1: Input: arr = [6,2,3,4] Output: [6,3,3,4] Explanation: On the first day, the array is changed from [6,2,3,4] to [6,3,3,4]. No more operations can be done to this array. Example 2: Input: arr = [1,6,3,4,3,5] Output: [1,4,4,4,4,5] Explanation: On the first day, the array is changed from [1,6,3,4,3,5] to [1,5,4,3,4,5]. On the second day, the array is changed from [1,5,4,3,4,5] to [1,4,4,4,4,5]. No more operations can be done to this array. Constraints: 1 <= arr.length <= 100 1 <= arr[i] <= 100
- Iterate
public List<Integer> transformArray(int[] arr) { int left = -1; boolean isChange = true; while(isChange) { left = arr[0]; isChange = false; for(int i = 1; i < arr.length-1; i++) { int v = arr[i]; if(v > left && v > arr[i+1]) { arr[i]--; isChange = true; } else if(v < left && v < arr[i+1]) { arr[i]++; isChange = true; } left = v; } } List<Integer> result = new ArrayList<>(); for(int i : arr) result.add(i); return result; }
-
1245. Tree Diameter
Given an undirected tree, return its diameter: the number of edges in a longest path in that tree. The tree is given as an array of edges where edges[i] = [u, v] is a bidirectional edge between nodes u and v. Each node has labels in the set {0, 1, ..., edges.length}. Example 1: Input: edges = [[0,1],[0,2]] Output: 2 Explanation: A longest path of the tree is the path 1 - 0 - 2. Example 2: Input: edges = [[0,1],[1,2],[2,3],[1,4],[4,5]] Output: 4 Explanation: A longest path of the tree is the path 3 - 2 - 1 - 4 - 5. Constraints: 0 <= edges.length < 10^4 edges[i][0] != edges[i][1] 0 <= edges[i][j] <= edges.length The given edges form an undirected tree.
- DP
public int minimumMoves(int[] arr) { int N = arr.length; int[][] dp = new int[N+1][N]; for(int w = 1; w <= N; w++) { // w: window size for(int i = 0; i+w-1 < N; i++) { int j = i+w-1; if(w == 1) { dp[i][j] = 1; } else { dp[i][j] = 1 + dp[i+1][j]; // remove i if(arr[i] == arr[i+1]) { dp[i][j] = Math.min(dp[i][j], 1 + dp[i+2][j]); // remove i and i+1 } for(int k = i+2; k <= j; k++) { if(arr[k] == arr[i]) { // remove i and k alone with substring between them dp[i][j] = Math.min(dp[i][j], dp[i+1][k-1] + dp[k+1][j]); } } } } } return dp[0][N-1]; }
-
992. Subarrays with K Different Integers
992. Subarrays with K Different Integers
Given an array A of positive integers, call a (contiguous, not necessarily distinct) subarray of A good if the number of different integers in that subarray is exactly K. (For example, [1,2,3,1,2] has 3 different integers: 1, 2, and 3.) Return the number of good subarrays of A. Example 1: Input: A = [1,2,1,2,3], K = 2 Output: 7 Explanation: Subarrays formed with exactly 2 different integers: [1,2], [2,1], [1,2], [2,3], [1,2,1], [2,1,2], [1,2,1,2]. Example 2: Input: A = [1,2,1,3,4], K = 3 Output: 3 Explanation: Subarrays formed with exactly 3 different integers: [1,2,1,3], [2,1,3], [1,3,4]. Note: 1 <= A.length <= 20000 1 <= A[i] <= A.length 1 <= K <= A.length
- SlidingWindow
public int subarraysWithKDistinct(int[] A, int K) { return atMostK(A, K) - atMostK(A, K - 1); } public int atMostK(int[] A, int K) { int i = 0, res = 0; Map<Integer, Integer> count = new HashMap<>(); for (int j = 0; j < A.length; j++) { if (!count.containsKey(A[j]) || count.get(A[j]) == 0) K--; count.put(A[j], count.getOrDefault(A[j], 0) + 1); while (K < 0) { count.put(A[i], count.get(A[i]) - 1); if (count.get(A[i]) == 0) K++; i++; } res += j - i + 1; } return res; }
-
866. Prime Palindrome
Find the smallest prime palindrome greater than or equal to N. Recall that a number is prime if it's only divisors are 1 and itself, and it is greater than 1. For example, 2,3,5,7,11 and 13 are primes. Recall that a number is a palindrome if it reads the same from left to right as it does from right to left. For example, 12321 is a palindrome. Example 1: Input: 6 Output: 7 Example 2: Input: 8 Output: 11 Example 3: Input: 13 Output: 101 Note: 1 <= N <= 10^8 The answer is guaranteed to exist and be less than 2 * 10^8.
- Brutal Force of iterating palindromes by increasing root
class Solution { public int primePalindrome(int N) { for(int i = 0; i <= 4; i++) { int root = (int)Math.pow(10, i), limit = root * 10; int temp = 0, p = 0; while(root < limit) { temp = reverseInt(root / 10); p = root * (int)Math.pow(10, i) + temp; if(p >= N && isPrime(p)) { return p; } root++; } root = (int)Math.pow(10, i); while(root < limit) { temp = reverseInt(root); p = root * (int)Math.pow(10, i+1) + temp; if(p >= N && isPrime(p)) { return p; } root++; } } return -1; } public int reverseInt(int n) { int r = 0; while(n > 0) { r = r * 10 + n % 10; n = n / 10; } return r; } public boolean isPrime(int n) { if(n < 2) return false; for(int i = 2; i <= (int)Math.sqrt(n); i++) { if(n % i == 0) return false; } return true; } }
-
1249. Minimum Remove to Make Valid Parentheses
1249. Minimum Remove to Make Valid Parentheses
Given a string s of '(' , ')' and lowercase English characters. Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting parentheses string is valid and return any valid string. Formally, a parentheses string is valid if and only if: It is the empty string, contains only lowercase characters, or It can be written as AB (A concatenated with B), where A and B are valid strings, or It can be written as (A), where A is a valid string. Example 1: Input: s = "lee(t(c)o)de)" Output: "lee(t(c)o)de" Explanation: "lee(t(co)de)" , "lee(t(c)ode)" would also be accepted. Example 2: Input: s = "a)b(c)d" Output: "ab(c)d" Example 3: Input: s = "))((" Output: "" Explanation: An empty string is also valid. Example 4: Input: s = "(a(b(c)d)" Output: "a(b(c)d)" Constraints: 1 <= s.length <= 10^5 s[i] is one of '(' , ')' and lowercase English letters.
- O(N)
- first pass remove invalid ‘)’
- second pass remove invalid ‘(‘
public String minRemoveToMakeValid(String s) { StringBuilder sb = new StringBuilder(); int cnt = 0; for (char c : s.toCharArray()) { if (c == '(') { cnt++; } else if (c == ')') { if (cnt == 0) continue; cnt--; } sb.append(c); } for (int i = sb.length() - 1; i >= 0 && cnt > 0; i--) { if (sb.charAt(i) == '(') { sb.deleteCharAt(i); cnt--; } } return sb.toString(); }
- O(N)
-
1244. Design A Leaderboard
Design a Leaderboard class, which has 3 functions: addScore(playerId, score): Update the leaderboard by adding score to the given player's score. If there is no player with such id in the leaderboard, add him to the leaderboard with the given score. top(K): Return the score sum of the top K players. reset(playerId): Reset the score of the player with the given id to 0. It is guaranteed that the player was added to the leaderboard before calling this function. Initially, the leaderboard is empty. Example 1: Input: ["Leaderboard","addScore","addScore","addScore","addScore","addScore","top","reset","reset","addScore","top"] [[],[1,73],[2,56],[3,39],[4,51],[5,4],[1],[1],[2],[2,51],[3]] Output: [null,null,null,null,null,null,73,null,null,null,141] Explanation: Leaderboard leaderboard = new Leaderboard (); leaderboard.addScore(1,73); // leaderboard = [[1,73]]; leaderboard.addScore(2,56); // leaderboard = [[1,73],[2,56]]; leaderboard.addScore(3,39); // leaderboard = [[1,73],[2,56],[3,39]]; leaderboard.addScore(4,51); // leaderboard = [[1,73],[2,56],[3,39],[4,51]]; leaderboard.addScore(5,4); // leaderboard = [[1,73],[2,56],[3,39],[4,51],[5,4]]; leaderboard.top(1); // returns 73; leaderboard.reset(1); // leaderboard = [[2,56],[3,39],[4,51],[5,4]]; leaderboard.reset(2); // leaderboard = [[3,39],[4,51],[5,4]]; leaderboard.addScore(2,51); // leaderboard = [[2,51],[3,39],[4,51],[5,4]]; leaderboard.top(3); // returns 141 = 51 + 51 + 39; Constraints: 1 <= playerId, K <= 10000 It's guaranteed that K is less than or equal to the current number of players. 1 <= score <= 100 There will be at most 1000 function calls.
- PriorityQueue
- or treemap without enque and deque
class Leaderboard { HashMap<Integer, Player> map = new HashMap<>(); PriorityQueue<Player> q = new PriorityQueue<>(); class Player implements Comparable<Player> { int id; int score; public Player(int id) { this.id = id; } public Player(int id, int score) { this(id); this.score = score; } public int compareTo(Player p) { return Integer.compare(p.score, this.score); } } public Leaderboard() { } public void addScore(int playerId, int score) { Player p = null; if(map.containsKey(playerId)) { p = map.get(playerId); q.remove(p); p.score += score; } else { p = new Player(playerId, score); map.put(playerId, p); } q.offer(p); } public int top(int K) { ArrayList<Player> temp = new ArrayList<>(); int result = 0; for(int i = 0; i < K; i++) { Player p = q.poll(); result += p.score; temp.add(p); } for(Player p : temp) q.offer(p); return result; } public void reset(int playerId) { Player p = map.get(playerId); q.remove(p); p.score = 0; q.offer(p); } }
- PriorityQueue
-
1246. Palindrome Removal
Given an integer array arr, in one move you can select a palindromic subarray arr[i], arr[i+1], ..., arr[j] where i <= j, and remove that subarray from the given array. Note that after removing a subarray, the elements on the left and on the right of that subarray move to fill the gap left by the removal. Return the minimum number of moves needed to remove all numbers from the array. Example 1: Input: arr = [1,2] Output: 2 Example 2: Input: arr = [1,3,4,1,5] Output: 3 Explanation: Remove [4] then remove [1,3,1] then remove [5]. Constraints: 1 <= arr.length <= 100 1 <= arr[i] <= 20
- DP
public int minimumMoves(int[] arr) { int N = arr.length; int[][] dp = new int[N+1][N]; for(int w = 1; w <= N; w++) { // w: window size for(int i = 0; i+w-1 < N; i++) { int j = i+w-1; if(w == 1) { dp[i][j] = 1; } else { dp[i][j] = 1 + dp[i+1][j]; // remove i if(arr[i] == arr[i+1]) { dp[i][j] = Math.min(dp[i][j], 1 + dp[i+2][j]); // remove i and i+1 } for(int k = i+2; k <= j; k++) { if(arr[k] == arr[i]) { // remove i and k alone with substring between them dp[i][j] = Math.min(dp[i][j], dp[i+1][k-1] + dp[k+1][j]); } } } } } return dp[0][N-1]; }
-
911. Online Election
In an election, the i-th vote was cast for persons[i] at time times[i]. Now, we would like to implement the following query function: TopVotedCandidate.q(int t) will return the number of the person that was leading the election at time t. Votes cast at time t will count towards our query. In the case of a tie, the most recent vote (among tied candidates) wins. Example 1: Input: ["TopVotedCandidate","q","q","q","q","q","q"], [[[0,1,1,0,0,1,0],[0,5,10,15,20,25,30]],[3],[12],[25],[15],[24],[8]] Output: [null,0,1,1,0,0,1] Explanation: At time 3, the votes are [0], and 0 is leading. At time 12, the votes are [0,1,1], and 1 is leading. At time 25, the votes are [0,1,1,0,0,1], and 1 is leading (as ties go to the most recent vote.) This continues for 3 more queries at time 15, 24, and 8. Note: 1 <= persons.length = times.length <= 5000 0 <= persons[i] <= persons.length times is a strictly increasing array with all elements in [0, 10^9]. TopVotedCandidate.q is called at most 10000 times per test case. TopVotedCandidate.q(int t) is always called with t >= times[0].
- preprocess and binary search
class TopVotedCandidate { int[] result; int[] times; public TopVotedCandidate(int[] persons, int[] times) { this.times = times; result = new int[persons.length+1]; HashMap<Integer, Integer> map = new HashMap<>(); int max = -1, id = -1; for(int i = 0; i < times.length; i++) { int v = 1 + map.getOrDefault(persons[i], 0); map.put(persons[i], v); if(v >= max) { max = v; id = persons[i]; } result[i] = id; } } class Person { int id; int cnt; int time; public Person(int id, int cnt, int time) { this.id = id; this.cnt = cnt; this.time = time; } } public int q(int t) { int i = Arrays.binarySearch(times, t); if(i < 0) { i = -i-2; } return result[i]; } }
-
1004. Max Consecutive Ones III
1004. Max Consecutive Ones III
Given an array A of 0s and 1s, we may change up to K values from 0 to 1. Return the length of the longest (contiguous) subarray that contains only 1s. Example 1: Input: A = [1,1,1,0,0,0,1,1,1,1,0], K = 2 Output: 6 Explanation: [1,1,1,0,0,1,1,1,1,1,1] Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. Example 2: Input: A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 Output: 10 Explanation: [0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. Note: 1 <= A.length <= 20000 0 <= K <= A.length A[i] is 0 or 1
- Sliding Window
class Solution { public int longestOnes(int[] A, int K) { int l = 0, r = 0, result = 0, cnt = 0; while(r < A.length) { if(A[r++] == 0) cnt++; while(cnt > K) { if(A[l] == 0) { cnt--; } l++; } result = Math.max(result, r-l); } return result; } }
-
1157. Online Majority Element In Subarray
1157. Online Majority Element In Subarray
Implementing the class MajorityChecker, which has the following API: MajorityChecker(int[] arr) constructs an instance of MajorityChecker with the given array arr; int query(int left, int right, int threshold) has arguments such that: 0 <= left <= right < arr.length representing a subarray of arr; 2 * threshold > right - left + 1, ie. the threshold is always a strict majority of the length of the subarray Each query(...) returns the element in arr[left], arr[left+1], ..., arr[right] that occurs at least threshold times, or -1 if no such element exists. Example: MajorityChecker majorityChecker = new MajorityChecker([1,1,2,2,1,1]); majorityChecker.query(0,5,4); // returns 1 majorityChecker.query(0,3,3); // returns -1 majorityChecker.query(2,3,2); // returns 2 Constraints: 1 <= arr.length <= 20000 1 <= arr[i] <= 20000 For each query, 0 <= left <= right < len(arr) For each query, 2 * threshold > right - left + 1 The number of queries is at most 10000
- Randomness and binarySearch
class MajorityChecker { HashMap<Integer, ArrayList<Integer>> map; Random r = new Random(); int[] arr; public MajorityChecker(int[] arr) { this.arr = arr; this.map = new HashMap<>(); for(int i = 0; i < arr.length; i++) { ArrayList<Integer> temp = map.getOrDefault(arr[i], new ArrayList<>()); temp.add(i); map.put(arr[i], temp); } } public int check(int left, int right, int threshold) { int index = left + this.r.nextInt(right-left+1); ArrayList<Integer> temp = map.get(arr[index]); int l = Collections.binarySearch(temp, left); int r = Collections.binarySearch(temp, right); if(l < 0) l = -l-1; if(r < 0) r = -r-2; if(r-l+1 < threshold) return -1; return arr[index]; } public int query(int left, int right, int threshold) { for(int i = 0; i < 30; i++) { int result = check(left, right, threshold); if(result != -1) return result; } return -1; } } /** * Your MajorityChecker object will be instantiated and called as such: * MajorityChecker obj = new MajorityChecker(arr); * int param_1 = obj.query(left,right,threshold); */
-
668. Kth Smallest Number in Multiplication Table
668. Kth Smallest Number in Multiplication Table
Nearly every one have used the Multiplication Table. But could you find out the k-th smallest number quickly from the multiplication table? Given the height m and the length n of a m * n Multiplication Table, and a positive integer k, you need to return the k-th smallest number in this table. Example 1: Input: m = 3, n = 3, k = 5 Output: Explanation: The Multiplication Table: 1 2 3 2 4 6 3 6 9 The 5-th smallest number is 3 (1, 2, 2, 3, 3). Example 2: Input: m = 2, n = 3, k = 6 Output: Explanation: The Multiplication Table: 1 2 3 2 4 6 The 6-th smallest number is 6 (1, 2, 2, 3, 4, 6). Note: The m and n will be in the range [1, 30000]. The k will be in the range [1, m * n]
- Binary Search
public int findKthNumber(int n, int m, int k) { int l = 1, r = n * m; while(l < r) { int mid = l + (r-l) / 2; int row = 1, col = n, cnt = 0; while(row <= m && col >= 1) { if(row * col <= mid) { cnt += col; row++; } else { col--; } } // System.out.println(l + " " + r + " m=" + mid + " cnt=" + cnt); if(cnt >= k) { r = mid; } else { l = mid+1; } } return l; }
- TLE PriorityQueue
public int findKthNumber2(int row, int col, int k) { int n = Math.max(row, col), m = Math.min(row, col); PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(a[0]*a[1]-b[0]*b[1])); for(int i = 1; i <= m; i++) { q.offer(new int[]{i,i}); } int cnt = 0; while(!q.isEmpty()) { int[] p = q.poll(); cnt++; if(p[0] != p[1]) { if(p[0] <= n && p[1] <= m) { cnt++; } } if(cnt >= k) { return p[0] * p[1]; } if(p[1]+1 <= n) q.offer(new int[]{p[0], p[1]+1}); } return -1; }
-
503. Next Greater Element II
Given a circular array (the next element of the last element is the first element of the array), print the Next Greater Number for every element. The Next Greater Number of a number x is the first greater number to its traversing-order next in the array, which means you could search circularly to find its next greater number. If it doesn't exist, output -1 for this number. Example 1: Input: [1,2,1] Output: [2,-1,2] Explanation: The first 1's next greater number is 2; The number 2 can't find next greater number; The second 1's next greater number needs to search circularly, which is also 2. Note: The length of given array won't exceed 10000.
- First Version with Stack
public int[] nextGreaterElements(int[] nums) { int[] arr = new int[nums.length * 2]; System.arraycopy(nums, 0, arr, 0, nums.length); System.arraycopy(nums, 0, arr, nums.length, nums.length); int[] result = new int[nums.length]; Arrays.fill(result, -1); LinkedList<Integer> q = new LinkedList<>(); for(int i = 0; i < arr.length; i++) { while(!q.isEmpty() && arr[i] > arr[q.peekLast()]) { int index = q.removeLast(); if(index < nums.length) result[index] = arr[i]; } q.offer(i); } return result; }
- Optimization on space
int[] result = new int[nums.length]; Arrays.fill(result, -1); LinkedList<Integer> q = new LinkedList<>(); for(int i = 0; i < nums.length * 2; i++) { while(!q.isEmpty() && nums[i % nums.length] > nums[q.peekLast()]) { result[q.removeLast()] = nums[i % nums.length]; } q.offer(i % nums.length); } return result; }
-
1231. Divide Chocolate
You have one chocolate bar that consists of some chunks. Each chunk has its own sweetness given by the array sweetness. You want to share the chocolate with your K friends so you start cutting the chocolate bar into K+1 pieces using K cuts, each piece consists of some consecutive chunks. Being generous, you will eat the piece with the minimum total sweetness and give the other pieces to your friends. Find the maximum total sweetness of the piece you can get by cutting the chocolate bar optimally. Example 1: Input: sweetness = [1,2,3,4,5,6,7,8,9], K = 5 Output: 6 Explanation: You can divide the chocolate to [1,2,3], [4,5], [6], [7], [8], [9] Example 2: Input: sweetness = [5,6,7,8,9,1,2,3,4], K = 8 Output: 1 Explanation: There is only one way to cut the bar into 9 pieces. Example 3: Input: sweetness = [1,2,2,1,2,2,1,2,2], K = 2 Output: 5 Explanation: You can divide the chocolate to [1,2,2], [1,2,2], [1,2,2] Constraints: 0 <= K < sweetness.length <= 10^4 1 <= sweetness[i] <= 10^5
- Binary Search
- the same with 410. Split Array Largest Sum which is to minimize the maximum
- this is to maximize the minimum
public int maximizeSweetness(int[] nums, int m) { m++; long l = Integer.MAX_VALUE, r = 0; for(int n : nums) { r += n; l = Math.min(l, n); } while(l+1 < r) { long mid = l + (r-l) / 2; if(canSplit(nums, m, mid)) { l = mid; } else { r = mid - 1; } } if(canSplit(nums, m, r)) return (int)r; else return (int)l; } public boolean canSplit(int[] nums, int m, long target) { long sum = 0, cnt = 0; for(int n : nums) { if(sum + n >= target) { sum = 0; cnt++; } else { sum += n; } } return cnt >= m; }
- Binary Search
-
1230. Toss Strange Coins
You have some coins. The i-th coin has a probability prob[i] of facing heads when tossed. Return the probability that the number of coins facing heads equals target if you toss every coin exactly once. Example 1: Input: prob = [0.4], target = 1 Output: 0.40000 Example 2: Input: prob = [0.5,0.5,0.5,0.5,0.5], target = 0 Output: 0.03125 Constraints: 1 <= prob.length <= 1000 0 <= prob[i] <= 1 0 <= target <= prob.length Answers will be accepted as correct if they are within 10^-5 of the correct answer.
- DP
- dp[i][j]: j coins heads up in the first i coins
public double probabilityOfHeads(double[] prob, int target) { double[][] dp = new double[prob.length+1][target+1]; dp[0][0] = 1.0; for(int i = 1; i < dp.length; i++) { for(int j = 0; j <= Math.min(target, i); j++) { dp[i][j] = (j == 0 ? 0 : dp[i-1][j-1]) * prob[i-1] + dp[i-1][j] * (1-prob[i-1]); } } return dp[dp.length-1][dp[0].length-1]; }
- DP
-
meeting-scheduler
Given the availability time slots arrays slots1 and slots2 of two people and a meeting duration duration, return the earliest time slot that works for both of them and is of duration duration. If there is no common time slot that satisfies the requirements, return an empty array. The format of a time slot is an array of two elements [start, end] representing an inclusive time range from start to end. It is guaranteed that no two availability slots of the same person intersect with each other. That is, for any two time slots [start1, end1] and [start2, end2] of the same person, either start1 > end2 or start2 > end1. Example 1: Input: slots1 = [[10,50],[60,120],[140,210]], slots2 = [[0,15],[60,70]], duration = 8 Output: [60,68] Example 2: Input: slots1 = [[10,50],[60,120],[140,210]], slots2 = [[0,15],[60,70]], duration = 12 Output: [] Constraints: 1 <= slots1.length, slots2.length <= 10^4 slots1[i].length, slots2[i].length == 2 slots1[i][0] < slots1[i][1] slots2[i][0] < slots2[i][1] 0 <= slots1[i][j], slots2[i][j] <= 10^9 1 <= duration <= 10^6
- Two pointers
public List<Integer> minAvailableDuration(int[][] slots1, int[][] slots2, int duration) { Arrays.sort(slots1, (a,b)->(a[0]-b[0])); Arrays.sort(slots2, (a,b)->(a[0]-b[0])); int i = 0, j = 0; while(i < slots1.length && j < slots2.length) { int[] s1 = slots1[i], s2 = slots2[j]; int l1 = s1[0], r1 = s1[1], l2 = s2[0], r2 = s2[1]; int d = Math.min(r1, r2) - Math.max(l1, l2); if(d >= duration) { return Arrays.asList(Math.max(l1, l2), Math.max(l1, l2)+duration); } if(r1 < r2) { i++; } else { j++; } } return Arrays.asList(); }
- Preprocessing and priorityqueue
public List<Integer> minAvailableDuration(int[][] slots1, int[][] slots2, int duration) { PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparing(a -> a[0])); for (int[] s : slots1) if (s[1] - s[0] >= duration) pq.offer(s); for (int[] s : slots2) if (s[1] - s[0] >= duration) pq.offer(s); while (pq.size() > 1) { if (pq.poll()[1] >= pq.peek()[0] + duration) return Arrays.asList(pq.peek()[0], pq.peek()[0] + duration); } return Arrays.asList(); }
-
1228. Missing Number In Arithmetic Progression
1228. Missing Number In Arithmetic Progression
In some array arr, the values were in arithmetic progression: the values arr[i+1] - arr[i] are all equal for every 0 <= i < arr.length - 1. Then, a value from arr was removed that was not the first or last value in the array. Return the removed value. Example 1: Input: arr = [5,7,11,13] Output: 9 Explanation: The previous array was [5,7,9,11,13]. Example 2: Input: arr = [15,13,12] Output: 14 Explanation: The previous array was [15,14,13,12]. Constraints: 3 <= arr.length <= 1000 0 <= arr[i] <= 10^5
public int missingNumber(int[] arr) { int l = 0, r = arr.length-1; while(l < r) { int mid = l + (r-l) / 2; double left = (double)Math.abs((arr[mid]-arr[0])) / (mid+1); double right = (double)Math.abs((arr[arr.length-1]-arr[mid])) / (arr.length-mid); if(left <= right) { l = mid+1; } else { r = mid; } } return (arr[r]+arr[r-1]) / 2; }
-
1227. Airplane Seat Assignment Probability
1227. Airplane Seat Assignment Probability
n passengers board an airplane with exactly n seats. The first passenger has lost the ticket and picks a seat randomly. But after that, the rest of passengers will: Take their own seat if it is still available, Pick other seats randomly when they find their seat occupied What is the probability that the n-th person can get his own seat? Example 1: Input: n = 1 Output: 1.00000 Explanation: The first person can only get the first seat. Example 2: Input: n = 2 Output: 0.50000 Explanation: The second person has a probability of 0.5 to get the second seat (when first person gets the first seat). Constraints: 1 <= n <= 10^5
- Subproblem O(N)
- f(n) will always be 0.5 when n >= 2
public double nthPersonGetsNthSeat(int n) { if(n == 1) return 1.0; return 1.0 / n + (double)(n-2) / n * nthPersonGetsNthSeat(n-1); }
- Subproblem O(N)
-
1192. Critical Connections in a Network
1192. Critical Connections in a Network
There are n servers numbered from 0 to n-1 connected by undirected server-to-server connections forming a network where connections[i] = [a, b] represents a connection between servers a and b. Any server can reach any other server directly or indirectly through the network. A critical connection is a connection that, if removed, will make some server unable to reach some other server. Return all critical connections in the network in any order. Example 1: Input: n = 4, connections = [[0,1],[1,2],[2,0],[1,3]] Output: [[1,3]] Explanation: [[3,1]] is also accepted. Constraints: 1 <= n <= 10^5 n-1 <= connections.length <= 10^5 connections[i][0] != connections[i][1] There are no repeated connections.
class Solution { List<List<Integer>> r; int[] id; int[] low; boolean[] onStack; Stack<Integer> stack; List<Integer>[] graph; int n; int index; public List<List<Integer>> criticalConnections(int n, List<List<Integer>> connections) { this.n = n; id = new int[n]; Arrays.fill(id, -1); low = new int[n]; onStack = new boolean[n]; stack = new Stack<>(); r = new ArrayList<>(); graph = new ArrayList[n]; for (int i = 0; i < n; i++) { graph[i] = new ArrayList<>(); } // build graph for (int i = 0; i < connections.size(); i++) { int from = connections.get(i).get(0), to = connections.get(i).get(1); graph[from].add(to); graph[to].add(from); } dfs(0, -1); return r; } public void dfs(int i, int prev) { stack.push(i); onStack[i] = true; low[i] = id[i] = index++; for(int j : graph[i]) { if(id[j] == -1) dfs(j, i); if(j != prev && onStack[j]) low[i] = Math.min(low[i], low[j]); } // find a strongly connected part if(low[i] == id[i]) { while(true) { int p = stack.pop(); onStack[p] = false; if(p == i) break; // end of current subgraph } if(prev!= -1) { r.add(Arrays.asList(i, prev)); } } } }
-
1224. Maximum Equal Frequency
Given an array nums of positive integers, return the longest possible length of an array prefix of nums, such that it is possible to remove exactly one element from this prefix so that every number that has appeared in it will have the same number of occurrences. If after removing one element there are no remaining elements, it's still considered that every appeared number has the same number of ocurrences (0). Example 1: Input: nums = [2,2,1,1,5,3,3,5] Output: 7 Explanation: For the subarray [2,2,1,1,5,3,3] of length 7, if we remove nums[4]=5, we will get [2,2,1,1,3,3], so that each number will appear exactly twice. Example 2: Input: nums = [1,1,1,2,2,2,3,3,3,4,4,4,5] Output: 13 Example 3: Input: nums = [1,1,1,2,2,2] Output: 5 Example 4: Input: nums = [10,2,8,9,3,8,1,5,2,3,7,6] Output: 8 Constraints: 2 <= nums.length <= 10^5 1 <= nums[i] <= 10^5
- O(N)
- cnt: the count of each element in the array
- frq: the requency of each count
- 3 cases: 1112223334 && 1112223333 && 12345678
class Solution { public int maxEqualFreq(int[] nums) { int[] cnt = new int[100001], frq = new int[100001]; int maxCnt = 0; int result = 0; for(int i = 0; i < nums.length; i++) { int v = nums[i]; cnt[v]++; frq[cnt[v]-1]--; frq[cnt[v]]++; maxCnt = Math.max(maxCnt, cnt[v]); if(maxCnt * frq[maxCnt] == i || (maxCnt-1) * (frq[maxCnt-1]+1) == i || maxCnt == 1) result = i+1; } return result; } }
- O(N)
-
1223. Dice Roll Simulation
A die simulator generates a random number from 1 to 6 for each roll. You introduced a constraint to the generator such that it cannot roll the number i more than rollMax[i] (1-indexed) consecutive times. Given an array of integers rollMax and an integer n, return the number of distinct sequences that can be obtained with exact n rolls. Two sequences are considered different if at least one element differs from each other. Since the answer may be too large, return it modulo 10^9 + 7. Example 1: Input: n = 2, rollMax = [1,1,2,2,2,3] Output: 34 Explanation: There will be 2 rolls of die, if there are no constraints on the die, there are 6 * 6 = 36 possible combinations. In this case, looking at rollMax array, the numbers 1 and 2 appear at most once consecutively, therefore sequences (1,1) and (2,2) cannot occur, so the final answer is 36-2 = 34. Example 2: Input: n = 2, rollMax = [1,1,1,1,1,1] Output: 30 Example 3: Input: n = 3, rollMax = [1,1,1,2,2,3] Output: 181 Constraints: 1 <= n <= 5000 rollMax.length == 6 1 <= rollMax[i] <= 15
- DynamicProgramming
- state:
- n: n rolls remaining
- index: the value of the last index of the rolled numbers
- k: the number of consecutive index in the rolled numbers including the last one
- state:
class Solution { int[][][] memo; int[] rollMax; int MOD = 1000000007; public int dieSimulator(int n, int[] rollMax) { this.memo = new int[n+1][7][16]; this.rollMax = rollMax; int result = 0; for(int i = 0; i < 6; i++) { result += dfs(n-1, i, 1); result %= MOD; } return result % MOD; } public int dfs(int n, int index, int k) { if(n == 0) return 1; if(memo[n][index][k] != 0) return memo[n][index][k]; int result = 0; for(int i = 0; i < 6; i++) { if(i == index) { if(rollMax[i] > k) { result += dfs(n-1, i, k+1); result %= MOD; } } else { result += dfs(n-1, i, 1); result %= MOD; } } memo[n][index][k] = result % MOD; return memo[n][index][k]; } }
- DynamicProgramming
-
1206. Design Skiplist
Design a Skiplist without using any built-in libraries. A Skiplist is a data structure that takes O(log(n)) time to add, erase and search. Comparing with treap and red-black tree which has the same function and performance, the code length of Skiplist can be comparatively short and the idea behind Skiplists are just simple linked lists. For example: we have a Skiplist containing [30,40,50,60,70,90] and we want to add 80 and 45 into it. The Skiplist works this way: Artyom Kalinin [CC BY-SA 3.0], via Wikimedia Commons You can see there are many layers in the Skiplist. Each layer is a sorted linked list. With the help of the top layers, add , erase and search can be faster than O(n). It can be proven that the average time complexity for each operation is O(log(n)) and space complexity is O(n). To be specific, your design should include these functions: bool search(int target) : Return whether the target exists in the Skiplist or not. void add(int num): Insert a value into the SkipList. bool erase(int num): Remove a value in the Skiplist. If num does not exist in the Skiplist, do nothing and return false. If there exists multiple num values, removing any one of them is fine. See more about Skiplist : https://en.wikipedia.org/wiki/Skip_list Note that duplicates may exist in the Skiplist, your code needs to handle this situation. Example: Skiplist skiplist = new Skiplist(); skiplist.add(1); skiplist.add(2); skiplist.add(3); skiplist.search(0); // return false. skiplist.add(4); skiplist.search(1); // return true. skiplist.erase(0); // return false, 0 is not in skiplist. skiplist.erase(1); // return true. skiplist.search(1); // return false, 1 has already been erased. Constraints: 0 <= num, target <= 20000 At most 50000 calls will be made to search, add, and erase.
- Naive design
- make sure the first layer is empty before adding; that is to keep at least two layers anytime
- use stack to trace the position to add or remove nodes
class Skiplist { Node root; Random r = new Random(); public Skiplist() { root = new Node(-1); root.low = new Node(-1); } public boolean search(int target) { Node prev = root, curr = root.next; while(true) { if(curr == null || curr.val >= target) { if(curr != null && curr.val == target) return true; if(prev.low == null) return false; prev = prev.low; if(prev.val == -1) curr = prev.next; else curr = prev; } else if(curr.val < target) { prev = curr; curr = curr.next; } } } public boolean flipCoin() { int i = r.nextInt(2); return i == 1; } public void add(int num) { if(root.next != null) { Node nr = new Node(-1); nr.low = root; root = nr; } Stack<Node> stack = new Stack<>(); Node curr = root.next, prev = root; while(true) { if(curr == null || curr.val >= num) { if(prev.low == null) break; stack.push(prev); prev = prev.low; if(prev.val == -1) { curr = prev.next; } else { curr = prev; } } else { prev = curr; curr = curr.next; } } Node n = new Node(num); n.next = curr; prev.next = n; Node low = n; while(!stack.isEmpty()) { if(flipCoin()) { prev = stack.pop(); n = new Node(num); n.next = prev.next; prev.next = n; n.low = low; low = n; } else { break; } } // print(); // System.out.println("_________above Add " + num + " ___________"); } public void print() { Node head = root; while(head != null) { Node curr = head; while(curr != null) { System.out.print(curr.val + "->"); curr = curr.next; } System.out.println(); head = head.low; } } public boolean erase(int num) { Stack<Node> stack = new Stack<>(); Node prev = root, curr = root.next; while(true) { if(curr == null || curr.val >= num) { if(prev.low == null) break; stack.push(prev); prev = prev.low; if(prev.val == -1) curr = prev.next; else curr = prev; } else if(curr.val < num) { prev = curr; curr = curr.next; } } if(curr == null || curr.val != num) return false; prev.next = curr.next; while(!stack.isEmpty()) { prev = stack.pop(); curr = prev.next; // System.out.println(prev.val); if(curr == null || curr.val != num) break; prev.next = curr.next; } while(root.next == null && root.low.next == null && root.low.low != null) { root = root.low; } // print(); // System.out.println("_________above Erase " + num + " ___________"); return true; } class Node { int val; Node next; Node low; public Node(int val) { this.val = val; } public String toString() { return "" + val; } } } /** * Your Skiplist object will be instantiated and called as such: * Skiplist obj = new Skiplist(); * boolean param_1 = obj.search(target); * obj.add(num); * boolean param_3 = obj.erase(num); */
- Naive design
-
1216. Valid Palindrome III
Given a string s and an integer k, find out if the given string is a K-Palindrome or not. A string is K-Palindrome if it can be transformed into a palindrome by removing at most k characters from it. Example 1: Input: s = "abcdeca", k = 2 Output: true Explanation: Remove 'b' and 'e' characters. Constraints: 1 <= s.length <= 1000 s has only lowercase English letters. 1 <= k <= s.length
- Least Common Sequece of s and the reverse of s is the longest palindrome of s.
class Solution { public boolean isValidPalindrome(String s, int k) { String r = new StringBuilder(s).reverse().toString(); int lcs = lcs(s, r); return lcs >= s.length() - k; } public int lcs(String s1, String s2) { int[][] dp = new int[s1.length()+1][s2.length()+1]; for(int i = 1; i < dp.length; i++) { for(int j = 1; j < dp[0].length; j++) { if(s1.charAt(i-1) == s2.charAt(j-1)) { dp[i][j] = dp[i-1][j-1] + 1; } else { dp[i][j] = Math.max(dp[i-1][j], dp[i][j-1]); } } } return dp[dp.length-1][dp[0].length-1]; } }
-
1215. Stepping Numbers
A Stepping Number is an integer such that all of its adjacent digits have an absolute difference of exactly . For example, 321 is a Stepping Number while 421 is not. Given two integers low and high, find and return a sorted list of all the Stepping Numbers in the range [low, high] inclusive. Example 1: Input: low = 0, high = 21 Output: [0,1,2,3,4,5,6,7,8,9,10,12,21] Constraints: 0 <= low <= high <= 2 * 10^9
- BFS
class Solution { public List<Integer> countSteppingNumbers(int low, int high) { ArrayList<Integer> result = new ArrayList<>(); if(low == 0) result.add(low); Queue<Integer> q = new LinkedList<>(); for(int i = 1; i <= 9; i++) q.offer(i); while(!q.isEmpty()) { int size = q.size(); for(int k = 0; k < size; k++) { int n = q.poll(); if(n >= low && n <= high) result.add(n); if((long)n * 10 > high) continue; int last = n % 10; if(last > 0) q.offer(n * 10 + last-1); if(last < 9) q.offer(n * 10 + last+1); } } return result; } }
-
1074. Number of Submatrices That Sum to Target
1074. Number of Submatrices That Sum to Target
Given a matrix, and a target, return the number of non-empty submatrices that sum to target. A submatrix x1, y1, x2, y2 is the set of all cells matrix[x][y] with x1 <= x <= x2 and y1 <= y <= y2. Two submatrices (x1, y1, x2, y2) and (x1', y1', x2', y2') are different if they have some coordinate that is different: for example, if x1 != x1'. Example 1: Input: matrix = [[0,1,0],[1,1,1],[0,1,0]], target = 0 Output: 4 Explanation: The four 1x1 submatrices that only contain 0. Example 2: Input: matrix = [[1,-1],[-1,1]], target = 0 Output: 5 Explanation: The two 1x2 submatrices, plus the two 2x1 submatrices, plus the 2x2 submatrix. Note: 1 <= matrix.length <= 300 1 <= matrix[0].length <= 300 -1000 <= matrix[i] <= 1000 -10^8 <= target <= 10^8
- N^3
- dimesion compress to 1D
- count with hashmap and prefix sum
class Solution { public int numSubmatrixSumTarget(int[][] matrix, int target) { int[] arr = new int[matrix[0].length]; int result = 0; for(int i = 0; i < matrix.length; i++) { arr = matrix[i].clone(); result += count(arr, target); for(int j = i+1; j < matrix.length; j++) { for(int p = 0; p < matrix[0].length; p++) { arr[p] += matrix[j][p]; } result += count(arr, target); } } return result; } public int count(int[] arr, int k) { int result = 0, sum = 0;; HashMap<Integer, Integer> map = new HashMap<>(); map.put(0, 1); for(int i = 0; i < arr.length; i++) { sum += arr[i]; int v = sum - k; if(map.containsKey(v)) { result += map.get(v); } map.put(sum, map.getOrDefault(sum, 0) + 1); } return result; } }
- N^3
-
1218. Longest Arithmetic Subsequence of Given Difference
1218. Longest Arithmetic Subsequence of Given Difference
Given an integer array arr and an integer difference, return the length of the longest subsequence in arr which is an arithmetic sequence such that the difference between adjacent elements in the subsequence equals difference. Example 1: Input: arr = [1,2,3,4], difference = 1 Output: 4 Explanation: The longest arithmetic subsequence is [1,2,3,4]. Example 2: Input: arr = [1,3,5,7], difference = 1 Output: 1 Explanation: The longest arithmetic subsequence is any single element. Example 3: Input: arr = [1,5,7,8,5,3,4,2,1], difference = -2 Output: 4 Explanation: The longest arithmetic subsequence is [7,5,3,1].
- HashMap < value, length of valid sequence ends at this value>
class Solution { public int longestSubsequence(int[] arr, int d) { int result = 1; HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < arr.length; i++) { if(map.containsKey(arr[i]-d)) { map.put(arr[i], map.get(arr[i]-d)+1); } else { map.put(arr[i], 1); } result = Math.max(result, map.get(arr[i])); } return result; } }
-
363. Max Sum of Rectangle No Larger Than K
363. Max Sum of Rectangle No Larger Than K
Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix such that its sum is no larger than k. Example: Input: matrix = [[1,0,1],[0,-2,3]], k = 2 Output: 2 Explanation: Because the sum of rectangle [[0, 1], [-2, 3]] is 2, and 2 is the max number no larger than k (k = 2). Note: The rectangle inside the matrix must have an area > 0. What if the number of rows is much larger than the number of columns?
- N^3 * log(N)
- Using treeset to store prefix sum
public int maxSumSubmatrix(int[][] matrix, int k) { int[] arr = new int[matrix[0].length]; int result = Integer.MIN_VALUE; for(int i = 0; i < matrix.length; i++) { arr = matrix[i].clone(); int temp = count(arr, k); for(int j = i+1; j < matrix.length; j++) { for(int p = 0; p < matrix[0].length; p++) { arr[p] += matrix[j][p]; } temp = Math.max(temp, count(arr, k)); } result = Math.max(result, temp); } return result; } public int count(int[] arr, int k) { int result = Integer.MIN_VALUE, sum = 0;; TreeSet<Integer> set = new TreeSet<>(); set.add(0); for(int i = 0; i < arr.length; i++) { sum += arr[i]; int v = sum - k; Integer candidate = set.ceiling(v); if(candidate != null) result = Math.max(result, sum - candidate); set.add(sum); } return result; }
- N^3 * log(N)
-
1220. Count Vowels Permutation
1220. Count Vowels Permutation
Given an integer n, your task is to count how many strings of length n can be formed under the following rules: Each character is a lower case vowel ('a', 'e', 'i', 'o', 'u') Each vowel 'a' may only be followed by an 'e'. Each vowel 'e' may only be followed by an 'a' or an 'i'. Each vowel 'i' may not be followed by another 'i'. Each vowel 'o' may only be followed by an 'i' or a 'u'. Each vowel 'u' may only be followed by an 'a'. Since the answer may be too large, return it modulo 10^9 + 7. Example 1: Input: n = 1 Output: 5 Explanation: All possible strings are: "a", "e", "i" , "o" and "u". Example 2: Input: n = 2 Output: 10 Explanation: All possible strings are: "ae", "ea", "ei", "ia", "ie", "io", "iu", "oi", "ou" and "ua". Example 3: Input: n = 5 Output: 68 Constraints: 1 <= n <= 2 * 10^4
- DP
class Solution { public static int MOD = 1000000007; public int countVowelPermutation(int n) { long[] arr = new long[5]; // 0 -> 4 : 'a' -> 'u' // similar to 935. Knight Dialer Arrays.fill(arr, 1); for(int i = 2; i <= n; i++) { long[] temp = new long[5]; for(int j = 0; j < 5; j++) { long v = arr[j]; if(j == 0) temp[1] += v; if(j == 1) { temp[0] += v; temp[2] += v; } if(j == 2) { for(int k = 0; k < 5; k++) if(k != 2) temp[k] += v; } if(j == 3) { temp[2] += v; temp[4] += v; } if(j == 4) temp[0] += v; } for(int j = 0; j < 5; j++) { temp[j] %= MOD; } arr = temp; } long result = 0; for(long i : arr) { result += i; result %= MOD; } return (int)result; } }
-
80. Remove Duplicates from Sorted Array II
80. Remove Duplicates from Sorted Array II
Given a sorted array nums, remove the duplicates in-place such that duplicates appeared at most twice and return the new length. Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory. Example 1: Given nums = [1,1,1,2,2,3], Your function should return length = 5, with the first five elements of nums being 1, 1, 2, 2 and 3 respectively. It doesn't matter what you leave beyond the returned length. Example 2: Given nums = [0,0,1,1,1,1,2,3,3], Your function should return length = 7, with the first seven elements of nums being modified to 0, 0, 1, 1, 2, 3 and 3 respectively. It doesn't matter what values are set beyond the returned length. Clarification: Confused why the returned value is an integer but your answer is an array? Note that the input array is passed in by reference, which means modification to the input array will be known to the caller as well. Internally you can think of this: // nums is passed in by reference. (i.e., without making a copy) int len = removeDuplicates(nums); // any modification to nums in your function would be known by the caller. // using the length returned by your function, it prints the first len elements. for (int i = 0; i < len; i++) { print(nums[i]); }
- Inplace
public int removeDuplicates(int[] nums) { if(nums.length == 0) return 0; int l = 0, r = 0, curr = nums[0], cnt = 0; while(r < nums.length) { if(curr == nums[r]) { cnt++; } else { curr = nums[r]; cnt = 1; } if(cnt <= 2) { nums[l++] = nums[r]; } r++; } return l; }
-
722. Remove Comments
Given a C++ program, remove comments from it. The program source is an array where source[i] is the i-th line of the source code. This represents the result of splitting the original source code string by the newline character \n. In C++, there are two types of comments, line comments, and block comments. The string // denotes a line comment, which represents that it and rest of the characters to the right of it in the same line should be ignored. The string /* denotes a block comment, which represents that all characters until the next (non- overlapping) occurrence of */ should be ignored. (Here, occurrences happen in reading order: line by line from left to right.) To be clear, the string /*/ does not yet end the block comment, as the ending would be overlapping the beginning. The first effective comment takes precedence over others: if the string // occurs in a block comment, it is ignored. Similarly, if the string /* occurs in a line or block comment, it is also ignored. If a certain line of code is empty after removing comments, you must not output that line: each string in the answer list will be non-empty. There will be no control characters, single quote, or double quote characters. For example, source = "string s = "/* Not a comment. */";" will not be a test case. (Also, nothing else such as defines or macros will interfere with the comments.) It is guaranteed that every open block comment will eventually be closed, so /* outside of a line or block comment always starts a new comment. Finally, implicit newline characters can be deleted by block comments. Please see the examples below for details. After removing the comments from the source code, return the source code in the same format. Example 1: Input: source = ["/*Test program */", "int main()", "{ ", " // variable declaration ", "int a, b, c;", "/* This is a test", " multiline ", " comment for ", " testing */", "a = b + c;", "}"] The line by line code is visualized as below: /*Test program */ int main() { // variable declaration int a, b, c; /* This is a test multiline comment for testing */ a = b + c; } Output: ["int main()","{ "," ","int a, b, c;","a = b + c;","}"] The line by line code is visualized as below: int main() { int a, b, c; a = b + c; } Explanation: The string /* denotes a block comment, including line 1 and lines 6-9. The string // denotes line 4 as comments. Example 2: Input: source = ["a/*comment", "line", "more_comment*/b"] Output: ["ab"] Explanation: The original source string is "a/*comment\nline\nmore_comment*/b", where we have bolded the newline characters. After deletion, the implicit newline characters are deleted, leaving the string "ab", which when delimited by newline characters becomes ["ab"]. Note: The length of source is in the range [1, 100]. The length of source[i] is in the range [0, 80]. Every open block comment is eventually closed. There are no single-quote, double-quote, or control characters in the source code.
- Stack
class Solution { public List<String> removeComments(String[] source) { boolean line = true, lc = false, bc = false, newLine = true;; List<String> result = new ArrayList<>(); for(int i = 0; i < source.length; i++) { if(lc) { lc = false; line = true; } String s = source[i]; StringBuilder sb = new StringBuilder(); for(int j = 0; j < s.length(); j++) { boolean startLC = j+1 < s.length() && s.charAt(j) == '/' && s.charAt(j+1) == '/'; boolean startBC = j+1 < s.length() && s.charAt(j) == '/' && s.charAt(j+1) == '*'; boolean endBC = j+1 < s.length() && s.charAt(j) == '*' && s.charAt(j+1) == '/'; if(line) { if(startLC) { line = false; lc = true; j++; } else if(startBC) { line = false; bc = true; j++; } else { sb.append(s.charAt(j)); } } else if(bc && endBC) { line = true; bc = false; j++; } } if(sb.length() > 0) { if(newLine) result.add(sb.toString()); else { String prefix = result.remove(result.size()-1); result.add(prefix + sb.toString()); } } newLine = !bc; } return result; } }
-
402. Remove K Digits
Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible. Note: The length of num is less than 10002 and will be ≥ k. The given num does not contain any leading zero. Example 1: Input: num = "1432219", k = 3 Output: "1219" Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. Example 2: Input: num = "10200", k = 1 Output: "200" Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. Example 3: Input: num = "10", k = 2 Output: "0" Explanation: Remove all the digits from the number and it is left with nothing which is 0.
- Stack
public String removeKdigits(String num, int k) { if(k == num.length()) return "0"; LinkedList<Character> stack = new LinkedList<>(); int cnt = 0; for(int i = 0; i < num.length(); i++) { char c = num.charAt(i); while(cnt < k && !stack.isEmpty() && stack.peek() > c) { stack.pop(); cnt++; } stack.push(c); } while(cnt < k) { stack.removeFirst(); cnt++; } StringBuilder result = new StringBuilder(); for(char c : stack) result.insert(0, c); int j = 0; while(j < result.length() && result.charAt(j) == '0') j++; if(j == result.length()) return "0"; return result.toString().substring(j); }
-
1092. Shortest Common Supersequence
1092. Shortest Common Supersequence
Given two strings str1 and str2, return the shortest string that has both str1 and str2 as subsequences. If multiple answers exist, you may return any of them. (A string S is a subsequence of string T if deleting some number of characters from T (possibly 0, and the characters are chosen anywhere from T) results in the string S.) Example 1: Input: str1 = "abac", str2 = "cab" Output: "cabac" Explanation: str1 = "abac" is a subsequence of "cabac" because we can delete the first "c". str2 = "cab" is a subsequence of "cabac" because we can delete the last "ac". The answer provided is the shortest such string that satisfies these properties. Note: 1 <= str1.length, str2.length <= 1000 str1 and str2 consist of lowercase English letters.
- find the LCS
String[][] memo; public String shortestCommonSupersequence(String s1, String s2) { int n = s1.length(), m = s2.length(); memo = new String[n][m]; String lcs = lcs(s1, s2); StringBuilder sb = new StringBuilder(); int i = 0, j = 0, k = 0; while(i < n || j < m) { while(i < n && (k >= lcs.length() || s1.charAt(i) != lcs.charAt(k))) sb.append(s1.charAt(i++)); while(j < m && (k >= lcs.length() || s2.charAt(j) != lcs.charAt(k))) sb.append(s2.charAt(j++)); if(k < lcs.length()) sb.append(lcs.charAt(k++)); i++; j++; } return sb.toString(); } public String lcs(String s1, String s2) { int i = s1.length()-1, j = s2.length()-1; if(i == -1 || j == -1) return ""; if(memo[i][j] != null) return memo[i][j]; char c1 = s1.charAt(i), c2 = s2.charAt(j); String result = null; if(c1 == c2) { result = lcs(s1.substring(0, i), s2.substring(0, j)) + c1; } else { String sub1 = lcs(s1.substring(0, i), s2); String sub2 = lcs(s1, s2.substring(0, j)); result = sub1.length() > sub2.length() ? sub1 : sub2; } memo[i][j] = result; return result; }
-
1131. Maximum of Absolute Value Expression
1131. Maximum of Absolute Value Expression
Given two arrays of integers with equal lengths, return the maximum value of: |arr1[i] - arr1[j]| + |arr2[i] - arr2[j]| + |i - j| where the maximum is taken over all 0 <= i, j < arr1.length. Example 1: Input: arr1 = [1,2,3,4], arr2 = [-1,4,5,6] Output: 13 Example 2: Input: arr1 = [1,-2,-5,0,10], arr2 = [0,-2,-1,-7,-4] Output: 20 Constraints: 2 <= arr1.length == arr2.length <= 40000 -10^6 <= arr1[i], arr2[i] <= 10^6
- Break the absolute expression
public int maxAbsValExpr(int[] arr1, int[] arr2) { int[] arr = new int[arr1.length]; int result = 0; for(int k = 0; k < 4; k++) { for(int i = 0; i < arr.length; i++) { switch(k) { case 0 : arr[i] = arr1[i] + arr2[i] - i; break; case 1 : arr[i] = arr1[i] - arr2[i] - i; break; case 2 : arr[i] = -arr1[i] + arr2[i] - i; break; case 3 : arr[i] = -arr1[i] - arr2[i] - i; break; } } result = Math.max(result, maxDist(arr)); } return result; } public int maxDist(int[] arr) { int result = 0, max = arr[0]; for(int i = 1; i < arr.length; i++) { result = Math.max(result, max - arr[i]); max = Math.max(max, arr[i]); } return result; }
-
1059. All Paths from Source Lead to Destination
1059. All Paths from Source Lead to Destination
Given the edges of a directed graph, and two nodes source and destination of this graph, determine whether or not all paths starting from source eventually end at destination, that is: At least one path exists from the source node to the destination node If a path exists from the source node to a node with no outgoing edges, then that node is equal to destination. The number of possible paths from source to destination is a finite number. Return true if and only if all roads from source lead to destination. Example 1: Input: n = 3, edges = [[0,1],[0,2]], source = 0, destination = 2 Output: false Explanation: It is possible to reach and get stuck on both node 1 and node 2. Example 2: Input: n = 4, edges = [[0,1],[0,3],[1,2],[2,1]], source = 0, destination = 3 Output: false Explanation: We have two possibilities: to end at node 3, or to loop over node 1 and node 2 indefinitely. Example 3: Input: n = 4, edges = [[0,1],[0,2],[1,3],[2,3]], source = 0, destination = 3 Output: true Example 4: Input: n = 3, edges = [[0,1],[1,1],[1,2]], source = 0, destination = 2 Output: false Explanation: All paths from the source node end at the destination node, but there are an infinite number of paths, such as 0-1-2, 0-1-1-2, 0-1-1-1-2, 0-1-1-1-1-2, and so on. Example 5: Input: n = 2, edges = [[0,1],[1,1]], source = 0, destination = 1 Output: false Explanation: There is infinite self-loop at destination node. Note: The given graph may have self loops and parallel edges. The number of nodes n in the graph is between 1 and 10000 The number of edges in the graph is between 0 and 10000 0 <= edges.length <= 10000 edges[i].length == 2 0 <= source <= n - 1 0 <= destination <= n - 1
- DFS
class Solution { int src, dst; boolean flag; public boolean leadsToDestination(int n, int[][] edges, int source, int destination) { this.src = source; this.dst = destination; this.flag = true; ArrayList<Integer>[] graph = new ArrayList[n]; HashSet<Integer>[] used = new HashSet[n]; boolean[] visited = new boolean[n]; for(int i = 0; i < n; i++) { graph[i] = new ArrayList<>(); used[i] = new HashSet<>(); } for(int[] e : edges) { int i = e[0], j = e[1]; graph[i].add(j); } if(graph[this.dst].size() > 0) return false; boolean result = dfs(graph, used, visited, this.src); return result; } public boolean dfs(ArrayList<Integer>[] graph, HashSet<Integer>[] used, boolean[] visited, int curr) { // System.out.println(curr); if(visited[curr] && curr != this.dst) { // circle return false; } visited[curr] = true; if(graph[curr].size() == used[curr].size()) { // check destination return curr == this.dst; } for(int next : graph[curr]) { if(used[curr].contains(next)) continue; used[curr].add(next); if(!dfs(graph, used, visited, next)) { return false; } used[curr].remove(next); } visited[curr] = false; return true; } }
-
616. Add Bold Tag in String
Given a string s and a list of strings dict, you need to add a closed pair of bold tag <b> and </b> to wrap the substrings in s that exist in dict. If two such substrings overlap, you need to wrap them together by only one pair of closed bold tag. Also, if two substrings wrapped by bold tags are consecutive, you need to combine them. Example 1: Input: s = "abcxyz123" dict = ["abc","123"] Output: "<b>abc</b>xyz<b>123</b>" Example 2: Input: s = "aaabbcc" dict = ["aaa","aab","bc"] Output: "<b>aaabbc</b>c" Note: The given dict won't contain duplicates, and its length won't exceed 100. All the strings in input have length in range [1, 1000].
- Merge Interval
public String addBoldTag(String input, String[] dict) { HashSet<String> set = new HashSet<>(); int max = 0; for(String s : dict) { set.add(s); max = Math.max(max, s.length()); } ArrayList<int[]> arr = new ArrayList<>(); for(int i = 0; i < input.length(); i++) { for(int j = Math.min(i + max, input.length()); j > i; j--) { String candidate = input.substring(i, j); if(set.contains(candidate)) { if(arr.size() > 0 && arr.get(arr.size()-1)[1] >= i-1) { arr.get(arr.size()-1)[1] = Math.max(j-1, arr.get(arr.size()-1)[1]); } else { arr.add(new int[]{i, j-1}); } break; } } } StringBuilder sb = new StringBuilder(); int j = 0; for(int i = 0; i < input.length(); i++) { if(j < arr.size() && i == arr.get(j)[0]) sb.append("<b>"); sb.append(input.charAt(i)); if(j < arr.size() && i == arr.get(j)[1]) { sb.append("</b>"); j++; } } return sb.toString(); }
-
1208. Get Equal Substrings Within Budget
1208. Get Equal Substrings Within Budget
You are given two strings s and t of the same length. You want to change s to t. Changing the i-th character of s to i-th character of t costs |s[i] - t[i]| that is, the absolute difference between the ASCII values of the characters. You are also given an integer maxCost. Return the maximum length of a substring of s that can be changed to be the same as the corresponding substring of twith a cost less than or equal to maxCost. If there is no substring from s that can be changed to its corresponding substring from t, return 0. Example 1: Input: s = "abcd", t = "bcdf", cost = 3 Output: 3 Explanation: "abc" of s can change to "bcd". That costs 3, so the maximum length is 3. Example 2: Input: s = "abcd", t = "cdef", cost = 3 Output: 1 Explanation: Each charactor in s costs 2 to change to charactor in t, so the maximum length is 1. Example 3: Input: s = "abcd", t = "acde", cost = 0 Output: 1 Explanation: You can't make any change, so the maximum length is 1. Constraints: 1 <= s.length, t.length <= 10^5 0 <= maxCost <= 10^6 s and t only contain lower case English letters.
- sliding window
class Solution { public int equalSubstring(String s, String t, int maxCost) { int result = 0; int[] arr = new int[s.length()]; for(int i = 0; i < s.length(); i++) arr[i] = Math.abs(s.charAt(i)-t.charAt(i)); int sum = 0, l = 0, r = 0; while(r < arr.length) { if(sum <= maxCost) { result = Math.max(result, r-l); sum += arr[r]; r++; } else { sum -= arr[l]; l++; } } if(sum <= maxCost) result = Math.max(result, r-l); return result; } }
-
1210. Minimum Moves to Reach Target with Rotations
1210. Minimum Moves to Reach Target with Rotations
In an n*n grid, there is a snake that spans 2 cells and starts moving from the top left corner at (0, 0) and (0, 1). The grid has empty cells represented by zeros and blocked cells represented by ones. The snake wants to reach the lower right corner at (n-1, n-2) and (n-1, n-1). In one move the snake can: Move one cell to the right if there are no blocked cells there. This move keeps the horizontal/ vertical position of the snake as it is. Move down one cell if there are no blocked cells there. This move keeps the horizontal/vertical position of the snake as it is. Rotate clockwise if it's in a horizontal position and the two cells under it are both empty. In that case the snake moves from (r, c) and (r, c+1) to (r, c) and (r+1, c). Rotate counterclockwise if it's in a vertical position and the two cells to its right are both empty. In that case the snake moves from (r, c) and (r+1, c) to (r, c) and (r, c+1). Return the minimum number of moves to reach the target. If there is no way to reach the target, return -1. Example 1: Input: grid = [[0,0,0,0,0,1], [1,1,0,0,1,0], [0,0,0,0,1,1], [0,0,1,0,1,0], [0,1,1,0,0,0], [0,1,1,0,0,0]] Output: 11 Explanation: One possible solution is [right, right, rotate clockwise, right, down, down, down, down, rotate counterclockwise, right, down]. Example 2: Input: grid = [[0,0,1,1,1,1], [0,0,0,0,1,1], [1,1,0,0,0,1], [1,1,1,0,0,1], [1,1,1,0,0,1], [1,1,1,0,0,0]] Output: 9 Constraints: 2 <= n <= 100 0 <= grid[i][j] <= 1 It is guaranteed that the snake starts at empty cells.
- BFS
class Solution { public int minimumMoves(int[][] grid) { int n = grid.length; LinkedList<Integer> q = new LinkedList<>(); HashSet<Integer> visited = new HashSet<>(); q.offer(getKey(0, 0, 0)); int level = 0, target = getKey(n-1, n-2, 0); while(!q.isEmpty()) { int size = q.size(); for(int p = 0; p < size; p++) { int num = q.poll(); if(visited.contains(num)) continue; visited.add(num); if(num == target) return level; int i = num / 1000, j = (num-i*1000) / 10, d = num & 1; if(d == 0) { // horizontal if((j+2 < n) && grid[i][j+2] == 0) // move forward (right) q.offer(getKey(i, j+1, 0)); if(i+1 < n && j+1 < n && grid[i+1][j] == 0 && grid[i+1][j+1] == 0) { q.offer(getKey(i, j, 1)); // rotate q.offer(getKey(i+1, j, 0)); // move down entirely } } else { // vertical if(i+2 < n && grid[i+2][j] == 0) // move forward (down) q.offer(getKey(i+1, j, 1)); if(i+1 < n && j+1 < n && grid[i][j+1] == 0 && grid[i+1][j+1] == 0) { q.offer(getKey(i, j, 0)); // rotate q.offer(getKey(i, j+1, 1)); // move right entirely } } } level++; } return -1; } // 0 <= i , j <= 99 public int getKey(int i, int j, int d) { return i * 1000 + j * 10 + d; } }
-
1209. Remove All Adjacent Duplicates in String II
1209. Remove All Adjacent Duplicates in String II
Given a string s, a k duplicate removal consists of choosing k adjacent and equal letters from s and removing them causing the left and the right side of the deleted substring to concatenate together. We repeatedly make k duplicate removals on s until we no longer can. Return the final string after all such duplicate removals have been made. It is guaranteed that the answer is unique. Example 1: Input: s = "abcd", k = 2 Output: "abcd" Explanation: There's nothing to delete. Example 2: Input: s = "deeedbbcccbdaa", k = 3 Output: "aa" Explanation: First delete "eee" and "ccc", get "ddbbbdaa" Then delete "bbb", get "dddaa" Finally delete "ddd", get "aa" Example 3: Input: s = "pbbcggttciiippooaais", k = 2 Output: "ps" Constraints: 1 <= s.length <= 10^5 2 <= k <= 10^4 s only contains lower case English letters.
- Stack
public String removeDuplicates(String s, int k) { Stack<int[]> stack = new Stack<>(); for(char c : s.toCharArray()) { if(!stack.isEmpty() && stack.peek()[0] == c) stack.peek()[1]++; else { stack.push(new int[]{c, 1}); if(stack.peek()[1] == k) stack.pop(); } StringBuilder sb = new StringBuilder(); while(!stack.isEmpty()) { int[] pair = stack.pop(); for(int i = 0; i < pair[1]; i++) sb.insert(0, (char)pair[0]); } return sb.toString(); }
- Recursion
- remove repeats every pass
class Solution { public String removeDuplicates(String s, int k) { return remove(s, k); } public String remove(String s, int k) { // System.out.println(s); if(s.length() < k) return s; int cnt = 1; boolean flag = false; int l = 0; ArrayList<int[]> arr = new ArrayList<>(); for(int i = 1; i < s.length(); i++) { if(s.charAt(i) != s.charAt(i-1)) { cnt = 1; l = i; } else { cnt++; } if(cnt == k) { arr.add(new int[]{l,i}); flag = true; l = i+1; i++; cnt = 1; } } if(!flag) return s; StringBuilder sb = new StringBuilder(); sb.append(s.substring(0, arr.get(0)[0])); for(int i = 1; i < arr.size(); i++) { sb.append(s.substring(arr.get(i-1)[1]+1, arr.get(i)[0])); } sb.append(s.substring(arr.get(arr.size()-1)[1]+1)); return remove(sb.toString(), k); } }
-
871. Minimum Number of Refueling Stops
871. Minimum Number of Refueling Stops
A car travels from a starting position to a destination which is target miles east of the starting position. Along the way, there are gas stations. Each station[i] represents a gas station that is station[i][0] miles east of the starting position, and has station[i][1] liters of gas. The car starts with an infinite tank of gas, which initially has startFuel liters of fuel in it. It uses 1 liter of gas per 1 mile that it drives. When the car reaches a gas station, it may stop and refuel, transferring all the gas from the station into the car. What is the least number of refueling stops the car must make in order to reach its destination? If it cannot reach the destination, return -1. Note that if the car reaches a gas station with 0 fuel left, the car can still refuel there. If the car reaches the destination with 0 fuel left, it is still considered to have arrived. Example 1: Input: target = 1, startFuel = 1, stations = [] Output: 0 Explanation: We can reach the target without refueling. Example 2: Input: target = 100, startFuel = 1, stations = [[10,100]] Output: -1 Explanation: We can't reach the target (or even the first gas station). Example 3: Input: target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]] Output: 2 Explanation: We start with 10 liters of fuel. We drive to position 10, expending 10 liters of fuel. We refuel from 0 liters to 60 liters of gas. Then, we drive from position 10 to position 60 (expending 50 liters of fuel), and refuel from 10 liters to 50 liters of gas. We then drive to and reach the target. We made 2 refueling stops along the way, so we return 2. Note: 1 <= target, startFuel, stations[i][1] <= 10^9 0 <= stations.length <= 500 0 < stations[0][0] < stations[1][0] < ... < stations[stations.length-1][0] < target
- PriorityQueue greedy
- when current fuel < 0, try to fuel the car with the largest station before the current station
public int minRefuelStops(int target, int startFuel, int[][] stations) { if(stations.length == 0) return startFuel >= target ? 0 : -1; PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->(b-a)); int result = 0, fuel = startFuel; for(int i = 0; i < stations.length; i++) { fuel -= stations[i][0] - (i-1 >= 0 ? stations[i-1][0] : 0); while(fuel < 0) { if(q.isEmpty()) return -1; fuel += q.poll(); result++; } q.offer(stations[i][1]); } fuel -= target - stations[stations.length-1][0]; while(fuel < 0) { if(q.isEmpty()) return -1; fuel += q.poll(); result++; } return result; }
- PriorityQueue greedy
-
1032. Stream of Characters
Implement the StreamChecker class as follows: StreamChecker(words): Constructor, init the data structure with the given words. query(letter): returns true if and only if for some k >= 1, the last k characters queried (in order from oldest to newest, including this letter just queried) spell one of the words in the given list. Example: StreamChecker streamChecker = new StreamChecker(["cd","f","kl"]); // init the dictionary. streamChecker.query('a'); // return false streamChecker.query('b'); // return false streamChecker.query('c'); // return false streamChecker.query('d'); // return true, because 'cd' is in the wordlist streamChecker.query('e'); // return false streamChecker.query('f'); // return true, because 'f' is in the wordlist streamChecker.query('g'); // return false streamChecker.query('h'); // return false streamChecker.query('i'); // return false streamChecker.query('j'); // return false streamChecker.query('k'); // return false streamChecker.query('l'); // return true, because 'kl' is in the wordlist Note: 1 <= words.length <= 2000 1 <= words[i].length <= 2000 Words will only consist of lowercase English letters. Queries will only consist of lowercase English letters. The number of queries is at most 40000.
- Trie
- keep a collection of candidate Tier nodes
class StreamChecker { int max; ArrayList<Node> arr = new ArrayList<>(); Node root; public StreamChecker(String[] words) { root = new Node(); max = 0; for(String s : words) { max = Math.max(max, s.length()); Node n = root; for(char c : s.toCharArray()) { if(n.arr[c-'a'] == null) n.arr[c-'a'] = new Node(); n = n.arr[c-'a']; } n.isWord = true; } } public boolean query(char c) { boolean result = false; ArrayList<Node> temp = new ArrayList<>(); arr.add(root); for(Node n : arr) { if(n.arr[c-'a'] == null) continue; temp.add(n.arr[c-'a']); if(n.arr[c-'a'].isWord) result = true; } arr = temp; return result; } class Node { Node[] arr = new Node[26]; boolean isWord; } }
- Trie
-
366. Find Leaves of Binary Tree
366. Find Leaves of Binary Tree
Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty. Example: Input: [1,2,3,4,5] 1 / \ 2 3 / \ 4 5 Output: [[4,5,3],[2],[1]] Explanation: 1. Removing the leaves [4,5,3] would result in this tree: 1 / 2 2. Now removing the leaf [2] would result in this tree: 1 3. Now removing the leaf [1] would result in the empty tree: []
- post order traverse
- record the height of the current subtree
class Solution { public List<List<Integer>> findLeaves(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); getH(result, root); return result; } public int getH(List<List<Integer>> result, TreeNode root) { if(root == null) return 0; int l = getH(result, root.left); int r = getH(result, root.right); int h = Math.max(l, r) + 1; if(h > result.size()) result.add(new ArrayList<>()); result.get(h-1).add(root.val); return h; } }
- post order traverse
-
895. Maximum Frequency Stack
Implement FreqStack, a class which simulates the operation of a stack-like data structure. FreqStack has two functions: push(int x), which pushes an integer x onto the stack. pop(), which removes and returns the most frequent element in the stack. If there is a tie for most frequent element, the element closest to the top of the stack is removed and returned. Example 1: Input: ["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"], [[],[5],[7],[5],[7],[4],[5],[],[],[],[]] Output: [null,null,null,null,null,null,null,5,7,5,4] Explanation: After making six .push operations, the stack is [5,7,5,7,4,5] from bottom to top. Then: pop() -> returns 5, as 5 is the most frequent. The stack becomes [5,7,5,7,4]. pop() -> returns 7, as 5 and 7 is the most frequent, but 7 is closest to the top. The stack becomes [5,7,5,4]. pop() -> returns 5. The stack becomes [5,7,4]. pop() -> returns 4. The stack becomes [5,7]. Note: Calls to FreqStack.push(int x) will be such that 0 <= x <= 10^9. It is guaranteed that FreqStack.pop() won't be called if the stack has zero elements. The total number of FreqStack.push calls will not exceed 10000 in a single test case. The total number of FreqStack.pop calls will not exceed 10000 in a single test case. The total number of FreqStack.push and FreqStack.pop calls will not exceed 150000 across all test cases.
- O(N)
- duplicate at each freqency
- When increase freqency for an element:
- do not remove the element in the current stack;
- keep it and add another copy to the next stack
- by doing this we can keep the relative adding order of the elements
class FreqStack { int high; HashMap<Integer, Stack<Integer>> f2k; HashMap<Integer, Integer> k2f; public FreqStack() { f2k = new HashMap<>(); k2f = new HashMap<>(); high = 0; } public void push(int x) { int f = k2f.getOrDefault(x, 0); k2f.put(x, ++f); Stack<Integer> s = f2k.getOrDefault(f, new Stack<>()); s.push(x); f2k.put(f, s); high = Math.max(high, f); } public int pop() { int result = f2k.get(high).pop(); if(k2f.get(result) == 1) k2f.remove(result); else k2f.put(result, k2f.get(result)-1); if(f2k.get(high).isEmpty()) { f2k.remove(high); high--; } return result; } }
- Pass on first submit
- similar to LFC & All in One data structure
class FreqStack { int timestamp = 0; int high = 0; HashMap<Integer, PriorityQueue<Node>> f2k = new HashMap<>(); // freqency -> keys HashMap<Integer, Node> map = new HashMap<>(); public FreqStack() { } public void push(int x) { Node n = map.getOrDefault(x, new Node(x, timestamp++)); if(map.containsKey(x)) n.add(timestamp++); map.put(x, n); f2k.getOrDefault(n.size()-1, new PriorityQueue<>()).remove(n); PriorityQueue<Node> q = f2k.getOrDefault(n.size(), new PriorityQueue<>()); q.offer(n); f2k.put(n.size(), q); high = Math.max(high, n.size()); } public int pop() { PriorityQueue<Node> q = f2k.get(high); Node n = q.poll(); if(q.isEmpty()) { f2k.remove(high); high--; } int result = n.key; n.reduce(); if(n.isEmpty()) map.remove(n.key); else { PriorityQueue<Node> lowQ = f2k.getOrDefault(n.size(), new PriorityQueue<>()); lowQ.offer(n); f2k.put(n.size(), lowQ); } return result; } class Node implements Comparable<Node>{ int key; ArrayList<Integer> times = new ArrayList<>(); public Node(int k, int t) { key = k; times.add(t); } public void add(int t) { times.add(t); } public void reduce() { times.remove(times.size()-1); } public int size() { return times.size(); } public boolean isEmpty() { return times.isEmpty(); } private int getLatest() { return times.get(times.size()-1); } @Override public int compareTo(Node n) { return n.getLatest() - this.getLatest(); } public String toString() { return "" + key + "->" + times; } } } /** * Your FreqStack object will be instantiated and called as such: * FreqStack obj = new FreqStack(); * obj.push(x); * int param_2 = obj.pop(); */
- O(N)
-
99. Recover Binary Search Tree
99. Recover Binary Search Tree
Two elements of a binary search tree (BST) are swapped by mistake. Recover the tree without changing its structure. Example 1: Input: [1,3,null,null,2] 1 / 3 \ 2 Output: [3,1,null,null,2] 3 / 1 \ 2 Example 2: Input: [3,1,4,null,null,2] 3 / \ 1 4 / 2 Output: [2,1,4,null,null,3] 2 / \ 1 4 / 3 Follow up: A solution using O(n) space is pretty straight forward. Could you devise a constant space solution?
- O(1) Space
- inorder traverse twice(l-root-r && r-root-l) to find x and y
- correct the swaped elements
class Solution { TreeNode pre; Integer x, y; boolean hasX, hasY; public void recoverTree(TreeNode root) { ascending(root); pre = null; descending(root); traverse(root, x, y); } public void descending(TreeNode root) { if(y != null) return; if(root == null) return; descending(root.right); if(pre != null && pre.val < root.val) { y = pre.val; return; } pre = root; descending(root.left); } public void ascending(TreeNode root) { if(x != null) return; if(root == null) return; ascending(root.left); if(pre != null && pre.val > root.val) { x = pre.val; return; } pre = root; ascending(root.right); } public void traverse(TreeNode root, int x, int y) { if(hasX && hasY) return; if(root == null) return; if(root.val == x) { root.val = y; hasX = true; } else if(root.val == y) { root.val = x; hasY = true; } traverse(root.left, x, y); traverse(root.right, x, y); } }
- Naive O(N)
- inorder traverse the tree
- find two swaped elements in the array
- correct the swaped elements
public void recoverTree(TreeNode root) { ArrayList<Integer> arr = new ArrayList<>(); traverse(root, arr); int x = 0, y = 0; // two misplaced elements for(int i = 0; i < arr.size(); i++) { if(i+1 < arr.size() && arr.get(i) > arr.get(i+1)) { x = arr.get(i); break; } } for(int i = arr.size()-1; i >= 0; i--) { if(i-1 >= 0 && arr.get(i) < arr.get(i-1)) { y = arr.get(i); break; } } traverse(root, x, y); } public void traverse(TreeNode root, int x, int y) { if(root == null) return; if(root.val == x) root.val = y; else if(root.val == y) root.val = x; traverse(root.left, x, y); traverse(root.right, x, y); } public void traverse(TreeNode root, ArrayList<Integer> arr) { if(root == null) return; traverse(root.left, arr); arr.add(root.val); traverse(root.right, arr); }
- O(1) Space
-
229. Majority Element II
Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. Note: The algorithm should run in linear time and in O(1) space. Example 1: Input: [3,2,3] Output: [3] Example 2: Input: [1,1,1,3,3,2,2,2] Output: [1,2]
- Boyer-Moore Majority Vote algorithm
- n1 and n2 are not necessarily the valid results
- the second pass check their validity
public List<Integer> majorityElement(int[] nums) { int cnt1 = 0, cnt2 = 0, n1 = 0, n2 = 0; for(int i : nums) { if(n1 == i) cnt1++; else if(n2 == i) cnt2++; else if(cnt1 == 0) { cnt1 = 1; n1 = i; } else if(cnt2 == 0) { cnt2 = 1; n2 = i; } else { cnt1--; cnt2--; } } cnt1 = 0; cnt2 = 0; for(int i : nums) { if(i == n1) cnt1++; if(i == n2) cnt2++; } List<Integer> result = new ArrayList<>(); if(cnt1 > nums.length/3) result.add(n1); if(n1 != n2 && cnt2 > nums.length/3) result.add(n2); return result; }
- Boyer-Moore Majority Vote algorithm
-
807. Max Increase to Keep City Skyline
807. Max Increase to Keep City Skyline
In a 2 dimensional array grid, each value grid[i][j] represents the height of a building located there. We are allowed to increase the height of any number of buildings, by any amount (the amounts can be different for different buildings). Height 0 is considered to be a building as well. At the end, the "skyline" when viewed from all four directions of the grid, i.e. top, bottom, left, and right, must be the same as the skyline of the original grid. A city's skyline is the outer contour of the rectangles formed by all the buildings when viewed from a distance. See the following example. What is the maximum total sum that the height of the buildings can be increased? Example: Input: grid = [[3,0,8,4],[2,4,5,7],[9,2,6,3],[0,3,1,0]] Output: 35 Explanation: The grid is: [ [3, 0, 8, 4], [2, 4, 5, 7], [9, 2, 6, 3], [0, 3, 1, 0] ] The skyline viewed from top or bottom is: [9, 4, 8, 7] The skyline viewed from left or right is: [8, 7, 9, 3] The grid after increasing the height of buildings without affecting skylines is: gridNew = [ [8, 4, 8, 7], [7, 4, 7, 7], [9, 4, 8, 7], [3, 3, 3, 3] ] Notes: 1 < grid.length = grid[0].length <= 50. All heights grid[i][j] are in the range [0, 100]. All buildings in grid[i][j] occupy the entire grid cell: that is, they are a 1 x 1 x grid[i][j] rectangular prism.
- Record row max and col max
- easier than Medium
public int maxIncreaseKeepingSkyline(int[][] grid) { int[] maxRow = new int[grid.length], maxCol = new int[grid[0].length]; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { maxRow[i] = Math.max(maxRow[i], grid[i][j]); maxCol[j] = Math.max(maxCol[j], grid[i][j]); } } int result = 0; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { result += Math.min(maxRow[i], maxCol[j])-grid[i][j]; } } return result; }
- Record row max and col max
-
419. Battleships in a Board
Given an 2D board, count how many battleships are in it. The battleships are represented with 'X's, empty slots are represented with '.'s. You may assume the following rules: You receive a valid board, made of only battleships or empty slots. Battleships can only be placed horizontally or vertically. In other words, they can only be made of the shape 1xN (1 row, N columns) or Nx1 (N rows, 1 column), where N can be of any size. At least one horizontal or vertical cell separates between two battleships - there are no adjacent battleships. Example: X..X ...X ...X In the above board there are 2 battleships. Invalid Example: ...X XXXX ...X This is an invalid board that you will not receive - as battleships will always have a cell separating between them. Follow up: Could you do it in one-pass, using only O(1) extra memory and without modifying the value of the board?
- Follow up
- easier than Medium
- count the # of horizontal consecutive X –> result
- for each vertical ship of length N –> result -= (N-1) // minus overcount vertical ships ```java
public int countBattleships(char[][] board) { int result = 0; for(int i = 0; i < board.length; i++) { int cnt = 0; boolean hasX = false; for(int j = 0; j < board[0].length; j++) { if(board[i][j] == ‘X’) { hasX = true; if(i+1 < board.length && board[i+1][j] == ‘X’) result–; } else { if(hasX) result++; hasX = false; } } if(hasX) result++; } return result; }
* Modify boards ```java public int countBattleshipsModifyBoard(char[][] board) { int result = 0; for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { if(board[i][j] == 'X') { result++; for(int x = i; x >= 0 && board[x][j] == 'X'; x--) board[x][j] = '.'; for(int x = i+1; x < board.length && board[x][j] == 'X'; x++) board[x][j] = '.'; for(int y = j; y >= 0 && board[i][y] == 'X'; y--) board[i][y] = '.'; for(int y = j+1; y < board[0].length && board[i][y] == 'X'; y++) board[i][y] = '.'; } } } return result; }
- Follow up
-
228. Summary Ranges
Given a sorted integer array without duplicates, return the summary of its ranges. Example 1: Input: [0,1,2,4,5,7] Output: ["0->2","4->5","7"] Explanation: 0,1,2 form a continuous range; 4,5 form a continuous range. Example 2: Input: [0,2,3,4,6,8,9] Output: ["0","2->4","6","8->9"] Explanation: 2,3,4 form a continuous range; 8,9 form a continuous range.
- O(N^2)
- key: after sort, if k is the smallest index to the right of j to let nums[i]+nums[j] <= nums[k] and nums[i]+nums[j] > nums[k-1] then nums[i] + nums[j+1] > nums[k-1] so for each inner loop, k will monotonically increase which reduce a level of loop for k
public int triangleNumber(int[] nums) { Arrays.sort(nums); int result = 0, prev = -1; for(int i = 0; i < nums.length-2; i++) { if(nums[i] == 0) continue; int k = i+2; // define k out of inner loop for(int j = i+1; j < nums.length-1; j++) { while(k < nums.length && nums[k] < nums[i] + nums[j]) k++; result += k - j - 1; } } return result; }
- simple binary search
public int triangleNumberBinarySearch(int[] nums) { Arrays.sort(nums); int result = 0, prev = -1; for(int i = 0; i < nums.length-2; i++) { for(int j = i+1; j < nums.length-1; j++) { int target = nums[i] + nums[j]; int index = search(nums, j+1, target); result += index - j - 1; } } return result; } public int search(int[] nums, int l, int target) { int r = nums.length; while(l < r) { int mid = l + (r-l) / 2; if(nums[mid] < target) { l = mid+1; } else { r = mid; } } return l; }
- O(N^2)
-
316. Remove Duplicate Letters
Given a string which contains only lowercase letters, remove duplicate letters so that every letter appears once and only once. You must make sure your result is the smallest in lexicographical order among all possible results. Example 1: Input: "bcabc" Output: "abc" Example 2: Input: "cbacdcbc" Output: "acdb"
- Using stack
- easier than Hard
public String removeDuplicateLetters(String s) { int[] cnt = new int[26]; for(char c : s.toCharArray()) cnt[c-'a']++; HashSet<Character> set = new HashSet<>(); Stack<Character> stack = new Stack<>(); for(char c : s.toCharArray()) { if(!set.contains(c)) { while(!stack.isEmpty() && c < stack.peek() && cnt[stack.peek()-'a'] > 0) { set.remove(stack.pop()); } stack.push(c); set.add(c); } cnt[c-'a']--; } StringBuilder sb = new StringBuilder(); for(char c : stack) sb.append(c); return sb.toString(); }
- Using stack
-
780. Reaching Points
A move consists of taking a point (x, y) and transforming it to either (x, x+y) or (x+y, y). Given a starting point (sx, sy) and a target point (tx, ty), return True if and only if a sequence of moves exists to transform the point (sx, sy) to (tx, ty). Otherwise, return False. Examples: Input: sx = 1, sy = 1, tx = 3, ty = 5 Output: True Explanation: One series of moves that transforms the starting point to the target is: (1, 1) -> (1, 2) (1, 2) -> (3, 2) (3, 2) -> (3, 5) Input: sx = 1, sy = 1, tx = 2, ty = 2 Output: False Input: sx = 1, sy = 1, tx = 1, ty = 1 Output: True Note: sx, sy, tx, ty will all be integers in the range [1, 10^9].
- A better way to shrink…….oh my math.
Say tx > ty. We know that the next parent operations will be to subtract ty from tx, until such time that tx = tx % ty. When both tx > ty and ty > sy, we can perform all these parent operations in one step, replacing while tx > ty: tx -= ty with tx %= ty.
Otherwise, if say tx > ty and ty <= sy, then we know ty will not be changing (it can only decrease). Thus, only tx will change, and it can only change by subtracting by ty. Hence, (tx - sx) % ty == 0 is a necessary and sufficient condition for the problem’s answer to be True.
class Solution { public boolean reachingPoints(int sx, int sy, int tx, int ty) { while (tx >= sx && ty >= sy) { if (tx == ty) break; if (tx > ty) { if (ty > sy) tx %= ty; // equal to reduce tx until tx < ty else return (tx - sx) % ty == 0; // ty == sy; equal to reduce tx to see if finally tx == sx } else { if (tx > sx) ty %= tx; else return (ty - sy) % tx == 0; // tx = sx } } return (tx == sx && ty == sy); } }
- Binary Search
- shrink the search scope with binary search ```java
class Solution { public boolean reachingPoints(int sx, int sy, int tx, int ty) { if(tx + ty < sx + sy) return false;
int x = tx, y = ty; int sum1 = sx + sy; while(x + y >= sx + sy) { if(x == sx && y == sy) return true; int[] arr = shrink(sum1, x, y); // shrink x or y // x still be greater than y if x > y before shrink // and vice versa x = arr[0]; y = arr[1]; if(x == sx && y == sy) return true; if(x == y) { if(x == sx && sy == 0 || sx == 0 && sy == y) return true; else return false; } else if(x > y) { x = x - y; } else { y = y - x; } } } public int[] shrink(int sum1, int x, int y) { int small = Math.min(x, y); int large = Math.max(x, y); int l = 1, r = (large / small)-1; while(l < r) { int mid = l + (r-l) / 2; // large-mid*small is always greater than small int sum = large + small - mid * small; if(sum < sum1) { r = mid-1; } else if(sum >= sum1) { if(mid == l) { // ugly... r = l; break; } l = mid; } } if(x > y) { x = large - r * small; } else { y = large - r * small; } return new int[]{x, y}; } } ```
-
736. Parse Lisp Expression
You are given a string expression representing a Lisp-like expression to return the integer value of. The syntax for these expressions is given as follows. An expression is either an integer, a let-expression, an add-expression, a mult-expression, or an assigned variable. Expressions always evaluate to a single integer. (An integer could be positive or negative.) A let-expression takes the form (let v1 e1 v2 e2 ... vn en expr), where let is always the string "let", then there are 1 or more pairs of alternating variables and expressions, meaning that the first variable v1 is assigned the value of the expression e1, the second variable v2 is assigned the value of the expression e2, and so on sequentially; and then the value of this let-expression is the value of the expression expr. An add-expression takes the form (add e1 e2) where add is always the string "add", there are always two expressions e1, e2, and this expression evaluates to the addition of the evaluation of e1 and the evaluation of e2. A mult-expression takes the form (mult e1 e2) where mult is always the string "mult", there are always two expressions e1, e2, and this expression evaluates to the multiplication of the evaluation of e1 and the evaluation of e2. For the purposes of this question, we will use a smaller subset of variable names. A variable starts with a lowercase letter, then zero or more lowercase letters or digits. Additionally for your convenience, the names "add", "let", or "mult" are protected and will never be used as variable names. Finally, there is the concept of scope. When an expression of a variable name is evaluated, within the context of that evaluation, the innermost scope (in terms of parentheses) is checked first for the value of that variable, and then outer scopes are checked sequentially. It is guaranteed that every expression is legal. Please see the examples for more details on scope. Evaluation Examples: Input: (add 1 2) Output: 3 Input: (mult 3 (add 2 3)) Output: 15 Input: (let x 2 (mult x 5)) Output: 10 Input: (let x 2 (mult x (let x 3 y 4 (add x y)))) Output: 14 Explanation: In the expression (add x y), when checking for the value of the variable x, we check from the innermost scope to the outermost in the context of the variable we are trying to evaluate. Since x = 3 is found first, the value of x is 3. Input: (let x 3 x 2 x) Output: 2 Explanation: Assignment in let statements is processed sequentially. Input: (let x 1 y 2 x (add x y) (add x y)) Output: 5 Explanation: The first (add x y) evaluates as 3, and is assigned to x. The second (add x y) evaluates as 3+2 = 5. Input: (let x 2 (add (let x 3 (let x 4 x)) x)) Output: 6 Explanation: Even though (let x 4 x) has a deeper scope, it is outside the context of the final x in the add-expression. That final x will equal 2. Input: (let a1 3 b2 (add a1 1) b2) Output 4 Explanation: Variable names can contain digits after the first character. Note: The given string expression is well formatted: There are no leading or trailing spaces, there is only a single space separating different components of the string, and no space between adjacent parentheses. The expression is guaranteed to be legal and evaluate to an integer. The length of expression is at most 2000. (It is also non-empty, as that would not be a legal expression.) The answer and all intermediate calculations of that answer are guaranteed to fit in a 32-bit integer.
- recursion
- end recursion when s is a variable or number
- Otherwise read the operation and sub expression and go to next recursion
class Solution { ArrayList<HashMap<String, Integer>> arr = new ArrayList<>(); public int evaluate(String s) { // end of recursion Integer num = getNum(s); if(num != null) // a number return num; if(!s.contains("(")) // a variable return get(s); // flatten brackets int result = 0; HashMap<String, Integer> map = new HashMap<>(); arr.add(map); s = s.substring(1, s.length()-1); if(s.substring(0,3).equals("add") || s.substring(0,4).equals("mult")) { int firstSpace = s.indexOf(" "); int l = 0, r = 0, i = firstSpace+1; while(i < s.length()) { if(l == r && s.charAt(i) == ' ') break; if(s.charAt(i) == '(') l++; if(s.charAt(i) == ')') r++; i++; } int secondSpace = i; String p1 = s.substring(firstSpace+1, secondSpace); String p2 = s.substring(secondSpace+1); if(s.substring(0,3).equals("add")) { result = evaluate(p1) + evaluate(p2); } else { result = evaluate(p1) * evaluate(p2); } } else { // let int start = s.indexOf(" ")+1; String key = ""; int l = 0, r = 0, i = start, cnt = 0; while(i < s.length()) { if(l == r && s.charAt(i) == ' ') { cnt++; if((cnt & 1) == 1) { key = s.substring(start, i); } else { int value = evaluate(s.substring(start, i)); map.put(key, value); } start = i+1; } if(s.charAt(i) == '(') l++; if(s.charAt(i) == ')') r++; i++; } result = evaluate(s.substring(start)); } arr.remove(arr.size()-1); return result; } public Integer getNum(String s) { boolean isNum = true; int num = 0, start = 0; int sign = 1; if(s.charAt(0) == '-') { sign = -1; start = 1; } for(int i = start; i < s.length(); i++) { char c = s.charAt(i); if(Character.isDigit(c)) num = num * 10 + c - '0'; else { isNum = false; break; } } if(isNum) return sign * num; else return null; } public int get(String key) { for(int i = arr.size()-1; i >= 0; i--) { if(arr.get(i).containsKey(key)) return arr.get(i).get(key); } return 0; } }
- recursion
-
1031. Maximum Sum of Two Non-Overlapping Subarrays
1031. Maximum Sum of Two Non-Overlapping Subarrays
Given an array A of non-negative integers, return the maximum sum of elements in two non-overlapping (contiguous) subarrays, which have lengths L and M. (For clarification, the L-length subarray could occur before or after the M-length subarray.) Formally, return the largest V for which V = (A[i] + A[i+1] + ... + A[i+L-1]) + (A[j] + A[j+1] + ... + A[j+M-1]) and either: 0 <= i < i + L - 1 < j < j + M - 1 < A.length, or 0 <= j < j + M - 1 < i < i + L - 1 < A.length. Example 1: Input: A = [0,6,5,2,2,5,1,9,4], L = 1, M = 2 Output: 20 Explanation: One choice of subarrays is [9] with length 1, and [6,5] with length 2. Example 2: Input: A = [3,8,1,3,2,1,8,9,0], L = 3, M = 2 Output: 29 Explanation: One choice of subarrays is [3,8,1] with length 3, and [8,9] with length 2. Example 3: Input: A = [2,1,5,6,0,9,5,0,3,8], L = 4, M = 3 Output: 31 Explanation: One choice of subarrays is [5,6,0,9] with length 4, and [3,8] with length 3. Note: L >= 1 M >= 1 L + M <= A.length <= 1000 0 <= A[i] <= 1000
- O(N)
- cache the largest array in A[0:j] and A[i:] with two arrays
public int maxSumTwoNoOverlap(int[] A, int L, int M) { int[] prefix = new int[A.length+1]; for(int i = 1; i < prefix.length; i++) prefix[i] = A[i-1] + prefix[i-1]; int[] dp1 = new int[A.length]; // Largest subarray with length M in A[0:i] int[] dp2 = new int[A.length]; // Largest subarray with length M in A[i:] int sum = prefix[M] - prefix[0]; dp1[M-1] = sum; for(int i = M; i < A.length; i++) { sum += (A[i]-A[i-M]); dp1[i] = Math.max(sum, dp1[i-1]); } sum = prefix[A.length] - prefix[A.length-M]; dp2[A.length-M] = sum; for(int i = A.length-M-1; i >= 0; i--) { sum += (A[i]-A[i+M]); dp2[i] = Math.max(sum, dp2[i+1]); } int result = 0; for(int i = 0; i + L - 1 < A.length; i++) { int j = i + L - 1; int sumL = prefix[j+1] - prefix[i]; int sumM = Math.max(i>1 ? dp1[i-1] : 0, j+1 < A.length ? dp2[j+1] : 0); result = Math.max(result, sumM + sumL); } return result; }
- Brutal Force
public int maxSumTwoNoOverlap(int[] A, int L, int M) { int[] prefix = new int[A.length+1]; for(int i = 1; i < prefix.length; i++) prefix[i] = A[i-1] + prefix[i-1]; int result = 0; for(int i = 0; i + L - 1 < A.length; i++) { int j = i + L - 1; int sumL = prefix[j+1] - prefix[i]; int sumM = Integer.MIN_VALUE; for(int l = 0; l + M - 1 < i; l++) { int r = l + M - 1; sumM = Math.max(sumM, prefix[r+1]-prefix[l]); } for(int l = j+1; l + M - 1 < A.length; l++) { int r = l + M - 1; sumM = Math.max(sumM, prefix[r+1]-prefix[l]); } result = Math.max(result, sumM + sumL); } return result; }
- O(N)
-
877. Stone Game
Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties. Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins. Assuming Alex and Lee play optimally, return True if and only if Alex wins the game. Example 1: Input: [5,3,4,5] Output: true Explanation: Alex starts first, and can only take the first 5 or the last 5. Say he takes the first 5, so that the row becomes [3, 4, 5]. If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points. If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points. This demonstrated that taking the first 5 was a winning move for Alex, so we return true. Note: 2 <= piles.length <= 500 piles.length is even. 1 <= piles[i] <= 500 sum(piles) is odd.
- dp top down
- total sum minus the amount that the other person has to take when following the optimal rule
- the # the other person can take changes when the current person takes differnt # of piles
- for loop try all possibilities, and find a min value
- the result is Total sum - min value ```java
class Solution { int[][] dp; int[] suffix; public int stoneGameII(int[] piles) { dp = new int[piles.length][32]; suffix = new int[piles.length]; suffix[suffix.length-1] = piles[suffix.length-1]; for(int i = suffix.length-2; i >= 0; i–) suffix[i] = piles[i] + suffix[i+1]; return backtrack(piles, 1, 0); }
public int backtrack(int[] piles, int m, int j) { if(piles.length-j <= 2 * m) return suffix[j]; // take all if(dp[j][m] != 0) return dp[j][m]; // cached int result = Integer.MAX_VALUE; for(int x = 1; x <= 2 * m && x+j-1 < piles.length; x++) result = Math.min(result, backtrack(piles, Math.max(x,m), j+x)); dp[j][m] = suffix[j] - result; // total sum of [j:] subtract the min value the other // person must take when following the optimal rule return dp[j][m]; } } ```
- dp top down
-
877. Stone Game
Alex and Lee play a game with piles of stones. There are an even number of piles arranged in a row, and each pile has a positive integer number of stones piles[i]. The objective of the game is to end with the most stones. The total number of stones is odd, so there are no ties. Alex and Lee take turns, with Alex starting first. Each turn, a player takes the entire pile of stones from either the beginning or the end of the row. This continues until there are no more piles left, at which point the person with the most stones wins. Assuming Alex and Lee play optimally, return True if and only if Alex wins the game. Example 1: Input: [5,3,4,5] Output: true Explanation: Alex starts first, and can only take the first 5 or the last 5. Say he takes the first 5, so that the row becomes [3, 4, 5]. If Lee takes 3, then the board is [4, 5], and Alex takes 5 to win with 10 points. If Lee takes the last 5, then the board is [3, 4], and Alex takes 4 to win with 9 points. This demonstrated that taking the first 5 was a winning move for Alex, so we return true. Note: 2 <= piles.length <= 500 piles.length is even. 1 <= piles[i] <= 500 sum(piles) is odd.
- expand window
- dp[i][j] : the difference of substracting Lee’s stone from Alex’s in range [i,j].
- dp[0][arr.length-1] is positive when Alex has more stone than Lee in the end.
public boolean stoneGame(int[] nums) { int[][] dp = new int[nums.length][nums.length]; int flag = (nums.length - 1) & 1; // Alex's turn int sum = 0; for(int i = 0; i < nums.length; i++) { sum += nums[i]; dp[i][i] = -nums[i]; // Lee is always to pick the last stone because the # of arr is even } for(int w = 2; w <= nums.length; w++) { for(int i = 0; i+w <= nums.length; i++) { int j = i+w-1; if(((j-i) & 1) == flag) // Alex's turn dp[i][j] = Math.max(nums[i]+dp[i+1][j], nums[j]+dp[i][j-1]); else // Lee's turn dp[i][j] = Math.max(-nums[i]+dp[i+1][j], -nums[j]+dp[i][j-1]); } } return dp[0][nums.length-1] > 0; }
- expand window
-
1049. Last Stone Weight II
We have a collection of rocks, each rock has a positive integer weight. Each turn, we choose any two rocks and smash them together. Suppose the stones have weights x and y with x <= y. The result of this smash is: If x == y, both stones are totally destroyed; If x != y, the stone of weight x is totally destroyed, and the stone of weight y has new weight y-x. At the end, there is at most 1 stone left. Return the smallest possible weight of this stone (the weight is 0 if there are no stones left.) Example 1: Input: [2,7,4,1,8,1] Output: 1 Explanation: We can combine 2 and 4 to get 2 so the array converts to [2,7,1,8,1] then, we can combine 7 and 8 to get 1 so the array converts to [2,1,1,1] then, we can combine 2 and 1 to get 1 so the array converts to [1,1,1] then, we can combine 1 and 1 to get 0 so the array converts to [1] then that's the optimal value. Note: 1 <= stones.length <= 30 1 <= stones[i] <= 100
- 01 Knapsack problem
- cost array == value array
- capacity == sum / 2
public int lastStoneWeightII(int[] stones) { int sum = 0; for(int i : stones) sum += i; int V = sum / 2; int[] dp = new int[V+1]; for(int stone : stones) for(int j = V; j >= stone; j--) dp[j] = Math.max(dp[j], dp[j-stone]+stone); return sum - 2 * dp[V]; }
- 01 Knapsack problem
-
1201. Ugly Number III
Write a program to find the n-th ugly number. Ugly numbers are positive integers which are divisible by a or b or c. Example 1: Input: n = 3, a = 2, b = 3, c = 5 Output: 4 Explanation: The ugly numbers are 2, 3, 4, 5, 6, 8, 9, 10... The 3rd is 4. Example 2: Input: n = 4, a = 2, b = 3, c = 4 Output: 6 Explanation: The ugly numbers are 2, 3, 4, 6, 8, 9, 12... The 4th is 6. Example 3: Input: n = 5, a = 2, b = 11, c = 13 Output: 10 Explanation: The ugly numbers are 2, 4, 6, 8, 10, 11, 12, 13... The 5th is 10. Example 4: Input: n = 1000000000, a = 2, b = 217983653, c = 336916467 Output: 1999999984 Constraints: 1 <= n, a, b, c <= 10^9 1 <= a * b * c <= 10^18 It's guaranteed that the result will be in range [1, 2 * 10^9]
- Binray search & Math
remember how to calculate gcd and lcm.
Euclid’s algorithm is based on the following property: if p>q then the gcd of p and q is the same as the gcd of p%q and q. p%q is the remainder of p which cannot be divided by q, e.g. 33 % 5 is 3. This is based on the fact that the gcd of p and q also must divided (p-q) or (p-2q) or (p-3q) .... Therefore you can subtract the maximum of a multitude q from p which is p%q. Then lcm can be calculated with gcd => a * b = x * gcd * y * gcd => lcm = x * y * gcd = a * b / gcd Math is beautiful.
class Solution { public int nthUglyNumber(int k, int a, int b, int c) { long ab = lcm(a, b), bc = lcm(b,c), ac = lcm(a,c), abc = lcm(ab, c); int l = 0, r = 2 * (int)Math.pow(10, 9)+1; while(l < r) { int mid = l + (r-l) / 2; long order = getOrder(mid, a, b, c, ab, ac, bc, abc); if(order > k) { r = mid - 1; } else if(order < k) { l = mid + 1; } else { r = mid; // potential answer } } return l; } public long gcd(long a, long b) { if(b == 0) return a; return gcd(b, a % b); // a % b = a - n * b which can be divided by the gcd of a and b // so the a, b, and a % b share the same gcd, and a > b && b > a % b } public long lcm(long a, long b) { long high = a > b ? a : b; long low = high == a ? b : a; return a * b / gcd(high, low); } public long getOrder(int n, int a, int b, int c, long ab, long ac, long bc, long abc) { return n/a + n/b + n/c - n/ab - n/ac - n/bc + n/abc; } }
-
86. Partition List
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x. You should preserve the original relative order of the nodes in each of the two partitions. Example: Input: head = 1->4->3->2->5->2, x = 3 Output: 1->2->2->4->3->5
public ListNode partition(ListNode head, int x) { ListNode sentinel = new ListNode(0); sentinel.next = head; ListNode boundary = sentinel, prev = sentinel, curr = head; while(curr != null) { if(curr.val < x && boundary != prev) { prev.next = curr.next; curr.next = boundary.next; boundary.next = curr; boundary = boundary.next; curr = prev.next; } else { if(curr.val < x && boundary == prev) boundary = boundary.next; prev = prev.next; curr = curr.next; } } return sentinel.next; }
-
264. Ugly Number II
Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. Example: Input: n = 10 Output: 12 Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers. Note: 1 is typically treated as an ugly number. n does not exceed 1690.
- pre computation
three pointers a, b and c, to mark the last ugly number which was multiplied by 2, 3 and 5, correspondingly.
class Solution { static int[] arr = new int[1690]; { arr[0] = 1; int a = 0, b = 0, c = 0; for(int i = 1; i < arr.length; i++) { arr[i] = Math.min(Math.min(arr[a]*2, arr[b]*3), arr[c]*5); if(arr[i] == arr[a]*2) a++; if(arr[i] == arr[b]*3) b++; if(arr[i] == arr[c]*5) c++; } } public int nthUglyNumber(int n) { return arr[n-1]; } }
-
1203. Sort Items by Groups Respecting Dependencies
1203. Sort Items by Groups Respecting Dependencies
There are n items each belonging to zero or one of m groups where group[i] is the group that the i-th item belongs to and it's equal to -1 if the i-th item belongs to no group. The items and the groups are zero indexed. A group can have no item belonging to it. Return a sorted list of the items such that: The items that belong to the same group are next to each other in the sorted list. There are some relations between these items where beforeItems[i] is a list containing all the items that should come before the i-th item in the sorted array (to the left of the i-th item). Return any solution if there is more than one solution and return an empty list if there is no solution. Example 1: Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]] Output: [6,3,4,1,5,2,0,7] Example 2: Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]] Output: [] Explanation: This is the same as example 1 except that 4 needs to be before 6 in the sorted list. Constraints: 1 <= m <= n <= 3*10^4 group.length == beforeItems.length == n -1 <= group[i] <= m-1 0 <= beforeItems[i].length <= n-1 0 <= beforeItems[i][j] <= n-1 i != beforeItems[i][j] beforeItems[i] does not contain duplicates elements.
- two level topological sort on groups and items
public int[] sortItems(int n, int m, int[] group, List<List<Integer>> beforeItems) { for(int i = 0; i < group.length; i++) if(group[i] == -1) group[i] = m++; // assign none-group elements a group int[] inG = new int[m]; // indegree of groups int[] inI = new int[n]; // indegree of items ArrayList<Integer>[] graphI = new ArrayList[n]; // graph of groups: before group -> after groups ArrayList<Integer>[] graphG = new ArrayList[m]; // graph of items: before item -> after items ArrayList<Integer>[] g2i = new ArrayList[m]; // group index -> item indexes for(int i = 0; i < m; i++) g2i[i] = new ArrayList<>(); for(int i = 0; i < n; i++) graphI[i] = new ArrayList<>(); for(int i = 0; i < m; i++) graphG[i] = new ArrayList<>(); for(int i = 0; i < group.length; i++) g2i[group[i]].add(i); for(int i = 0; i < beforeItems.size(); i++) { List<Integer> l = beforeItems.get(i); for(int b : l) { if(group[i] != group[b]) { // order between differnt groups graphG[group[b]].add(group[i]); inG[group[i]]++; } else { // order between items within a group inI[i]++; graphI[b].add(i); } } } // Level1: the code below is a topological sort on the groups PriorityQueue<Integer> q = new PriorityQueue<>(); for(int i = 0; i < m; i++) { if(inG[i] == 0) q.offer(i); } ArrayList<ArrayList<Integer>> orderG = new ArrayList<>(); while(!q.isEmpty()) { int b = q.poll(); // current group with indgree equals 0 // Level 2: the code below is a topological sort on items within a group ArrayList<Integer> temp = new ArrayList<>(); PriorityQueue<Integer> iq = new PriorityQueue<>(); for(int i : g2i[b]) { if(inI[i] == 0) iq.offer(i); } while(!iq.isEmpty()) { int ib = iq.poll(); temp.add(ib); for(int ia : graphI[ib]) { inI[ia]--; if(inI[ia] == 0) { iq.offer(ia); } } } if(temp.size() != g2i[b].size()) return new int[]{}; // invalid result orderG.add(temp); // add the sorted group items into result // END inner sort for(int after : graphG[b]) { // add new groups with indgree equals 0 into the queue inG[after]--; if(inG[after] == 0) { q.offer(after); } } } if(orderG.size() != m) return new int[] {}; // END outer sort int[] result = new int[n]; int cnt = 0; for(ArrayList<Integer> l : orderG) { for(int i : l) result[cnt++] = i; } return result; }
-
1202. Smallest String With Swaps
1202. Smallest String With Swaps
You are given a string s, and an array of pairs of indices in the string pairs where pairs[i] = [a, b] indicates 2 indices(0-indexed) of the string. You can swap the characters at any pair of indices in the given pairs any number of times. Return the lexicographically smallest string that s can be changed to after using the swaps. Example 1: Input: s = "dcab", pairs = [[0,3],[1,2]] Output: "bacd" Explaination: Swap s[0] and s[3], s = "bcad" Swap s[1] and s[2], s = "bacd" Example 2: Input: s = "dcab", pairs = [[0,3],[1,2],[0,2]] Output: "abcd" Explaination: Swap s[0] and s[3], s = "bcad" Swap s[0] and s[2], s = "acbd" Swap s[1] and s[2], s = "abcd" Example 3: Input: s = "cba", pairs = [[0,1],[1,2]] Output: "abc" Explaination: Swap s[0] and s[1], s = "bca" Swap s[1] and s[2], s = "bac" Swap s[0] and s[1], s = "abc" Constraints: 1 <= s.length <= 10^5 0 <= pairs.length <= 10^5 0 <= pairs[i][0], pairs[i][1] < s.length s only contains lower case English letters.
- UnionFind
public String smallestStringWithSwaps(String s, List<List<Integer>> pairs) { ArrayList<HashSet<Integer>> groups = new ArrayList<>(); UnionFind uf = new UnionFind(s.length()); for(List<Integer> l : pairs) { int x = l.get(0), y = l.get(1); uf.union(x, y); } HashMap<Integer, ArrayList<Integer>> map = new HashMap<>(); for(int i = 0; i < s.length(); i++) { int root = uf.find(i); ArrayList<Integer> arr = map.getOrDefault(root, new ArrayList<>()); arr.add(i); map.put(root, arr); } StringBuilder sb = new StringBuilder(s); for(Integer root : map.keySet()) { ArrayList<Integer> indexes = map.get(root); ArrayList<Character> arr = new ArrayList<>(); for(int j : indexes) { arr.add(s.charAt(j)); } Collections.sort(arr); Collections.sort(indexes); int k = 0; for(int j : indexes) { sb.setCharAt(j, arr.get(k++)); } } return sb.toString(); } class UnionFind { int[] arr; public UnionFind(int n) { arr = new int[n]; for(int i = 0; i < n; i++) { arr[i] = i; } } public int find(int x) { if(arr[x] != x) arr[x] = find(arr[x]); return arr[x]; } public void union(int x, int y) { int a = find(x), b = find(y); if(a != b) { arr[b] = a; } } }
-
1200. Minimum Absolute Difference
1200. Minimum Absolute Difference
Given an array of distinct integers arr, find all pairs of elements with the minimum absolute difference of any two elements. Return a list of pairs in ascending order(with respect to pairs), each pair [a, b] follows a, b are from arr a < b b - a equals to the minimum absolute difference of any two elements in arr Example 1: Input: arr = [4,2,1,3] Output: [[1,2],[2,3],[3,4]] Explanation: The minimum absolute difference is 1. List all pairs with difference equal to 1 in ascending order. Example 2: Input: arr = [1,3,6,10,15] Output: [[1,3]] Example 3: Input: arr = [3,8,-10,23,19,-4,-14,27] Output: [[-14,-10],[19,23],[23,27]] Constraints: 2 <= arr.length <= 10^5 -10^6 <= arr[i] <= 10^6
- Sorting
public List<List<Integer>> minimumAbsDifference(int[] arr) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(arr); int min = Integer.MAX_VALUE; for(int i = 1; i < arr.length; i++) { int diff = Math.abs(arr[i]-arr[i-1]); if(diff <= min) { if(diff < min) { min = diff; result.clear(); } result.add(Arrays.asList(arr[i-1], arr[i])); } } return result; }
-
1198. Find Smallest Common Element in All Rows
1198. Find Smallest Common Element in All Rows
Given a matrix mat where every row is sorted in increasing order, return the smallest common element in all rows. If there is no common element, return -1. Example 1: Input: mat = [[1,2,3,4,5],[2,4,5,8,10],[3,5,7,9,11],[1,3,5,7,9]] Output: 5 Constraints: 1 <= mat.length, mat[i].length <= 500 1 <= mat[i][j] <= 10^4 mat[i] is sorted in increasing order.
- Cnt
public int smallestCommonElement(int[][] mat) { int[] cnt = new int[100001]; for(int i = 0; i < mat.length; i++) { for(int j = 0; j < mat[0].length; j++) { cnt[mat[i][j]]++; if(cnt[mat[i][j]] == mat.length) return mat[i][j]; } } return -1; }
- set
public int smallestCommonElement2(int[][] mat) { HashSet<Integer> set = new HashSet<>(); for(int j : mat[0]) set.add(j); for(int i = 1; i < mat.length; i++) { HashSet<Integer> temp = new HashSet<>(); for(int j : mat[i]) { if(set.contains(j)) temp.add(j); } set = temp; } ArrayList<Integer> arr = new ArrayList<>(set); Collections.sort(arr); if(arr.size() == 0) return -1; return arr.get(0); }
-
572. Subtree of Another Tree
Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself. Example 1: Given tree s: 3 / \ 4 5 / \ 1 2 Given tree t: 4 / \ 1 2 Return true, because t has the same structure and node values with a subtree of s. Example 2: Given tree s: 3 / \ 4 5 / \ 1 2 / 0 Given tree t: 4 / \ 1 2 Return false.
public boolean isSubtree(TreeNode s, TreeNode t) { if(s == null) return false; boolean root = false; if(s.val == t.val) root = check(s, t); if(root) return true; boolean left = isSubtree(s.left, t); if(left) return true; boolean right = isSubtree(s.right, t); return right; } public boolean check(TreeNode s, TreeNode t) { if(s == null && t == null) return true; if(s == null || t == null) return false; if(s.val != t.val) return false; return check(s.left, t.left) && check(s.right, t.right); }
-
325. Maximum Size Subarray Sum Equals k
325. Maximum Size Subarray Sum Equals k
Given an array nums and a target value k, find the maximum length of a subarray that sums to k. If there isn't one, return 0 instead. Note: The sum of the entire nums array is guaranteed to fit within the 32-bit signed integer range. Example 1: Input: nums = [1, -1, 5, -2, 3], k = 3 Output: 4 Explanation: The subarray [1, -1, 5, -2] sums to 3 and is the longest. Example 2: Input: nums = [-2, -1, 2, 1], k = 1 Output: 2 Explanation: The subarray [-1, 2] sums to 1 and is the longest. Follow Up: Can you do it in O(n) time?
public int maxSubArrayLen(int[] nums, int k) { HashMap<Integer, Integer> map = new HashMap<>(); map.put(0, -1); int sum = 0; int result = 0; for(int i = 0; i < nums.length; i++) { sum += nums[i]; if(map.containsKey(sum-k)) { // find a candidate result = Math.max(result, i-map.get(sum-k)); } if(!map.containsKey(sum)) map.put(sum, i); // keep the smallest index } return result; }
-
333. Largest BST Subtree
Given a binary tree, find the largest subtree which is a Binary Search Tree (BST), where largest means subtree with largest number of nodes in it. Note: A subtree must include all of its descendants. Example: Input: [10,5,15,1,8,null,7] 10 / \ 5 15 / \ \ 1 8 7 Output: 3 Explanation: The Largest BST Subtree in this case is the highlighted one. The return value is the subtree's size, which is 3. Follow up: Can you figure out ways to solve it with O(n) time complexity?
class Solution { int result = 0; public int largestBSTSubtree(TreeNode root) { find(root); return result; } // return {max, min, cnt} // {0, 0, -1} invalid substree public int[] find(TreeNode root) { if(root == null) return null; int[] l = find(root.left), r = find(root.right); if(l != null && (l[2] == -1 || root.val <= l[0]) || r != null && (r[2] == -1 || root.val >= r[1])) { return new int[]{0,0,-1}; // subtree or the curr tree is invalid } int max = root.val, min = root.val, cnt = 1; if(l != null) { max = Math.max(max, l[0]); min = Math.min(min, l[1]); cnt += l[2]; } if(r != null) { max = Math.max(max, r[0]); min = Math.min(min, r[1]); cnt += r[2]; } result = Math.max(result, cnt); return new int[] {max, min, cnt}; } }
- More concise version
class Solution { int result = 0; public int largestBSTSubtree(TreeNode root) { find(root); return result; } // return {max, min, cnt} // {0, 0, -1} invalid substree public int[] find(TreeNode root) { if(root == null) return new int[]{Integer.MIN_VALUE,Integer.MAX_VALUE,0}; int[] l = find(root.left), r = find(root.right); if(l[2] == -1 || r[2] == -1 || root.val <= l[0] || root.val >= r[1]) { return new int[]{0,0,-1}; // invalid subtree or current root } int cnt = 1 + l[2] + r[2]; result = Math.max(result, cnt); return new int[] {Math.max(root.val, r[0]), Math.min(root.val, l[1]), cnt}; } }
-
713. Subarray Product Less Than K
713. Subarray Product Less Than K
Your are given an array of positive integers nums. Count and print the number of (contiguous) subarrays where the product of all the elements in the subarray is less than k. Example 1: Input: nums = [10, 5, 2, 6], k = 100 Output: 8 Explanation: The 8 subarrays that have product less than 100 are: [10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]. Note that [10, 5, 2] is not included as the product of 100 is not strictly less than k. Note: 0 < nums.length <= 50000. 0 < nums[i] < 1000. 0 <= k < 10^6.
- sliding window
Consider sliding window greedy first when the sum/product is ordered. binary Search arr ordered dp problem has overlapped sub problems
public int numSubarrayProductLessThanK(int[] nums, int k) { int result = 0; int j = 0, i = 0; int product = 1; while(i < nums.length) { product *= nums[i]; while(j <= i && product >= k) { product /= nums[j++]; } int n = i-j+1; result += n; i++; } return result; }
- A failed binary search on prefix product array
A feasible way is to change the prefix product of origin array into a prefix log sum array
public int numSubarrayProductLessThanK(int[] nums, int k) { int result = 0; double[] arr = new double[nums.length]; arr[0] = nums[0]; for(int i = 1; i < arr.length; i++) { arr[i] = arr[i-1] * nums[i]; } for(int i = 0; i < nums.length; i++) { int l = 0, r = i; while(l <= r) { int mid = l + (r-l) / 2; double product = arr[i] / arr[mid] * nums[mid]; if(product >= k) { l = mid + 1; } else { if(l == r) break; r = mid; } } int cnt = i - l + 1; result += cnt; } return result; }
- log sum; for reference
class Solution { public int numSubarrayProductLessThanK(int[] nums, int k) { if (k == 0) return 0; double logk = Math.log(k); double[] prefix = new double[nums.length + 1]; for (int i = 0; i < nums.length; i++) { prefix[i+1] = prefix[i] + Math.log(nums[i]); } int ans = 0; for (int i = 0; i < prefix.length; i++) { int lo = i + 1, hi = prefix.length; while (lo < hi) { int mi = lo + (hi - lo) / 2; if (prefix[mi] < prefix[i] + logk - 1e-9) lo = mi + 1; else hi = mi; } ans += lo - i - 1; } return ans; } }
-
298. Binary Tree Longest Consecutive Sequence
298. Binary Tree Longest Consecutive Sequence
Given a binary tree, find the length of the longest consecutive sequence path. The path refers to any sequence of nodes from some starting node to any node in the tree along the parent- child connections. The longest consecutive path need to be from parent to child (cannot be the reverse). Example 1: Input: 1 \ 3 / \ 2 4 \ 5 Output: 3 Explanation: Longest consecutive sequence path is 3-4-5, so return 3. Example 2: Input: 2 \ 3 / 2 / 1 Output: 2 Explanation: Longest consecutive sequence path is 2-3, not 3-2-1, so return 2.
int result = 0; public int longestConsecutive(TreeNode root) { traverse(root); return result; } public int traverse(TreeNode root) { if(root == null) return 0; int left = traverse(root.left), right = traverse(root.right); int max = 1; if(root.left != null && root.val+1 == root.left.val) { max = left + 1; } if(root.right != null && root.val+1 == root.right.val) { max = Math.max(max, right+1); } result = Math.max(max, result); return max; }
-
542. 01 Matrix
Given a matrix consists of 0 and 1, find the distance of the nearest 0 for each cell. The distance between two adjacent cells is 1. Example 1: Input: [[0,0,0], [0,1,0], [0,0,0]] Output: [[0,0,0], [0,1,0], [0,0,0]] Example 2: Input: [[0,0,0], [0,1,0], [1,1,1]] Output: [[0,0,0], [0,1,0], [1,2,1]] Note: The number of elements of the given matrix will not exceed 10,000. There are at least one 0 in the given matrix. The cells are adjacent in only four directions: up, down, left and right.
- DFS failed with TLE; I was insane to try dfs first …; think it twice next time.
This bfs is a competition of all zeros, zeros capture closest ones in turn.
public int[][] updateMatrix(int[][] matrix) { int[][] result = new int[matrix.length][matrix[0].length]; for(int i = 0; i < result.length; i++) for(int j = 0; j < result[0].length; j++) if(matrix[i][j] == 1) result[i][j] = Integer.MAX_VALUE; int[] xs = {0,0,-1,1}, ys = {1,-1,0,0}; Queue<int[]> q = new LinkedList<>(); for(int i = 0; i < result.length; i++) for(int j = 0; j< result[0].length; j++) if(matrix[i][j] == 0) // start from all zeros, then step 1, step 2... q.offer(new int[]{i,j}); while(!q.isEmpty()) { int[] curr = q.poll(); for(int k = 0; k < 4; k++) { int x = curr[0] + xs[k], y = curr[1] + ys[k]; if(x < 0 || y < 0 || x >= result.length || y >= result[0].length) continue; if(result[x][y] > result[curr[0]][curr[1]]+1) { result[x][y] = result[curr[0]][curr[1]]+1; q.offer(new int[] {x,y}); } } } return result; }
- Two pass dp
public int[][] updateMatrix(int[][] matrix) { int MAX = matrix.length + matrix[0].length; int[][] dp = new int[matrix.length][matrix[0].length]; for(int i = 0; i < dp.length; i++) { for(int j = 0; j < dp[0].length; j++) { if(matrix[i][j] == 1){ int top = i-1 >= 0 ? dp[i-1][j] : MAX; int left = j-1 >= 0 ? dp[i][j-1] : MAX; dp[i][j] = Math.min(left, top)+1; } } } for(int i = dp.length-1; i >= 0; i--) { for(int j = dp[0].length-1; j >= 0; j--) { if(matrix[i][j] == 1) { int right = j+1 < dp[0].length ? dp[i][j+1] : MAX; int bottom = i+1 < dp.length ? dp[i+1][j] : MAX; dp[i][j] = Math.min(dp[i][j], Math.min(right, bottom)+1); } } } return dp; }
-
290. Word Pattern
Given a pattern and a string str, find if str follows the same pattern. Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str. Example 1: Input: pattern = "abba", str = "dog cat cat dog" Output: true Example 2: Input:pattern = "abba", str = "dog cat cat fish" Output: false Example 3: Input: pattern = "aaaa", str = "dog cat cat dog" Output: false Example 4: Input: pattern = "abba", str = "dog dog dog dog" Output: false Notes: You may assume pattern contains only lowercase letters, and str contains lowercase letters that may be separated by a single space.
public boolean wordPattern(String pattern, String str) { HashMap<String, Character> map = new HashMap<>(); HashSet<Character> used = new HashSet<>(); int p = 0; String[] arr = str.split(" "); if(arr.length != pattern.length()) return false; for(String s : arr) { if(p == pattern.length()) return false; if(map.containsKey(s) && map.get(s) != pattern.charAt(p)) { return false; } if(!map.containsKey(s) && used.contains(pattern.charAt(p))) { return false; } map.put(s, pattern.charAt(p)); used.add(pattern.charAt(p)); p++; } return true; }
-
791. Custom Sort String
S and T are strings composed of lowercase letters. In S, no letter occurs more than once. S was sorted in some custom order previously. We want to permute the characters of T so that they match the order that S was sorted. More specifically, if x occurs before y in S, then x should occur before y in the returned string. Return any permutation of T (as a string) that satisfies this property. Example : Input: S = "cba" T = "abcd" Output: "cbad" Explanation: "a", "b", "c" appear in S, so the order of "a", "b", "c" should be "c", "b", and "a". Since "d" does not appear in S, it can be at any position in T. "dcba", "cdba", "cbda" are also valid outputs. Note: S has length at most 26, and no character is repeated in S. T has length at most 200. S and T consist of lowercase letters only.
- Easier than Medium, but how about collecting all valid result?
public String customSortString(String S, String T) { String result = ""; int[] cnt = new int[26]; for(char c : T.toCharArray()) cnt[c-'a']++; for(char c : S.toCharArray()) { while(cnt[c-'a'] > 0) { result += c; cnt[c-'a']--; } } for(int i = 0; i < 26; i++) { while(cnt[i] > 0) { result += (char)(i+'a'); cnt[i]--; } } return result; }
-
549. Binary Tree Longest Consecutive Sequence II
549. Binary Tree Longest Consecutive Sequence II
Given a binary tree, you need to find the length of Longest Consecutive Path in Binary Tree. Especially, this path can be either increasing or decreasing. For example, [1,2,3,4] and [4,3,2,1] are both considered valid, but the path [1,2,4,3] is not valid. On the other hand, the path can be in the child- Parent-child order, where not necessarily be parent-child order. Example 1: Input: 1 / \ 2 3 Output: 2 Explanation: The longest consecutive path is [1, 2] or [2, 1]. Example 2: Input: 2 / \ 1 3 Output: 3 Explanation: The longest consecutive path is [1, 2, 3] or [3, 2, 1]. Note: All the values of tree nodes are in the range of [-1e7, 1e7].
- break three sum to two sum
int result = 0; public int longestConsecutive(TreeNode root) { traverse(root); return result; } public int[] traverse(TreeNode root) { if(root == null) return new int[]{0,0,0}; int[] left = traverse(root.left), right = traverse(root.right); int leftIn = root.val == left[0]+1 ? 1 + left[1] : 1; int rightIn = root.val == right[0]+1 ? 1 + right[1] : 1; int leftDe = root.val == left[0]-1 ? 1 + left[2] : 1; int rightDe = root.val == right[0]-1 ? 1 + right[2] : 1; int leftInRightDe = leftIn + rightDe - 1; int leftDeRightIn = leftDe + rightIn - 1; int max = Math.max(leftInRightDe, leftDeRightIn); result = Math.max(max, result); return new int[] {root.val, Math.max(leftIn, rightIn), Math.max(rightDe, leftDe)}; }
-
259. 3Sum Smaller
Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target. Example: Input: nums = [-2,0,1,3], and target = 2 Output: 2 Explanation: Because there are two triplets which sums are less than 2: [-2,0,1] [-2,0,3] Follow up: Could you solve it in O(n2) runtime?
- break three sum to two sum
public int threeSumSmaller(int[] nums, int target) { Arrays.sort(nums); int result = 0; for(int i = 0; i < nums.length; i++) { int t = target - nums[i]; int l = i+1, r = nums.length-1; while(l < r) { int sum = nums[l] + nums[r]; if(sum < t) { result += r-l; // give up the smallest value l++; } else { r--; // give up the largest value } } } return result; }
-
1199. Minimum Time to Build Blocks
1199. Minimum Time to Build Blocks
You are given a list of blocks, where blocks[i] = t means that the i-th block needs t units of time to be built. A block can only be built by exactly one worker. A worker can either split into two workers (number of workers increases by one) or build a block then go home. Both decisions cost some time. The time cost of spliting one worker into two workers is given as an integer split. Note that if two workers split at the same time, they split in parallel so the cost would be split. Output the minimum time needed to build all blocks. Initially, there is only one worker. Example 1: Input: blocks = [1], split = 1 Output: 1 Explanation: We use 1 worker to build 1 block in 1 time unit. Example 2: Input: blocks = [1,2], split = 5 Output: 7 Explanation: We split the worker into 2 workers in 5 time units then assign each of them to a block so the cost is 5 + max(1, 2) = 7. Example 3: Input: blocks = [1,2,3], split = 1 Output: 4 Explanation: Split 1 worker into 2, then assign the first worker to the last block and split the second worker into 2. Then, use the two unassigned workers to build the first two blocks. The cost is 1 + max(3, 1 + max(1, 2)) = 4. Constraints: 1 <= blocks.length <= 1000 1 <= blocks[i] <= 10^5 1 <= split <= 100
- Huffman Tree
Internal nodes are split
public int minBuildTime(int[] blocks, int split) { if(blocks.length == 0) return 0; PriorityQueue<Integer> q = new PriorityQueue<>(); for(int i : blocks) q.offer(i); while(q.size() > 1) { int left = q.poll(), right = q.poll(); q.offer(right + split); } return q.poll(); }
-
1197. Minimum Knight Moves
In an infinite chess board with coordinates from -infinity to +infinity, you have a knight at square [0, 0]. A knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction. Return the minimum number of steps needed to move the knight to the square [x, y]. It is guaranteed the answer exists. Example 1: Input: x = 2, y = 1 Output: 1 Explanation: [0, 0] → [2, 1] Example 2: Input: x = 5, y = 5 Output: 4 Explanation: [0, 0] → [2, 1] → [4, 2] → [3, 4] → [5, 5] Constraints: |x| + |y| <= 300
- Bidirectional bfs
public int minKnightMoves(int x, int y) { if(x == 0 && y == 0) return 0; x = Math.abs(x); y = Math.abs(y); int[] xs = {1,1,2,2,-1,-1,-2,-2}, ys = {2,-2,1,-1,2,-2,1,-1}; HashSet<String> visited = new HashSet<>(); HashSet<String> visited2 = new HashSet<>(); Queue<int[]> q = new LinkedList<>(); q.offer(new int[]{0,0}); visited.add(0 + "&" + 0); Queue<int[]> q2 = new LinkedList<>(); q2.offer(new int[]{x,y}); visited2.add(x + "&" + y); int l = 0, l2 = 0; while(!q.isEmpty()) { int size = q.size(); for(int t = 0; t < size; t++) { int[] c = q.poll(); for(int k = 0; k < 8; k++) { int nx = xs[k] + c[0], ny = ys[k] + c[1]; String key = nx + "&" + ny; if(nx == x && ny == y) return l+1; if(visited2.contains(key)) return l + l2 + 1; if(nx + ny > 300 || visited.contains(key)) continue; if(nx > x + 2 || ny > y + 2) continue; visited.add(key); q.offer(new int[] {nx, ny}); } } l++; int size2 = q2.size(); for(int t = 0; t < size2; t++) { int[] c = q2.poll(); for(int k = 0; k < 8; k++) { int nx = xs[k] + c[0], ny = ys[k] + c[1]; String key = nx + "&" + ny; if(visited.contains(key)) return l + l2 + 1; if(x < -2 || y < -2) continue; if(nx + ny > 300 || visited2.contains(key)) continue; visited2.add(key); q2.offer(new int[] {nx, ny}); } } l2++; } return -1; }
-
271. Encode and Decode Strings
271. Encode and Decode Strings
Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings. Machine 1 (sender) has the function: string encode(vector<string> strs) { // ... your code return encoded_string; } Machine 2 (receiver) has the function: vector<string> decode(string s) { //... your code return strs; } So Machine 1 does: string encoded_string = encode(strs); and Machine 2 does: vector<string> strs2 = decode(encoded_string); strs2 in Machine 2 should be the same as strs in Machine 1. Implement the encode and decode methods. Note: The string may contain any possible characters out of 256 valid ascii characters. Your algorithm should be generalized enough to work on any possible characters. Do not use class member/global/static variables to store states. Your encode and decode algorithms should be stateless. Do not rely on any library method such as eval or serialize methods. You should implement your own encode/decode algorithm.
- Like encoding and decoding a tcp head
len + content
9 decimal for len becasue the len of a string has 9 or less digit; offset with 0
public class Codec { // Encodes a list of strings to a single string. public String encode(List<String> strs) { if(strs.size() == 0) return null; StringBuilder sb = new StringBuilder(); for(String s : strs) { StringBuilder len = new StringBuilder(); int l = s.length(); int cnt = 0; while(l > 0) { cnt++; l = l / 10; } // System.out.println(cnt); for(int i = 0; i < 9-cnt; i++) { len.append('0'); } if(cnt > 0) len.append(s.length()); sb.append(len).append(s); } return sb.toString(); } // Decodes a single string to a list of strings. public List<String> decode(String encoded) { List<String> result = new ArrayList<>(); if(encoded == null) return result; // System.out.println(encoded); int i = 0; while(i < encoded.length()) { int len = Integer.valueOf(encoded.substring(i, i+9)); i += 9; String word = encoded.substring(i, i+len); i += len; result.add(word); } return result; } } // Your Codec object will be instantiated and called as such: // Codec codec = new Codec(); // codec.decode(codec.encode(strs));
-
809. Expressive Words
Sometimes people repeat letters to represent extra feeling, such as "hello" -> "heeellooo", "hi" -> "hiiii". In these strings like "heeellooo", we have groups of adjacent letters that are all the same: "h", "eee", "ll", "ooo". For some given string S, a query word is stretchy if it can be made to be equal to S by any number of applications of the following extension operation: choose a group consisting of characters c, and add some number of characters c to the group so that the size of the group is 3 or more. For example, starting with "hello", we could do an extension on the group "o" to get "hellooo", but we cannot get "helloo" since the group "oo" has size less than 3. Also, we could do another extension like "ll" -> "lllll" to get "helllllooo". If S = "helllllooo", then the query word "hello" would be stretchy because of these two extension operations: query = "hello" -> "hellooo" -> "helllllooo" = S. Given a list of query words, return the number of words that are stretchy. Example: Input: S = "heeellooo" words = ["hello", "hi", "helo"] Output: 1 Explanation: We can extend "e" and "o" in the word "hello" to get "heeellooo". We can't extend "helo" to get "heeellooo" because the group "ll" is not size 3 or more. Notes: 0 <= len(S) <= 100. 0 <= len(words) <= 100. 0 <= len(words[i]) <= 100. S and all words in words consist only of lowercase letters
- half a hour to finish…
public int expressiveWords(String S, String[] words) { int result = 0; for(String w : words) { if(isStretchy(S, w)) { result++; } } return result; } public boolean isStretchy(String t, String s) { if(s.length() > t.length() || t.length() < 3) return false; int i = 0, j = 0; int cnt = 0; while(i < t.length() || j < s.length()) { if(i == t.length() && j < s.length()) return false; // t: eeeeeeee // s: ed if(j < s.length() && t.charAt(i) == s.charAt(j)) { i++; j++; continue; } if(i == 0 && j == 0) return false; if(t.charAt(i) == t.charAt(i-1)) { // if this is a valid expansion position char c = t.charAt(i); // check if there are three consecutive c, if yes, consume them all if(i-2 >= 0 && t.charAt(i-2) == c || i+1 < t.length() && t.charAt(i+1) == c) { while(i < t.length() && t.charAt(i) == c) i++; if(j == s.length()) return i == t.length(); continue; } } return false; } return true; }
-
951. Flip Equivalent Binary Trees
951. Flip Equivalent Binary Trees
For a binary tree T, we can define a flip operation as follows: choose any node, and swap the left and right child subtrees. A binary tree X is flip equivalent to a binary tree Y if and only if we can make X equal to Y after some number of flip operations. Write a function that determines whether two binary trees are flip equivalent. The trees are given by root nodes root1 and root2. Example 1: Input: root1 = [1,2,3,4,5,6,null,null,null,7,8], root2 = [1,3,2,null,6,4,5,null,null,null,null,8,7] Output: true Explanation: We flipped at nodes with values 1, 3, and 5. Flipped Trees Diagram
Note: Each tree will have at most 100 nodes. Each value in each tree will be a unique integer in the range [0, 99].
- Easy Divide and Conquer
Another approach: Since the values in the tree are unique. We can also convert two trees to a canonical representation, then compare equality. (eg. always visit the smaller node of a pair of sister nodes)
public boolean flipEquiv(TreeNode root1, TreeNode root2) { if(root1 == null && root2 == null) return true; if(root1 == null || root2 == null || root1.val != root2.val) return false; return flipEquiv(root1.left, root2.left) && flipEquiv(root1.right, root2.right) || flipEquiv(root1.left, root2.right) && flipEquiv(root1.right, root2.left); }
-
33. Find And Replace in String
33. Find And Replace in String
To some string S, we will perform some replacement operations that replace groups of letters with new ones (not necessarily the same size). Each replacement operation has 3 parameters: a starting index i, a source word x and a target word y. The rule is that if x starts at position i in the original string S, then we will replace that occurrence of x with y. If not, we do nothing. For example, if we have S = "abcd" and we have some replacement operation i = 2, x = "cd", y = "ffff", then because "cd" starts at position 2 in the original string S, we will replace it with "ffff". Using another example on S = "abcd", if we have both the replacement operation i = 0, x = "ab", y = "eee", as well as another replacement operation i = 2, x = "ec", y = "ffff", this second operation does nothing because in the original string S[2] = 'c', which doesn't match x[0] = 'e'. All these operations occur simultaneously. It's guaranteed that there won't be any overlap in replacement: for example, S = "abc", indexes = [0, 1], sources = ["ab","bc"] is not a valid test case. Example 1: Input: S = "abcd", indexes = [0,2], sources = ["a","cd"], targets = ["eee","ffff"] Output: "eeebffff" Explanation: "a" starts at index 0 in S, so it's replaced by "eee". "cd" starts at index 2 in S, so it's replaced by "ffff". Example 2: Input: S = "abcd", indexes = [0,2], sources = ["ab","ec"], targets = ["eee","ffff"] Output: "eeecd" Explanation: "ab" starts at index 0 in S, so it's replaced by "eee". "ec" doesn't starts at index 2 in the original S, so we do nothing. Notes: 0 <= indexes.length = sources.length = targets.length <= 100 0 < indexes[i] < S.length <= 1000 All characters in given inputs are lowercase letters.
- Backtrack with pruning
public String findReplaceString(String S, int[] indexes, String[] sources, String[] targets) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < indexes.length; i++) map.put(indexes[i], i); StringBuilder sb = new StringBuilder(); for(int i = 0; i < S.length();) { if(map.containsKey(i)) { int p = map.get(i), j = i + sources[p].length(); String sub = (j <= S.length()) ? S.substring(i, j) : ""; if(sub.equals(sources[p])) { sb.append(targets[p]); i += sources[p].length(); continue; } } sb.append(S.charAt(i)); i++; } return sb.toString(); }
-
299. Bulls and Cows
You are playing the following Bulls and Cows game with your friend: You write down a number and ask your friend to guess what the number is. Each time your friend makes a guess, you provide a hint that indicates how many digits in said guess match your secret number exactly in both digit and position (called "bulls") and how many digits match the secret number but locate in the wrong position (called "cows"). Your friend will use successive guesses and hints to eventually derive the secret number. Write a function to return a hint according to the secret number and friend's guess, use A to indicate the bulls and B to indicate the cows. Please note that both secret number and friend's guess may contain duplicate digits. Example 1: Input: secret = "1807", guess = "7810" Output: "1A3B" Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7. Example 2: Input: secret = "1123", guess = "0111" Output: "1A1B" Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow. Note: You may assume that the secret number and your friend's guess only contain digits, and their lengths are always equal.
A very smart one pass solution.
Increment or decrement the count of characters for chars from secret or guess.
public String getHint(String secret, String guess) { int[] cnt = new int[10]; int bull = 0, cow = 0; for(int i = 0; i < secret.length(); i++) { int s = secret.charAt(i)-'0', g = guess.charAt(i)-'0'; if(s == g) { bull++; } else { if(cnt[s] < 0) cow++; if(cnt[g] > 0) cow++; cnt[s]++; cnt[g]--; } } return bull + "A" + cow + "B"; }
- Two pass
public String getHintTwoPass(String secret, String guess) { HashMap<Character, Integer> map = new HashMap<>(); ArrayList<Character> incorrect = new ArrayList<>(); int bull = 0, cow = 0; for(int i = 0; i < secret.length(); i++) { char s = secret.charAt(i), g = guess.charAt(i); if(s == g) { bull++; } else { map.put(s, map.getOrDefault(s, 0)+1); incorrect.add(g); } } for(char c : incorrect) { if(map.containsKey(c) && map.get(c) > 0) { map.put(c, map.get(c)-1); cow++; } } return bull + "A" + cow + "B"; }
-
1087. Brace Expansion
A string S represents a list of words. Each letter in the word has 1 or more options. If there is one option, the letter is represented as is. If there is more than one option, then curly braces delimit the options. For example, "{a,b,c}" represents options ["a", "b", "c"]. For example, "{a,b,c}d{e,f}" represents the list ["ade", "adf", "bde", "bdf", "cde", "cdf"]. Return all words that can be formed in this manner, in lexicographical order. Example 1: Input: "{a,b}c{d,e}f" Output: ["acdf","acef","bcdf","bcef"] Example 2: Input: "abcd" Output: ["abcd"] Note: 1 <= S.length <= 50 There are no nested curly brackets. All characters inside a pair of consecutive opening and ending curly brackets are different.
- Easy
Brace expansion is easier than Medium Brace expansion 2 is harder than Hard
public String[] expand(String S) { ArrayList<String[]> arr = new ArrayList<>(); int prev = 0; for(int i = 0; i < S.length(); i++) { char c = S.charAt(i); if(c == '{') { if(i > prev) { arr.add(getArr(S.substring(prev, i))); } prev = i+1; } else if(c == '}') { String sub = S.substring(prev, i); arr.add(getArr(sub)); prev = i+1; } } if(prev < S.length()) arr.add(getArr(S.substring(prev))); ArrayList<String> result = new ArrayList<>(); concatenate(result, 0, arr, ""); return result.toArray(new String[0]); } public void concatenate(ArrayList<String> result, int index, ArrayList<String[]> arr, String temp) { if(index == arr.size()) { result.add(temp); return; } for(String s : arr.get(index)) { concatenate(result, index+1, arr, temp + s); } } public String[] getArr(String s) { if(s.contains(",")) { String[] result = s.split(","); Arrays.sort(result); return result; } return new String[] {s}; }
-
1170. Compare Strings by Frequency of the Smallest Character
1170. Compare Strings by Frequency of the Smallest Character
- Binary Search
public int[] numSmallerByFrequency(String[] queries, String[] words) { int[] fw = new int[words.length]; for(int i = 0; i < fw.length; i++) fw[i] = f(words[i]); Arrays.sort(fw); int[] result = new int[queries.length]; for(int i = 0; i < result.length; i++) { result[i] = getCnt(fw, f(queries[i])); } return result; } // find the first index that arr[index] > target public int getCnt(int[] arr, int target) { int l = 0, r = arr.length; while(l < r) { int mid = l + (r-l) / 2; if(arr[mid] <= target) l = mid+1; else r = mid; } return arr.length - l; } public int f(String s) { int[] arr = new int[26]; for(char c : s.toCharArray()) arr[c-'a']++; for(int i : arr) if(i > 0) return i; return 0; }
-
1066. Campus Bikes II
On a campus represented as a 2D grid, there are N workers and M bikes, with N <= M. Each worker and bike is a 2D coordinate on this grid. We assign one unique bike to each worker so that the sum of the Manhattan distances between each worker and their assigned bike is minimized. The Manhattan distance between two points p1 and p2 is Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|. Return the minimum possible sum of Manhattan distances between each worker and their assigned bike. Example 1: Input: workers = [[0,0],[2,1]], bikes = [[1,2],[3,3]] Output: 6 Explanation: We assign bike 0 to worker 0, bike 1 to worker 1. The Manhattan distance of both assignments is 3, so the output is 6. Note: 0 <= workers[i][0], workers[i][1], bikes[i][0], bikes[i][1] < 1000 All worker and bike locations are distinct. 1 <= workers.length <= bikes.length <= 10
- DP with state mask
public int assignBikes(int[][] workers, int[][] bikes) { int N = workers.length, M = bikes.length; int[][] dp = new int[N+1][1<<M]; for(int i = 1; i < dp.length; i++) Arrays.fill(dp[i], 2000 * 10); int result = Integer.MAX_VALUE; for(int i = 1; i < dp.length; i++) { for(int s = 0; s < dp[0].length; s++) { for(int j = 0; j < M; j++) { if(((1 << j) & s) != 0) { // find a 1 to remove int p = (1 << j) ^ s; // previous state; reomve a 1 from current state --> remove a used bike int d = Math.abs(workers[i-1][0]-bikes[j][0]) + Math.abs(workers[i-1][1]-bikes[j][1]); dp[i][s] = Math.min(dp[i][s], dp[i-1][p] + d); if(i == dp.length-1) // all N worker, time to record the result result = Math.min(result, dp[i][s]); } } } } return result; }
- Backtrack with pruning
int result = Integer.MAX_VALUE; public int assignBikes(int[][] workers, int[][] bikes) { backtrack(workers, bikes, 0, new boolean[bikes.length], 0); return result; } public void backtrack(int[][] ws, int[][] bs, int index, boolean[] vb, int sum) { if(index == ws.length) { result = Math.min(result, sum); return; } if(sum >= result) return; // pruning for(int i = 0; i < bs.length; i++) { if(vb[i]) continue; vb[i] = true; int d = Math.abs(ws[index][0]-bs[i][0]) + Math.abs(ws[index][1]-bs[i][1]); backtrack(ws, bs, index+1, vb, sum+d); vb[i] = false; } }
-
1007. Minimum Domino Rotations For Equal Row
1007. Minimum Domino Rotations For Equal Row
In a row of dominoes, A[i] and B[i] represent the top and bottom halves of the i-th domino. (A domino is a tile with two numbers from 1 to 6 - one on each half of the tile.) We may rotate the i-th domino, so that A[i] and B[i] swap values. Return the minimum number of rotations so that all the values in A are the same, or all the values in B are the same. If it cannot be done, return -1. Example 1:
Input: A = [2,1,2,4,2,2], B = [5,2,6,2,3,2] Output: 2 Explanation: The first figure represents the dominoes as given by A and B: before we do any rotations. If we rotate the second and fourth dominoes, we can make every value in the top row equal to 2, as indicated by the second figure. Example 2: Input: A = [3,5,1,2,3], B = [3,6,3,3,4] Output: -1 Explanation: In this case, it is not possible to rotate the dominoes to make one row of values equal. Note: 1 <= A[i], B[i] <= 6 2 <= A.length == B.length <= 20000
public int minDominoRotations(int[] A, int[] B) { int result = Math.min(check(A, B), check(B, A)); return result == A.length ? -1 : result; } // check if it is possible to change all number in A to be the same value public int check(int[] A, int[] B) { int result = A.length; int[] candidates = {A[0], B[0]}; for(int i : candidates) { int cnt = 0; for(int j = 0; j < A.length; j++) { if(A[j] == i) continue; if(B[j] == i) cnt++; else { cnt = A.length; break; } } result = Math.min(result, cnt); } return result; }
-
248. Strobogrammatic Number III
248. Strobogrammatic Number III
A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). Write a function to count the total strobogrammatic numbers that exist in the range of low <= num <= high. Example: Input: low = "50", high = "100" Output: 3 Explanation: 69, 88, and 96 are three strobogrammatic numbers. Note: Because the range might be a large number, the low and high numbers are represented as string.
- Brutal: Use strobogrammatic number ii
This problem reminds me the hard kth smallest number in lexicographical order But they are different, it is difficult to find a structured growing tree for this problem.
class Solution { int[] self = {0,1,8}; int[] nums = {0,1,6,9,8}; int[] map = {0,1,0,0,0,0,9,0,8,6}; String low, high; public int strobogrammaticInRange(String low, String high) { this.low = low; this.high = high; int result = 0; for(int i = low.length(); i <= high.length(); i++) { result += findStrobogrammatic(i); } return result; } public int findStrobogrammatic(int n) { int result = build(n, "", ""); return result; } public int build(int n, String prefix, String suffix) { int result = 0; if(n == 0) { if(inBound(prefix + suffix)) { result++; } } else if(n == 1) { for(int i : self) if(inBound(prefix + i + suffix)) { result++; } } else { for(int i : nums) { if(prefix.length() == 0 && i == 0) continue; result += build(n-2, prefix+i, map[i]+suffix); } } return result; } public boolean inBound(String s) { if(s.length() == low.length() && s.compareTo(low) < 0) return false; if(s.length() == high.length() && s.compareTo(high) > 0) return false; return true; }
-
246. Strobogrammatic Number
A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). Write a function to determine if a number is strobogrammatic. The number is represented as a string. Example 1: Input: "69" Output: true Example 2: Input: "88" Output: true Example 3: Input: "962" Output: false
- Easy
public boolean isStrobogrammatic(String num) { HashMap<Character, Character> map = new HashMap<>(); map.put('6','9'); map.put('9','6'); map.put('0','0'); map.put('1','1'); map.put('8','8'); int l = 0, r = num.length()-1; while(l <= r) { if(!map.containsKey(num.charAt(l)) ||map.get(num.charAt(l)) != num.charAt(r)) return false; l++; r--; } return true; }
-
1110. Delete Nodes And Return Forest
1110. Delete Nodes And Return Forest
Given the root of a binary tree, each node in the tree has a distinct value. After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees). Return the roots of the trees in the remaining forest. You may return the result in any order. Example 1: Input: root = [1,2,3,4,5,6,7], to_delete = [3,5] Output: [[1,2,null,4],[6],[7]] Constraints: The number of nodes in the given tree is at most 1000. Each node has a distinct value between 1 and 1000. to_delete.length <= 1000 to_delete contains distinct values between 1 and 1000.
- The boolean value is true when the node is a root of a tree in the forest
public List<TreeNode> delNodes(TreeNode root, int[] toDelete) { HashSet<Integer> set = new HashSet<>(); for(int i : toDelete) set.add(i); return dfs(root, set, true); } public List<TreeNode> dfs(TreeNode root, HashSet<Integer> set, boolean isRoot) { List<TreeNode> result = new ArrayList<>(); if(root == null) return result; if(set.contains(root.val)) { result = dfs(root.left, set, true); result.addAll(dfs(root.right, set, true)); root.left = null; root.right = null; } else { if(isRoot) result.add(root); result.addAll(dfs(root.left, set, false)); result.addAll(dfs(root.right, set, false)); if(root.left != null && set.contains(root.left.val)) root.left = null; if(root.right != null && set.contains(root.right.val)) root.right = null; } return result; }
- The boolean value is true when the parent node is to be removed
This is a more concise version.
```java
public List
delNodes(TreeNode root, int[] toDelete) { ArrayList result = new ArrayList<>(); HashSet set = new HashSet<>(); for(int i : toDelete) set.add(i); dfs(result, set, root, true); return result; } public TreeNode dfs(ArrayList
result, HashSet set, TreeNode root, boolean parentIsDeleted) { if(root == null) return null; boolean isDeleted = set.contains(root.val); if(parentIsDeleted && !isDeleted) result.add(root); root.left = dfs(result, set, root.left, isDeleted); root.right = dfs(result, set, root.right, isDeleted); return isDeleted ? null : root; } -
247. Strobogrammatic Number II
247. Strobogrammatic Number II
A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). Find all strobogrammatic numbers that are of length = n. Example: Input: n = 2 Output: ["11","69","88","96"]
- Easy
int[] self = {0,1,8}; int[] nums = {0,1,6,9,8}; int[] map = {0,1,0,0,0,0,9,0,8,6}; public List<String> findStrobogrammatic(int n) { ArrayList<String> result = new ArrayList<>(); build(result, n, "", ""); return result; } public void build(ArrayList<String> result, int n, String prefix, String suffix) { if(n == 0) { result.add(prefix + suffix); } else if(n == 1) { for(int i : self) { result.add(prefix + i + suffix); } } else { for(int i : nums) { if(prefix.length() == 0 && i == 0) continue; build(result, n-2, prefix+i, map[i]+suffix); } } }
-
788. Rotated Digits
X is a good number if after rotating each digit individually by 180 degrees, we get a valid number that is different from X. Each digit must be rotated - we cannot choose to leave it alone. A number is valid if each digit remains a digit after rotation. 0, 1, and 8 rotate to themselves; 2 and 5 rotate to each other; 6 and 9 rotate to each other, and the rest of the numbers do not rotate to any other number and become invalid. Now given a positive number N, how many numbers X from 1 to N are good? Example: Input: 10 Output: 4 Explanation: There are four good numbers in the range [1, 10] : 2, 5, 6, 9. Note that 1 and 10 are not good numbers, since they remain unchanged after rotating. Note: N will be in range [1, 10000].
- Smart DP representing three states source
public int rotatedDigits(int N) { int[] dp = new int[1+N]; // 0 -> invalid; // 1 -> retate to itself; // 2 -> rotate to a different number int result = 0; for(int i = 0; i <= N; i++) { if(i <= 9) { if(i == 0 || i == 1 || i == 8) dp[i] = 1; else if(i == 2 || i == 5 || i == 6 || i == 9) { dp[i] = 2; result++; } } else { int prefix = dp[i / 10], lastDigit = dp[i % 10]; if(prefix == 0 || lastDigit == 0) dp[i] = 0; else if(prefix == 1 && lastDigit == 1) dp[i] = 1; else { dp[i] = 2; result++; } } } return result; }
- Brutal force to generate all candidates
class Solution { int result = 0; int[] arr = {0, 1, 8, 2, 5, 6, 9}; int N; HashSet<Integer> set2569 = new HashSet<>(Arrays.asList(2,5,6,9)); public int rotatedDigits(int N) { this.N = N; for(int i = 1; i < arr.length; i++) { count(arr[i], set2569.contains(arr[i])); } return result; } public void count(int curr, boolean has2569) { if(curr > N) return; if(has2569) result++; for(int i : arr) { count(curr * 10 + i, has2569 || set2569.contains(i)); } } }
-
617. Merge Two Binary Trees
Given two binary trees and imagine that when you put one of them to cover the other, some nodes of the two trees are overlapped while the others are not. You need to merge them into a new binary tree. The merge rule is that if two nodes overlap, then sum node values up as the new value of the merged node. Otherwise, the NOT null node will be used as the node of new tree. Example 1: Input: Tree 1 Tree 2 1 2 / \ / \ 3 2 1 3 / \ \ 5 4 7 Output: Merged tree: 3 / \ 4 5 / \ \ 5 4 7 Note: The merging process must start from the root nodes of both trees.
- A true easy
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { if(t1 == null) return t2; if(t2 == null) return t1; t1.val += t2.val; t1.left = mergeTrees(t1.left, t2.left); t1.right = mergeTrees(t1.right, t2.right); return t1; }
-
113. Path Sum II
Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. Note: A leaf is a node with no children. Example: Given the below binary tree and sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1 Return: [ [5,4,11,2], [5,8,4,5] ]
public List<List<Integer>> pathSum(TreeNode root, int sum) { List<List<Integer>> result = new ArrayList<>(); pathSum(result, new ArrayList<Integer>(), root, sum); return result; } public void pathSum(List<List<Integer>> result, ArrayList<Integer> temp, TreeNode root, int target) { if(root == null) return; if(root.val == target && root.left == null && root.right == null) { temp.add(root.val); result.add(new ArrayList<>(temp)); temp.remove(temp.size()-1); return; } temp.add(root.val); pathSum(result, temp, root.left, target-root.val); pathSum(result, temp, root.right, target-root.val); temp.remove(temp.size()-1); }
-
112. Path Sum
Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all the values along the path equals the given sum. Note: A leaf is a node with no children. Example: Given the below binary tree and sum = 22, 5 / \ 4 8 / / \ 11 13 4 / \ \ 7 2 1 return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22.
public boolean hasPathSum(TreeNode root, int target) { if(root == null) return false; if(target == root.val && root.left == null && root.right == null) return true; return hasPathSum(root.left, target-root.val) || hasPathSum(root.right, target-root.val); }
-
437. Path Sum III
You are given a binary tree in which each node contains an integer value. Find the number of paths that sum to a given value. The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes). The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000. Example: root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 10 / \ 5 -3 / \ \ 3 2 11 / \ \ 3 -2 1 Return 3. The paths that sum to 8 are: 1. 5 -> 3 2. 5 -> 2 -> 1 3. -3 -> 11
- An interested prefix sum solution. for reference
Key: Remove current sum from the map when this substree is visited.
public int pathSum(TreeNode root, int sum) { HashMap<Integer, Integer> preSum = new HashMap(); preSum.put(0,1); return helper(root, 0, sum, preSum); } public int helper(TreeNode root, int currSum, int target, HashMap<Integer, Integer> preSum) { if (root == null) { return 0; } currSum += root.val; int res = preSum.getOrDefault(currSum - target, 0); preSum.put(currSum, preSum.getOrDefault(currSum, 0) + 1); res += helper(root.left, currSum, target, preSum) + helper(root.right, currSum, target, preSum); preSum.put(currSum, preSum.get(currSum) - 1); return res; }
// path sum of this tree public int pathSum(TreeNode root, int sum) { if(root == null) return 0; return withRoot(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum); } // path sum of this tree must include the root public int withRoot(TreeNode root, int sum) { if(root == null) return 0; return (root.val == sum ? 1 : 0) + withRoot(root.left, sum-root.val) + withRoot(root.right, sum-root.val); }
- Return all sums with current root
int result = 0; public int pathSum(TreeNode root, int sum) { traverse(root, sum); return result; } public ArrayList<Integer> traverse(TreeNode root, int sum) { if(root == null) return new ArrayList<>(); ArrayList<Integer> left = traverse(root.left, sum); ArrayList<Integer> right = traverse(root.right, sum); ArrayList<Integer> total = new ArrayList<>(); total.addAll(left); total.addAll(right); for(int i = 0; i < total.size(); i++) { total.set(i, total.get(i)+root.val); if(total.get(i) == sum) result++; } total.add(root.val); if(root.val == sum) result++; return total; }
-
406. Queue Reconstruction by Height
406. Queue Reconstruction by Height
Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue. Note: The number of people is less than 1,100. Example Input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] Output: [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
- O(N^2)
Process people from tall to short. Insert the current person into its proper postion (k), and shit other people to the right. Invariant: all people on the right side of the insertion position are taller than the current person, and on the left side there are k people taller than the current person.
public int[][] reconstructQueueFromTalltoShort(int[][] people) { LinkedList<int[]> result = new LinkedList<>(); Arrays.sort(people, (a,b)->(a[0]==b[0] ? a[1]-b[1] : b[0]-a[0])); for(int[] p : people) result.add(p[1], p); return result.toArray(new int[0][]); // if the T[] arr is not enough to store the list, a new array will be returned // this empty array here is just to indicate the type }
Find the the person should be on position i for each pass. This person has the smallest height among all people with zero k. Update k through the iteration.
public int[][] reconstructQueueFromKEqualsZero(int[][] people) { int[][] arr = new int[people.length][]; for(int i = 0; i < arr.length; i++) arr[i] = people[i].clone(); for(int i = 0; i < arr.length; i++) { int k = -1; for(int j = i; j < arr.length; j++) { if(arr[j][1] == 0) { if(k == -1 || arr[j][0] < arr[k][0]) k = j; } } swap(people, i, k); swap(arr, i, k); for(int j = i+1; j < arr.length; j++) { if(arr[j][0] <= arr[i][0]) arr[j][1]--; } } return people; } public void swap(int[][] arr, int i, int j) { int[] temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; }
-
666. Path Sum IV
If the depth of a tree is smaller than 5, then this tree can be represented by a list of three-digits integers. For each integer in this list: The hundreds digit represents the depth D of this node, 1 <= D <= 4. The tens digit represents the position P of this node in the level it belongs to, 1 <= P <= 8. The position is the same as that in a full binary tree. The units digit represents the value V of this node, 0 <= V <= 9. Given a list of ascending three-digits integers representing a binary tree with the depth smaller than 5, you need to return the sum of all paths from the root towards the leaves. Example 1: Input: [113, 215, 221] Output: 12 Explanation: The tree that the list represents is: 3 / \ 5 1 The path sum is (3 + 5) + (3 + 1) = 12. Example 2: Input: [113, 221] Output: 4 Explanation: The tree that the list represents is: 3 \ 1 The path sum is (3 + 1) = 4.
- Iterative counting the leafs of each node Map < position, cnt >
Note: Another easy solution is to convert the array into a tree, then traverse. A trick is to use a HashMap to represents the tree. Map< position, value>
public int pathSum(int[] nums) { HashMap<Integer, Integer> cnt = new HashMap<>(); // cnt of each node in the calculation // equals the number of leafs in the substree for(int j = nums.length-1; j >= 0; j--) { int i = nums[j]; int level = i / 100; // level of current node int pos = (i - (i/100) * 100) / 10; // position of current node int p1 = 2 * pos - 1, p2 = p1 + 1; // position of left and right child int k = i / 10, k1 = (level+1) * 10 + p1, k2 = (level+1) * 10 + p2; if(!cnt.containsKey(k1) && !cnt.containsKey(k2)) { // leaf cnt.put(k, 1); } else { // internal int left = cnt.getOrDefault(k1, 0), right = cnt.getOrDefault(k2, 0); cnt.put(k, left + right); } } int result = 0; for(int i : nums) { int key = i / 10, value = i % 10; result += cnt.get(key) * value; } return result; }
-
338. Counting Bits
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's in their binary representation and return them as an array. Example 1: Input: 2 Output: [0,1,1] Example 2: Input: 5 Output: [0,1,1,2,1,2] Follow up: It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass? Space complexity should be O(n). Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.
- Easy DP
public int[] countBits(int num) { int[] dp = new int[num+1]; for(int i = 0; i <= num; i++) { if((i & 1) == 1) { dp[i] = dp[i-1]+1; } else { dp[i] = dp[i >> 1]; } } return dp; }
-
226. Invert Binary Tree
Invert a binary tree. Example: Input: 4 / \ 2 7 / \ / \ 1 3 6 9 Output: 4 / \ 7 2 / \ / \ 9 6 3 1 Important: This problem was inspired by this original tweet by Max Howell: Google: 90% of our engineers use the software you wrote (Homebrew), but you can’t invert a binary tree on a whiteboard so f*** off.
- Easy
public TreeNode invertTree(TreeNode root) { if(root == null) return null; TreeNode temp = root.left; root.left = root.right; root.right = temp; invertTree(root.right); invertTree(root.left); return root; }
-
114. Flatten Binary Tree to Linked List
114. Flatten Binary Tree to Linked List
Given a binary tree, flatten it to a linked list in-place. For example, given the following tree: 1 / \ 2 5 / \ \ 3 4 6 The flattened tree should look like: 1 \ 2 \ 3 \ 4 \ 5 \ 6
- Return tail of the flattened subtree
class Solution { public void flatten(TreeNode root) { go(root); } public TreeNode go(TreeNode root) { if(root == null) return null; if(root.left == null && root.right == null) { return root; } TreeNode tl = go(root.left); TreeNode tr = go(root.right); if(tr == null) { root.right = root.left; root.left = null; return tl; } else if(tl == null) { return tr; } else { tl.right = root.right; root.right = root.left; root.left = null; return tr; } } }
-
1071. Greatest Common Divisor of Strings
1071. Greatest Common Divisor of Strings
For strings S and T, we say "T divides S" if and only if S = T + ... + T (T concatenated with itself 1 or more times) Return the largest string X such that X divides str1 and X divides str2. Example 1: Input: str1 = "ABCABC", str2 = "ABC" Output: "ABC" Example 2: Input: str1 = "ABABAB", str2 = "ABAB" Output: "AB" Example 3: Input: str1 = "LEET", str2 = "CODE" Output: "" Note: 1 <= str1.length <= 1000 1 <= str2.length <= 1000 str1[i] and str2[i] are English uppercase letters.
- Brutal Force
class Solution { public String gcdOfStrings(String s1, String s2) { int l1 = s1.length(), l2 = s2.length(); String result = ""; for(int i = 0; i < Math.min(l1, l2); i++) { if(s1.charAt(i) != s2.charAt(i)) return ""; int l = i+1; if(l1 % l != 0 || l2 % l != 0) continue; String s = s1.substring(0, i+1); if(check(s1, s) && check(s2, s)) { result = s; } } return result; } public boolean check(String s, String t) { if(s.equals(t)) return true; if(s.substring(0, t.length()).equals(t)) { return check(s.substring(t.length()), t); } return false; } } } /** * Your NumMatrix object will be instantiated and called as such: * NumMatrix obj = new NumMatrix(matrix); * obj.update(row,col,val); * int param_2 = obj.sumRegion(row1,col1,row2,col2); */
-
308. Range Sum Query 2D - Mutable
308. Range Sum Query 2D - Mutable
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). Range Sum Query 2D The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8. Example: Given matrix = [ [3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5] ] sumRegion(2, 1, 4, 3) -> 8 update(3, 2, 2) sumRegion(2, 1, 4, 3) -> 10
- Segment Tree
class NumMatrix { int[] arr; int[][] matrix; public NumMatrix(int[][] matrix) { if(!(matrix == null || matrix.length == 0)) { this.matrix = matrix; int n = matrix.length * matrix[0].length; int h = (int)Math.ceil(Math.log(n)/Math.log(2)); int len = (int)Math.pow(2, h+1)-1; arr = new int[len]; build(0, n-1, 0); } } public int build(int start, int end, int index) { if(start == end) { arr[index] = getValue(start); } else { int mid = start + (end-start) / 2; arr[index] = build(start, mid, index * 2 + 1) + build(mid+1, end, index * 2 + 2); } return arr[index]; } public int getValue(int n) { int i = n / matrix[0].length; int j = n % matrix[0].length; return matrix[i][j]; } public int sumRegion(int row1, int col1, int row2, int col2) { if(arr == null) return 0; int result = 0; for(int i = row1; i <= row2; i++) { int start = i * matrix[0].length + col1; int end = start + (col2-col1); result += query(start, end, 0, matrix.length*matrix[0].length-1, 0); } return result; } public void update(int row, int col, int val) { if(arr == null) return; int diff = val - matrix[row][col]; matrix[row][col] = val; update(row * matrix[0].length+col, diff, 0, matrix.length*matrix[0].length-1, 0); } public void update(int target, int diff, int start, int end, int index) { arr[index] += diff; if(start == end) return; int mid = start + (end-start) / 2; if(target <= mid) update(target, diff, start, mid, 2*index+1); else update(target, diff, mid+1, end, 2*index+2); } public int query(int i, int j, int start, int end, int index) { if(i <= start && j >= end) return arr[index]; if(i > end || j < start) return 0; int mid = start + (end-start) / 2; return query(i, j, start, mid, 2 * index + 1) + query(i, j, mid+1, end, 2 * index + 2); } } /** * Your NumMatrix object will be instantiated and called as such: * NumMatrix obj = new NumMatrix(matrix); * obj.update(row,col,val); * int param_2 = obj.sumRegion(row1,col1,row2,col2); */
-
395. Longest Substring with At Least K Repeating Characters
395. Longest Substring with At Least K Repeating Characters
Find the length of the longest substring T of a given string (consists of lowercase letters only) such that every character in T appears no less than k times. Example 1: Input: s = "aaabb", k = 3 Output: 3 The longest substring is "aaa", as 'a' is repeated 3 times. Example 2: Input: s = "ababbc", k = 2 Output: 5 The longest substring is "ababb", as 'a' is repeated 2 times and 'b' is repeated 3 times.
- Find the invalid characters, and use them to divide the input string.
Original problem can be greedily shrinked into subproblems.
public int longestSubstring(String s, int k) { return count(s, k); } public int count(String s, int k) { if(s.length() == 0) return 0; int[] cnt = new int[26]; for(char c : s.toCharArray()) cnt[c-'a']++; boolean allValid = true; for(int i = 0; i < 26; i++) { if(cnt[i] > 0 && cnt[i] < k) { allValid = false; cnt[i] = -1; } } if(allValid) return s.length(); int result = 0, l = 0, r = 0; while(r < s.length()) { if(cnt[s.charAt(r)-'a'] == -1) { result = Math.max(result, count(s.substring(l, r), k)); r++; l = r; } else { r++; } } if(l < s.length()) result = Math.max(result, count(s.substring(l, s.length()), k)); return result; }
-
307. Range Sum Query - Mutable
307. Range Sum Query - Mutable
Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. The update(i, val) function modifies nums by updating the element at index i to val. Example: Given nums = [1, 3, 5] sumRange(0, 2) -> 9 update(1, 2) sumRange(0, 2) -> 8 Note: The array is only modifiable by the update function. You may assume the number of calls to update and sumRange function is distributed evenly.
- Segment Tree
My first time to implement the segment tree.
class NumArray { int[] arr; int[] nums; public NumArray(int[] nums) { if(!(nums == null || nums.length == 0)) { this.nums = nums; int n = nums.length; int h = (int)Math.ceil(Math.log(n) / Math.log(2)); int len = (int)Math.pow(2, h+1) - 1; this.arr = new int[len]; build(0, n-1, 0); } } public int build(int start, int end, int index) { if(start == end) { arr[index] = nums[start]; } else { int mid = start + (end-start) / 2; arr[index] = build(start, mid, 2 * index + 1) + build(mid+1, end, 2 * index + 2); } return arr[index]; } public int query(int i, int j, int start, int end, int index) { if(start > j || end < i) return 0; if(start >= i && end <= j) return arr[index]; int mid = start + (end-start) / 2; return query(i, j, start, mid, 2 * index + 1) + query(i, j, mid+1, end, 2 * index + 2); } public void update(int start, int end, int index, int targetIndex, int diff) { arr[index] += diff; if(start == end) { return; } int mid = start + (end-start) / 2; if(targetIndex <= mid) update(start, mid, 2 * index + 1, targetIndex, diff); else update(mid+1, end, 2 * index + 2, targetIndex, diff); } public void update(int i, int val) { if(nums == null) return; int diff = val - nums[i]; nums[i] = val; update(0, nums.length-1, 0, i, diff); } public int sumRange(int i, int j) { if(nums == null) return 0; return query(i, j, 0, nums.length-1, 0); } }
-
326. Power of Three; 231. Power of Two; 342. Power of Four
326. Power of Three; 231. Power of Two; 342. Power of Four
Given an integer, write a function to determine if it is a power of three. Example 1: Input: 27 Output: true
- Base conversion
public boolean isPowerOfThree(int n) { return Integer.toString(n, 3).matches("^10*$"); }
-
191. Number of 1 Bits
Write a function that takes an unsigned integer and return the number of '1' bits it has (also known as the Hamming weight). Example 1: Input: 00000000000000000000000000001011 Output: 3 Explanation: The input binary string 00000000000000000000000000001011 has a total of three '1' bits. Example 2: Input: 00000000000000000000000010000000 Output: 1 Explanation: The input binary string 00000000000000000000000010000000 has a total of one '1' bit. Example 3: Input: 11111111111111111111111111111101 Output: 31 Explanation: The input binary string 11111111111111111111111111111101 has a total of thirty one '1' bits.
public int hammingWeight2(int n) { int result = 0; for(int i = 0; i < 32; i++) { result += ((n >>> i) & 1); } return result; }
public int hammingWeight(int n) { int result = 0; while(n != 0) { n &= (n-1); result++; } return result; }
-
172. Factorial Trailing Zeroes
172. Factorial Trailing Zeroes
Given an integer n, return the number of trailing zeroes in n!. Example 1: Input: 3 Output: 0 Explanation: 3! = 6, no trailing zero. Example 2: Input: 5 Output: 1 Explanation: 5! = 120, one trailing zero.
public int trailingZeroes(int n) { if(n == 0) return 0; return n / 5 + trailingZeroes(n / 5); }
-
52. N-Queens II
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other. Given an integer n, return the number of distinct solutions to the n-queens puzzle. Example: Input: 4 Output: 2 Explanation: There are two distinct solutions to the 4-queens puzzle as shown below. [ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ]
- Easy
ArrayList<int[]> qs; boolean[] js; int result = 0; public int totalNQueens(int n) { qs = new ArrayList<>(); js = new boolean[n]; build(n, 0); return result; } public void build(int n, int row) { if(row == n) { result++; return; } for(int j = 0; j < n; j++) { if(!check(row, j)) continue; qs.add(new int[] {row, j}); js[j] = true; build(n, row+1); js[j] = false; qs.remove(qs.size()-1); } } public boolean check(int i, int j) { if(js[j]) return false; for(int[] q : qs) { if(Math.abs(q[0]-i) == Math.abs(q[1]-j)) return false; } return true; }
-
77. Combinations
Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. Example: Input: n = 4, k = 2 Output: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
- Easy
public List<List<Integer>> combine(int n, int k) { List<List<Integer>> result = new ArrayList<>(); backtrack(n, k, new ArrayList<Integer>(), result, 1); return result; } public void backtrack(int n, int k, ArrayList<Integer> temp, List<List<Integer>> result, int start) { if(temp.size() == k) { result.add(new ArrayList<>(temp)); return; } for(int i = start; i <= n; i++) { temp.add(i); backtrack(n, k, temp, result, i+1); temp.remove(temp.size()-1); } }
-
454. 4Sum II
Given four lists A, B, C, D of integer values, compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero. To make problem a bit easier, all A, B, C, D have same length of N where 0 ≤ N ≤ 500. All integers are in the range of -228 to 228 - 1 and the result is guaranteed to be at most 231 - 1. Example: Input: A = [ 1, 2] B = [-2,-1] C = [-1, 2] D = [ 0, 2] Output: 2 Explanation: The two tuples are: 1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0 2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0
- One HashMap
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) { HashMap<Integer, Integer> map = new HashMap<>(); HashMap<Integer, Integer> map2 = new HashMap<>(); for(int a : A) { for(int b : B) map.put(a+b, map.getOrDefault(a+b, 0)+1); } int result = 0; for(int c : C) { for(int d : D) { if(map.containsKey(-(c+d))) { result += map.get(-(c+d)); } } } return result; }
- Two HashMap
public int fourSumCount(int[] A, int[] B, int[] C, int[] D) { HashMap<Integer, Integer> map = new HashMap<>(); HashMap<Integer, Integer> map2 = new HashMap<>(); for(int a : A) { for(int b : B) map.put(a+b, map.getOrDefault(a+b, 0)+1); } for(int a : C) { for(int b : D) map2.put(a+b, map2.getOrDefault(a+b, 0)+1); } int result = 0; for(int key : map2.keySet()) { if(map.containsKey(-key)) { result += map.get(-key) * map2.get(key); } } return result; }
-
220. Contains Duplicate III
Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolute difference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k. Example 1: Input: nums = [1,2,3,1], k = 3, t = 0 Output: true Example 2: Input: nums = [1,0,1,1], k = 1, t = 2 Output: true Example 3: Input: nums = [1,5,9,1,5,9], k = 2, t = 3 Output: false
- TreeSet
public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { if(nums.length <= 1 || k == 0) return false; TreeMap<Integer, Integer> map = new TreeMap<>(); for(int i = 0; i < nums.length; i++) { Integer floor = map.floorKey(nums[i]), ceiling = map.ceilingKey(nums[i]); if(floor != null && (long)nums[i] - floor <= t) return true; if(ceiling != null && (long)ceiling - nums[i] <= t) return true; if(i >= k) { int prev = nums[i-k]; if(map.get(prev) == 1) map.remove(prev); else map.put(prev, map.get(prev)-1); } map.put(nums[i], map.getOrDefault(nums[i],0)+1); } return false; }
-
73. Set Matrix Zeroes
Given a m x n matrix, if an element is 0, set its entire row and column to 0. Do it in-place. Example 1: Input: [ [1,1,1], [1,0,1], [1,1,1] ] Output: [ [1,0,1], [0,0,0], [1,0,1] ] Example 2: Input: [ [0,1,2,0], [3,4,5,2], [1,3,1,5] ] Output: [ [0,0,0,0], [0,4,5,0], [0,3,1,0] ] Follow up: A straight forward solution using O(mn) space is probably a bad idea. A simple improvement uses O(m + n) space, but still not the best solution. Could you devise a constant space solution?
- in place use first col and row as zero indicators
public void setZeroes(int[][] matrix) { int n = matrix.length, m = matrix[0].length; boolean col = false, row = false; // true is there is a zero on first col or row for(int i = 0; i < n; i++) if(matrix[i][0] == 0) col = true; for(int j = 0; j < m; j++) if(matrix[0][j] == 0) row = true; for(int i = 1; i < n; i++) { for(int j = 1; j < m; j++) { if(matrix[i][j] == 0) { matrix[i][0] = 0; matrix[0][j] = 0; } } } for(int i = 1; i < n; i++) { for(int j = 1; j < m; j++) { if(matrix[i][j] == 0) { matrix[i][0] = 0; matrix[0][j] = 0; } } } for(int i = 1; i < n; i++) { for(int j = 1; j < m; j++) { if(matrix[i][0] == 0 || matrix[0][j] == 0) { matrix[i][j] = 0; } } } if(row) Arrays.fill(matrix[0], 0); if(col) for(int i = 0; i < n; i++) matrix[i][0] = 0; }
- In place with dummy value represents transformed zero
public int thirdMaxQ(int[] nums) { PriorityQueue<Integer> q = new PriorityQueue<>(); HashSet<Integer> set = new HashSet<>(); for(int i : nums) set.add(i); for(int i : set) { if(q.size() < 3) q.offer(i); else if(q.peek() < i) { q.poll(); q.offer(i); } } if(q.size() == 2) q.poll(); return q.peek(); }
- Intuitive
public void setZeroes(int[][] matrix) { HashSet<Integer> set = new HashSet<>(); for(int[] arr : matrix) { for(int i : arr) set.add(i); } int n = 0; while(true) { if(set.contains(n)) n++; else break; } for(int i = 0; i < matrix.length; i++) { for(int j = 0; j < matrix[0].length; j++) { if(matrix[i][j] == 0) { for(int k = 0; k < matrix[0].length; k++) { if(matrix[i][k] != 0) matrix[i][k] = n; } for(int k = 0; k < matrix.length; k++) if(matrix[k][j] != 0) matrix[k][j] = n; } } } for(int i = 0; i < matrix.length; i++) { for(int j = 0; j < matrix[0].length; j++) { if(matrix[i][j] == n) matrix[i][j] = 0; } } }
-
285. Inorder Successor in BST
Given a binary search tree and a node in it, find the in-order successor of that node in the BST. The successor of a node p is the node with the smallest key greater than p.val. Example 1: Input: root = [2,1,3], p = 1 Output: 2 Explanation: 1's in-order successor node is 2. Note that both p and the return value is of TreeNode type.
- Iterative
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { ArrayDeque<TreeNode> stack = new ArrayDeque<>(); TreeNode curr = root; int prev = Integer.MAX_VALUE; while(curr != null || !stack.isEmpty()) { while(curr != null) { stack.push(curr); curr = curr.left; } curr = stack.pop(); if(prev == p.val) return curr; prev = curr.val; curr = curr.right; } return null; }
- Recursive
class Solution { TreeNode result; public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { traverse(root, p.val); return result; } public void traverse(TreeNode root, int v) { if(root == null) return; if(result != null) return; traverse(root.left, v); if(result == null && root.val > v) result = root; if(result != null) return; traverse(root.right, v); } }
-
414. Third Maximum Number
Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n). Example 1: Input: [3, 2, 1] Output: 1 Explanation: The third maximum is 1. Example 2: Input: [1, 2] Output: 2 Explanation: The third maximum does not exist, so the maximum (2) is returned instead. Example 3: Input: [2, 2, 3, 1] Output: 1 Explanation: Note that the third maximum here means the third maximum distinct number. Both numbers with value 2 are both considered as second maximum.
- Heap
public int thirdMaxQ(int[] nums) { PriorityQueue<Integer> q = new PriorityQueue<>(); HashSet<Integer> set = new HashSet<>(); for(int i : nums) set.add(i); for(int i : set) { if(q.size() < 3) q.offer(i); else if(q.peek() < i) { q.poll(); q.offer(i); } } if(q.size() == 2) q.poll(); return q.peek(); }
- Intuitive
public int thirdMax(int[] nums) { Integer m1 = null, m2 = null, m3 = null; for(int i : nums) { if(m1 == null) m1 = i; else if(m1 < i) { m3 = m2; m2 = m1; m1 = i; } else if(m1 > i) { if(m2 == null) m2 = i; else if(m2 < i) { m3 = m2; m2 = i; } else if(m2 > i) { if(m3 == null) m3 = i; else if(m3 < i) m3 = i; } } } if(m3 != null) return m3; return m1; }
-
328. Odd Even Linked List
Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes. You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity. Example 1: Input: 1->2->3->4->5->NULL Output: 1->3->5->2->4->NULL Example 2: Input: 2->1->3->5->6->4->7->NULL Output: 2->3->6->7->1->5->4->NULL Note: The relative order inside both the even and odd groups should remain as it was in the input. The first node is considered odd, the second node even and so on ...
- One pass
public ListNode oddEvenList(ListNode head) { if(head == null || head.next == null || head.next.next == null) return head; ListNode odd = head, prev = head.next, curr = head.next.next; // prev always even, curr always odd // move curr behind the odd while(curr != null) { prev.next = curr.next; curr.next = odd.next; odd.next = curr; odd = odd.next; prev = prev.next; if(prev == null) break; curr = prev.next; } return head; }
- Move odd to left for one step in a single traverse O(N2)
This approach is a trash…
public ListNode oddEvenList(ListNode head) { if(head == null) return null; ListNode curr = head, prev = null; int len = 1; while(curr.next != null) { prev = curr; curr = curr.next; len++; } ListNode tail = null; if(len % 2 == 1) { len--; if(len > 1) { tail = curr; prev.next = null; } } curr = head; ListNode n = null; while(len >= 4) { int i = len - 2; prev = curr; n = prev.next; while(i > 0) { prev.next = n.next; n.next = n.next.next; prev.next.next = n; prev = prev.next.next; n = prev.next; i -= 2; } curr = curr.next; len -= 2; } if(tail != null) { tail.next = curr.next; curr.next = tail; } return head; }
-
280. Wiggle Sort
Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3].... Example: Input: nums = [3,5,2,1,6,4] Output: One possible answer is [3,5,1,6,2,4]
public void wiggleSort(int[] nums) { Arrays.sort(nums); int[] result = new int[nums.length]; int cnt = 0; for(int i = (result.length-1) % 2 == 1 ? result.length-2 : result.length-1; i >= 0; i -= 2) { result[i] = nums[cnt++]; } for(int i = (result.length-1) % 2 == 0 ? result.length-2 : result.length-1; i >= 0; i -= 2) { result[i] = nums[cnt++]; } System.arraycopy(result, 0, nums, 0, nums.length); }
-
324. Wiggle Sort II
Given an unsorted array nums, reorder it such that nums[0] < nums[1] > nums[2] < nums[3].... Example 1: Input: nums = [1, 5, 1, 1, 6, 4] Output: One possible answer is [1, 4, 1, 5, 1, 6]. Example 2: Input: nums = [1, 3, 2, 2, 3, 1] Output: One possible answer is [2, 3, 1, 3, 1, 2]. Note: You may assume all input has valid answer. Follow Up: Can you do it in O(n) time and/or in-place with O(1) extra space?
public void wiggleSort(int[] nums) { Arrays.sort(nums); int[] result = new int[nums.length]; int cnt = 0; for(int i = (result.length-1) % 2 == 1 ? result.length-2 : result.length-1; i >= 0; i -= 2) { result[i] = nums[cnt++]; } for(int i = (result.length-1) % 2 == 0 ? result.length-2 : result.length-1; i >= 0; i -= 2) { result[i] = nums[cnt++]; } System.arraycopy(result, 0, nums, 0, nums.length); }
-
130. Surrounded Regions
Given a 2D board containing 'X' and 'O' (the letter O), capture all regions surrounded by 'X'. A region is captured by flipping all 'O's into 'X's in that surrounded region. Example: X X X X X O O X X X O X X O X X After running your function, the board should be: X X X X X X X X X X X X X O X X Explanation: Surrounded regions shouldn’t be on the border, which means that any 'O' on the border of the board are not flipped to 'X'. Any 'O' that is not on the border and it is not connected to an 'O' on the border will be flipped to 'X'. Two cells are connected if they are adjacent cells connected horizontally or vertically.
class Solution { int[] xs = {0,0,1,-1}, ys = {1,-1,0,0}; public void solve(char[][] board) { if(board == null || board.length == 0 || board[0] == null || board[0].length == 0) return; for(int i = 0; i < board.length; i++) { mark(board, i, 0); mark(board, i, board[0].length-1); } for(int j = 0; j < board[0].length; j++) { mark(board, 0, j); mark(board, board.length-1, j); } for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { char c = board[i][j]; if(c == 'B') board[i][j] = 'O'; if(c == 'O') board[i][j] = 'X'; } } } public void mark(char[][] board, int i, int j) { if(board[i][j] == 'B' || board[i][j] == 'X' ) return; board[i][j] = 'B'; for(int k = 0; k < 4; k++) { int nx = xs[k] + i, ny = ys[k] + j; if(nx >= 0 && ny >= 0 && nx < board.length && ny < board[0].length && board[nx][ny] != 'B' && board[nx][ny] != 'X') { mark(board, nx, ny); } } } }
-
384. Shuffle an Array
Shuffle a set of numbers without duplicates. Example: // Init an array with set 1, 2, and 3. int[] nums = {1,2,3}; Solution solution = new Solution(nums); // Shuffle the array [1,2,3] and return its result. Any permutation of [1,2,3] must equally likely to be returned. solution.shuffle(); // Resets the array back to its original configuration [1,2,3]. solution.reset(); // Returns the random shuffling of array [1,2,3]. solution.shuffle();
- O(N)
class Solution { int[] original; int[] shuffle; public Solution(int[] nums) { original = nums; shuffle = nums.clone(); } /** Resets the array to its original configuration and return it. */ public int[] reset() { return original; } /** Returns a random shuffling of the array. */ public int[] shuffle() { Random r = new Random(); for(int i = 0; i < shuffle.length; i++) { // make sure that i th element is randomly picked int index = i + r.nextInt(shuffle.length-i); int temp = shuffle[i]; shuffle[i] = shuffle[index]; shuffle[index] = temp; } return shuffle; } }
- O(N^2)
class Solution { int[] original; public Solution(int[] nums) { original = nums; } /** Resets the array to its original configuration and return it. */ public int[] reset() { return original; } /** Returns a random shuffling of the array. */ public int[] shuffle() { int len = original.length; // index: 0 -> len-1 Random r = new Random(); int[] shuffle = new int[len]; ArrayList<Integer> arr = new ArrayList<>(); for(int i : original) arr.add(i); int i = 0; while(arr.size() > 0) { int index = r.nextInt(arr.size()); shuffle[i++] = arr.remove(index); } return shuffle; } }
-
116. Populating Next Right Pointers in Each Node
116. Populating Next Right Pointers in Each Node
You are given a perfect binary tree where all leaves are on the same level, and every parent has two children. The binary tree has the following definition: struct Node { int val; Node *left; Node *right; Node *next; } Populate each next pointer to point to its next right node. If there is no next right node, the next pointer should be set to NULL. Initially, all next pointers are set to NULL.
- Iterative level by level
Utilize next pointers on the upper level
public Node connect(Node root) { if(root == null) return root; Node head = root, curr = head; while(head.left != null) { curr = head; while(curr != null) { curr.left.next = curr.right; curr.right.next = curr.next == null ? null : curr.next.left; curr = curr.next; } head = head.left; } return root; }
- Recursion with pairs
public Node connect(Node root) { if(root == null) return null; connect(root.left, root.right); return root; } public void connect(Node left, Node right) { if(left == null) return; left.next = right; connect(left.left, left.right); connect(right.left, right.right); connect(left.right, right.left); }
- Recursion
public Node connect(Node root) { if(root == null || root.left == null) return root; root.left.next = root.right; root.right.next = root.next == null ? null : root.next.left; connect(root.left); connect(root.right); return root; }
-
131. Palindrome Partitioning
Given a string s, partition s such that every substring of the partition is a palindrome. Return all possible palindrome partitioning of s. Example: Input: "aab" Output: [ ["aa","b"], ["a","a","b"] ]
- backtrack
public List<List<String>> partition(String s) { List<List<String>> result = new ArrayList<>(); backtrack(result, new ArrayList<String>(), s, 0); return result; } public void backtrack(List<List<String>> result, List<String> temp, String s, int start) { if(start == s.length()) { result.add(new ArrayList<>(temp)); } for(int i = start; i < s.length(); i++) { String sub = s.substring(start, i+1); if(isP(sub)) { temp.add(sub); backtrack(result, temp, s, i+1); temp.remove(temp.size()-1); } } } public boolean isP(String s) { for(int l = 0, r = s.length()-1; l < r; l++, r--) { if(s.charAt(l) != s.charAt(r)) return false; } return true; }
-
230. Kth Smallest Element in a BST
230. Kth Smallest Element in a BST
Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. Note: You may assume k is always valid, 1 ≤ k ≤ BST's total elements. Example 1: Input: root = [3,1,4,null,2], k = 1 3 / \ 1 4 \ 2 Output: 1 Example 2: Input: root = [5,3,6,2,4,null,null,1], k = 3 5 / \ 3 6 / \ 2 4 / 1 Output: 3 Follow up: What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine?
- inorder traverse
class Solution { int cnt = 0; int result = 0; public int kthSmallest(TreeNode root, int k) { traverse(root, k); return result; } public void traverse(TreeNode root, int k) { if(root == null) return; traverse(root.left, k); cnt++; if(cnt == k) { result = root.val; } traverse(root.right, k); }
-
169. Majority Element
Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times. You may assume that the array is non-empty and the majority element always exist in the array. Example 1: Input: [3,2,3] Output: 3 Example 2: Input: [2,2,1,1,1,2,2] Output: 2
- Space O(N)
Decrease searching space
[7, 7, 5, 7, 5, 1 | 5, 7 | 5, 5, 7, 7 | 7, 7, 7, 7] cnt= 0 0 0 maj= 7 5 5 7
public int majorityElement(int[] nums) { int majority = 0, cnt = 0; for(int i : nums) { if(cnt == 0) majority = i; cnt += majority == i ? 1 : -1; } return majority; }
- HashMap
public int majorityElementMap(int[] nums) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i : nums) map.put(i, map.getOrDefault(i, 0)+1); Map.Entry<Integer, Integer> max = null; for(Map.Entry<Integer, Integer> e : map.entrySet()) { if(max == null || e.getValue() > max.getValue()) max = e; } return max.getKey(); }
-
1008. Construct Binary Search Tree from Preorder Traversal
1008. Construct Binary Search Tree from Preorder Traversal
Return the root node of a binary search tree that matches the given preorder traversal. (Recall that a binary search tree is a binary tree where for every node, any descendant of node.left has a value < node.val, and any descendant of node.right has a value > node.val. Also recall that a preorder traversal displays the value of the node first, then traverses node.left, then traverses node.right.) Example 1: Input: [8,5,1,7,10,12] Output: [8,5,10,1,7,null,12] 1 <= preorder.length <= 100 The values of preorder are distinct.
public TreeNode bstFromPreorder(int[] preorder) { return build(preorder, 0, preorder.length-1); } public TreeNode build(int[] preorder, int start, int end) { if(start > end) return null; TreeNode root = new TreeNode(preorder[start]); int i = start + 1; while(i <= end && preorder[i] < root.val) i++; root.left = build(preorder, start+1, i-1); root.right = build(preorder, i, end); return root; }
-
134. Gas Station
There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. Return the starting gas station's index if you can travel around the circuit once in the clockwise direction, otherwise return -1. Note: If there exists a solution, it is guaranteed to be unique. Both input arrays are non-empty and have the same length. Each element in the input arrays is a non-negative integer. Example 1: Input: gas = [1,2,3,4,5] cost = [3,4,5,1,2] Output: 3 Explanation: Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4 Travel to station 4. Your tank = 4 - 1 + 5 = 8 Travel to station 0. Your tank = 8 - 2 + 1 = 7 Travel to station 1. Your tank = 7 - 3 + 2 = 6 Travel to station 2. Your tank = 6 - 4 + 3 = 5 Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3. Therefore, return 3 as the starting index.
- One pass
Key: If it’s not able to travel from i to j, then it’s also not albe to travel from k to j, where k is in [i,j) So started at i and failed at j, the next possible starting station will be j+1
public int canCompleteCircuit(int[] gas, int[] cost) { int totalGas = gas[0], currGas = gas[0], start = 0; for(int i = 1; i < gas.length; i++) { if(currGas < cost[i-1]) { start = i; currGas = gas[i]; } else { currGas += gas[i] - cost[i-1]; } totalGas += gas[i] - cost[i-1]; } totalGas -= cost[cost.length-1]; if(totalGas < 0) return -1; return start; }
-
251. Flatten 2D Vector
Design and implement an iterator to flatten a 2d vector. It should support the following operations: next and hasNext. Example: Vector2D iterator = new Vector2D([[1,2],[3],[4]]); iterator.next(); // return 1 iterator.next(); // return 2 iterator.next(); // return 3 iterator.hasNext(); // return true iterator.hasNext(); // return true iterator.next(); // return 4 iterator.hasNext(); // return false Notes: Please remember to RESET your class variables declared in Vector2D, as static/class variables are persisted across multiple test cases. Please see here for more details. You may assume that next() call will always be valid, that is, there will be at least a next element in the 2d vector when next() is called. Follow up: As an added challenge, try to code it using only iterators in C++ or iterators in Java.
class Vector2D { int i = 0, j = 0; int[][] nums; public Vector2D(int[][] v) { nums = v; } public int next() { hasNext(); int v = nums[i][j]; if(j == nums[i].length-1) { j = 0; i++; } else { j++; } return v; } public boolean hasNext() { while(i < nums.length && nums[i].length == 0) i++; if(i == nums.length) return false; return true; } }
-
745. Prefix and Suffix Search
Given many words, words[i] has weight i. Design a class WordFilter that supports one function, WordFilter.f(String prefix, String suffix). It will return the word with given prefix and suffix with maximum weight. If no word exists, return -1. Examples: Input: WordFilter(["apple"]) WordFilter.f("a", "e") // returns 0 WordFilter.f("b", "") // returns -1 Note: words has length in range [1, 15000]. For each test case, up to words.length queries WordFilter.f may be made. words[i] has length in range [1, 10]. prefix, suffix have lengths in range [0, 10]. words[i] and prefix, suffix queries consist of lowercase letters only.
- Preprocess word to suffix + { + word
The trick: { ascill code –> ‘z’ + 1 [ —> ‘Z’ + 1 becoming the 27th node of the tier
O(NK^2 + QK)
class WordFilter { Node root; public WordFilter(String[] words) { root = new Node(); root.index = words.length-1; for(int i = 0; i < words.length; i++) { for(int j = words[i].length(); j >= 0; j--) { String s = words[i].substring(j); String w = s + "{" + words[i]; Node curr = root; for(int k = 0; k < w.length(); k++) { char c = w.charAt(k); if(curr.arr[c-'a'] == null) curr.arr[c-'a'] = new Node(); curr = curr.arr[c-'a']; curr.index = i; } } } } public int f(String prefix, String suffix) { String w = suffix + "{" + prefix; Node n = root; for(int i = 0; i < w.length(); i++) { char c = w.charAt(i); if(n.arr[c-'a'] == null) return -1; n = n.arr[c-'a']; } return n.index; } class Node { Node[] arr = new Node[27]; int index = -1; } }
- Two Tier
Intersection of two sets, pick the largest index
O(KN + Q(K+N))
class WordFilter { Node pre, suf; public WordFilter(String[] words) { // System.out.println(Arrays.toString(words)); pre = new Node(); suf = new Node(); HashSet<Integer> all = new HashSet<>(); for(int i = 0; i < words.length; i++) all.add(i); pre.set = all; suf.set = all; for(int i = 0; i < words.length; i++) { String w = words[i]; Node pc = pre, sc = suf; for(int j = 0; j < w.length(); j++) { char c = w.charAt(j); if(pc.arr[c-'a'] == null) pc.arr[c-'a'] = new Node(); pc = pc.arr[c-'a']; pc.set.add(i); } for(int j = w.length()-1; j >= 0; j--) { char c = w.charAt(j); if(sc.arr[c-'a'] == null) sc.arr[c-'a'] = new Node(); sc = sc.arr[c-'a']; sc.set.add(i); } } } public int f(String prefix, String suffix) { Node pc = pre, sc = suf; for(int i = 0; i < prefix.length(); i++) { char c = prefix.charAt(i); if(pc.arr[c-'a'] == null) return -1; pc = pc.arr[c-'a']; } for(int i = suffix.length()-1; i >= 0; i--) { char c = suffix.charAt(i); if(sc.arr[c-'a'] == null) return -1; sc = sc.arr[c-'a']; } int result = -1; for(int i : pc.set) { if(sc.set.contains(i)) { result = Math.max(result, i); } } return result; } class Node { Node[] arr = new Node[26]; HashSet<Integer> set = new HashSet<>(); } }
-
698. Partition to K Equal Sum Subsets
698. Partition to K Equal Sum Subsets
Given an array of integers nums and a positive integer k, find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. Example 1: Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 Output: True Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums. Note: 1 <= k <= len(nums) <= 16. 0 < nums[i] < 10000.
public boolean canPartitionKSubsets(int[] nums, int k) { if(nums == null || nums.length == 0) return false; boolean[] used = new boolean[nums.length]; int sum = 0; for(int i : nums) sum += i; // System.out.println(sum); if(sum % k != 0) return false; int target = sum / k; return backtrack(nums, used, target, 0, k, 0); } public boolean backtrack(int[] nums, boolean[] used, int target, int sum, int k, int start) { if(k == 1) return true; if(sum == target) return backtrack(nums, used, target, 0, k-1, 0); for(int i = start; i < nums.length; i++) { if(used[i]) continue; used[i] = true; boolean result = backtrack(nums, used, target, sum + nums[i], k, i+1); if(result) return true; used[i] = false; } return false; }
-
123. Best Time to Buy and Sell Stock IV
123. Best Time to Buy and Sell Stock IV
Say you have an array for which the i-th element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most k transactions. Note: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). Example 1: Input: [2,4,1], k = 2 Output: 2 Explanation: Buy on day 1 (price = 2) and sell on day 2 (price = 4), profit = 4-2 = 2. Example 2: Input: [3,2,6,5,0,3], k = 2 Output: 7 Explanation: Buy on day 2 (price = 2) and sell on day 3 (price = 6), profit = 6-2 = 4. Then buy on day 5 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
- dp[i]
public int maxProfit(int K, int[] prices) { if(K > prices.length / 2) { return greedy(prices); } int[] buy = new int[K+1], sell = new int[K+1]; Arrays.fill(buy, Integer.MIN_VALUE); for(int i = 0; i < prices.length; i++) { for(int j = 1; j <= K; j++) { buy[j] = Math.max(buy[j], sell[j-1]-prices[i]); sell[j] = Math.max(sell[j], buy[j]+prices[i]); } } return sell[K]; } public int greedy(int[] prices) { int result = 0; for(int i = 1; i < prices.length; i++) { if(prices[i]-prices[i-1] > 0) { result += prices[i]-prices[i-1]; } } return result; }
- dp[i][j]
public int maxProfit(int K, int[] prices) { if(K > prices.length / 2) { return greedy(prices); } if(prices.length == 0) return 0; int[][] dp = new int[K+1][prices.length]; for(int i = 1; i <= K; i++) { int min = prices[0]; for(int j = 1; j < prices.length; j++) { min = Math.min(min, prices[j]-dp[i-1][j-1]); dp[i][j] = Math.max(dp[i][j-1], prices[j]-min); } } return dp[K][prices.length-1]; }
-
123. Best Time to Buy and Sell Stock III
123. Best Time to Buy and Sell Stock III
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions. Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again). Example 1: Input: [3,3,5,0,0,3,1,4] Output: 6 Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3. Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3. Example 2: Input: [1,2,3,4,5] Output: 4 Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.
- All increasing continuous subarray
public int maxProfit(int[] prices) { int k = 2; int[] buy = new int[k+1], sell = new int[k+1]; Arrays.fill(buy, Integer.MIN_VALUE); for(int j = 0; j < prices.length; j++) { for(int t = 1; t <= 2; t++) { buy[t] = Math.max(buy[t], sell[t-1]-prices[j]); sell[t] = Math.max(sell[t], buy[t]+prices[j]); } } return sell[sell.length-1]; }
-
122. Best Time to Buy and Sell Stock II
122. Best Time to Buy and Sell Stock II
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. Find out how many ways to assign symbols to make sum of integers equal to target S. Example 1: Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3 There are 5 ways to assign symbols to make the sum of nums be target 3. Note: The length of the given array is positive and will not exceed 20. The sum of elements in the given array will not exceed 1000. Your output answer is guaranteed to be fitted in a 32-bit integer.
- All increasing continuous subarray
public int maxProfit(int[] prices) { int result = 0; int sum = 0; int l = 0, r = 1; while(r < prices.length) { while(r < prices.length && prices[r] >= prices[r-1]) r++; if(r-1 > l) { result += prices[r-1] - prices[l]; } l = r; r++; } return result; }
-
494. Target Sum
You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. Find out how many ways to assign symbols to make sum of integers equal to target S. Example 1: Input: nums is [1, 1, 1, 1, 1], S is 3. Output: 5 Explanation: -1+1+1+1+1 = 3 +1-1+1+1+1 = 3 +1+1-1+1+1 = 3 +1+1+1-1+1 = 3 +1+1+1+1-1 = 3 There are 5 ways to assign symbols to make the sum of nums be target 3. Note: The length of the given array is positive and will not exceed 20. The sum of elements in the given array will not exceed 1000. Your output answer is guaranteed to be fitted in a 32-bit integer.
- Recursion with memo
public int findTargetSumWays(int[] nums, int S) { return dp(nums, nums.length-1, Integer.valueOf(S), new HashMap<String, Integer>()); } public int dp(int[] nums, int i, int t, HashMap<String, Integer> map) { if(i == -1) return t == 0 ? 1 : 0; String k = "" + t + "," + i; if(map.containsKey(k)) return map.get(k); int cnt = dp(nums, i-1, t-nums[i], map) + dp(nums, i-1, t+nums[i], map); map.put(k, cnt); return cnt; }
- Bottom Up
dp[i] relies only on dp[i-1]; init dp[0][sum] = 1 (here sum is the offset + zero) the actual range of the sum (index j) is -sum ~ sum, plus offset sum –> 0 ~ 2*sum
class Solution { public int findTargetSumWaysMN(int[] nums, int S) { int sum = 0; for(int i : nums) sum += i; // sum is the offset if(S > sum) return 0; int[][] dp = new int[nums.length+1][sum * 2 + 1]; dp[0][sum] = 1; for(int i = 1; i < dp.length; i++) { for(int j = 0; j < dp[0].length; j++) { if(j+nums[i-1] < dp[0].length) dp[i][j] += dp[i-1][j+nums[i-1]]; if(j-nums[i-1] >= 0) dp[i][j] += dp[i-1][j-nums[i-1]]; } } // 0 -- -sum // x -- S return dp[dp.length-1][S + sum]; } public int findTargetSumWaysM(int[] nums, int S) { int sum = 0; for(int i : nums) sum += i; // sum is the offset if(S > sum) return 0; int[][] dp = new int[2][sum * 2 + 1]; dp[0][sum] = 1; for(int n : nums) { for(int j = 0; j < dp[0].length; j++) { if(j+n < dp[0].length) dp[1][j] += dp[0][j+n]; if(j-n >= 0) dp[1][j] += dp[0][j-n]; } dp[0] = dp[1]; dp[1] = new int[dp[1].length]; } // 0 -- -sum // x -- S return dp[0][S + sum]; } }
-
279. Perfect Squares
Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n. Example 1: Input: n = 12 Output: 3 Explanation: 12 = 4 + 4 + 4. Example 2: Input: n = 13 Output: 2 Explanation: 13 = 4 + 9.
public int numSquares(int n) { int[] dp = new int[n+1]; Arrays.fill(dp, n+1); dp[0] = 0; for(int c = 1; c * c <= n; c++) { for(int i = 1; i < dp.length; i++) { if(i-c*c >= 0) dp[i] = Math.min(dp[i], dp[i-c*c]+1); } } return dp[n]; } public int numSquares2D(int n) { ArrayList<Integer> coins = new ArrayList<>(); for(int i = 1; i * i <= n; i++) { coins.add(i * i); } int[][] dp = new int[coins.size()+1][n+1]; for(int i = 0; i < dp.length; i++) Arrays.fill(dp[i], n+1); for(int i = 0; i < dp.length; i++) dp[i][0] = 0; for(int i = 1; i < dp.length; i++) { for(int j = 1; j < dp[0].length; j++) { dp[i][j] = Math.min(dp[i-1][j], j-coins.get(i-1) >= 0 ? (1 + dp[i][j-coins.get(i-1)]) : n+1); } } return dp[dp.length-1][dp[0].length-1]; }
-
209. Minimum Size Subarray Sum
209. Minimum Size Subarray Sum
Given an array of n positive integers and a positive integer s, find the minimal length of a contiguous subarray of which the sum ≥ s. If there isn't one, return 0 instead. Example: Input: s = 7, nums = [2,3,1,2,4,3] Output: 2 Explanation: the subarray [4,3] has the minimal length under the problem constraint. Follow up: If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
public int minSubArrayLen(int s, int[] nums) { if(nums.length == 0) return 0; int sum = nums[0], l = 0, r = l; int result = nums.length+1; while(true) { if(sum < s) { if(r + 1 == nums.length) break; sum += nums[++r]; } else { result = Math.min(result, r-l+1); if(l == nums.length) break; sum -= nums[l++]; } } return result == nums.length+1 ? 0 : result; }
-
887. Super Egg Drop
You are given K eggs, and you have access to a building with N floors from 1 to N. Each egg is identical in function, and if an egg breaks, you cannot drop it again. You know that there exists a floor F with 0 <= F <= N such that any egg dropped at a floor higher than F will break, and any egg dropped at or below floor F will not break. Each move, you may take an egg (if you have an unbroken one) and drop it from any floor X (with 1 <= X <= N). Your goal is to know with certainty what the value of F is. What is the minimum number of moves that you need to know with certainty what F is, regardless of the initial value of F? Example 1: Input: K = 1, N = 2 Output: 2 Explanation: Drop the egg from floor 1. If it breaks, we know with certainty that F = 0. Otherwise, drop the egg from floor 2. If it breaks, we know with certainty that F = 1. If it didn't break, then we know with certainty F = 2. Hence, we needed 2 moves in the worst case to know what F is with certainty. Example 2: Input: K = 2, N = 6 Output: 3 Example 3: Input: K = 3, N = 14 Output: 4 Note: 1 <= K <= 100 1 <= N <= 10000
public int superEggDrop(int K, int N) { if(K == 1 || N == 1) return N; int[][] dp = new int[N+1][K+1]; int m = 0; while(dp[m][K] < N) { m++; for(int k = 1; k <= K; k++) { dp[m][k] = dp[m-1][k-1] + dp[m-1][k] + 1; } } return m; }
-
166. Fraction to Recurring Decimal
166. Fraction to Recurring Decimal
Given two integers representing the numerator and denominator of a fraction, return the fraction in string format. If the fractional part is repeating, enclose the repeating part in parentheses. Example 1: Input: numerator = 1, denominator = 2 Output: "0.5" Example 2: Input: numerator = 2, denominator = 1 Output: "2" Example 3: Input: numerator = 2, denominator = 3 Output: "0.(6)"
public String fractionToDecimal(int num, int den) { long numerator = num, denominator = den; int nega = numerator * denominator >= 0 ? 1 : -1; numerator = Math.abs(numerator); denominator = Math.abs(denominator); long b = numerator / denominator; long r = numerator % denominator; if(r == 0) { return "" + (b * nega); } HashMap<Long, Integer> map = new HashMap<>(); int p = 0; String result = ""; boolean repeat = false; while(true) { if(r == 0) break; if(map.containsKey(r)) { repeat = true; break; } map.put(r, p++); r *= 10; while(r < denominator) { result += "0"; r *= 10; p++; } long n = r / denominator; result += n; r = r % denominator; } if(repeat) { String sub = result.substring(map.get(r)); sub = "(" + sub + ")"; result = result.substring(0, map.get(r)) + sub; } result = b + "." + result; if(nega == -1) result = "-" + result; return result; }
-
1048. Longest String Chain
Given a list of words, each word consists of English lowercase letters. Let's say word1 is a predecessor of word2 if and only if we can add exactly one letter anywhere in word1 to make it equal to word2. For example, "abc" is a predecessor of "abac". A word chain is a sequence of words [word_1, word_2, ..., word_k] with k >= 1, where word_1 is a predecessor of word_2, word_2 is a predecessor of word_3, and so on. Return the longest possible length of a word chain with words chosen from the given list of words. Example 1: Input: ["a","b","ba","bca","bda","bdca"] Output: 4 Explanation: one of the longest word chain is "a","ba","bda","bdca". Note: 1 <= words.length <= 1000 1 <= words[i].length <= 16 words[i] only consists of English lowercase letters.
public int longestStrChain(String[] words) { if(words.length == 0) return 0; ArrayList<ArrayList<String>> arr = new ArrayList<>(); for(int i = 0; i <= 17; i++) arr.add(new ArrayList<>()); for(String s : words) { arr.get(s.length()).add(s); } HashMap<String, Integer> map = new HashMap<>(); for(int i = 16; i >= 1; i--) { for(String w : arr.get(i)) map.put(w, 1); for(String s : arr.get(i)) { for(String l : arr.get(i+1)) { if(isValid(s, l)) { map.put(s, Math.max(map.get(l)+1, map.get(s))); } } } } int result = 0; for(int v : map.values()) result = Math.max(v, result); return result; } public boolean isValid(String s, String l) { boolean flag = false; for(int i = 0, j = 0; i < s.length() && j < l.length(); i++, j++) { if(s.charAt(i) != l.charAt(j)) { if(flag) return false; flag = true; i--; } } return true; }
-
1055. Shortest Way to Form String
1055. Shortest Way to Form String
From any string, we can form a subsequence of that string by deleting some number of characters (possibly no deletions). Given two strings source and target, return the minimum number of subsequences of source such that their concatenation equals target. If the task is impossible, return -1. Example 1: Input: source = "abc", target = "abcbc" Output: 2 Explanation: The target "abcbc" can be formed by "abc" and "bc", which are subsequences of source "abc". Example 2: Input: source = "abc", target = "acdbc" Output: -1 Explanation: The target string cannot be constructed from the subsequences of source string due to the character "d" in target string. Example 3: Input: source = "xyz", target = "xzyxz" Output: 3 Explanation: The target string can be constructed as follows "xz" + "y" + "xz". Constraints: Both the source and target strings consist of only lowercase English letters from "a"-"z". The lengths of source and target string are between 1 and 1000.
public int shortestWay(String source, String target) { HashSet<Character> set = new HashSet<>(); for(char c : source.toCharArray()) set.add(c); for(char c : target.toCharArray()) { if(!set.contains(c)) return -1; } int j = 0, result = 0; for(int i = 0; i < target.length();) { char c = target.charAt(i); while(j < source.length() && source.charAt(j) != c) j++; if(j == source.length()) { j = 0; result++; } else { j++; i++; } } result++; return result; }
-
418. Sentence Screen Fitting
Given a rows x cols screen and a sentence represented by a list of non-empty words, find how many times the given sentence can be fitted on the screen. Note: A word cannot be split into two lines. The order of words in the sentence must remain unchanged. Two consecutive words in a line must be separated by a single space. Total words in the sentence won't exceed 100. Length of each word is greater than 0 and won't exceed 10. 1 ≤ rows, cols ≤ 20,000. Example 1: Input: rows = 2, cols = 8, sentence = ["hello", "world"] Output: 1 Explanation: hello--- world--- The character '-' signifies an empty space on the screen. Example 2: Input: rows = 3, cols = 6, sentence = ["a", "bcd", "e"] Output: 2 Explanation: a-bcd- e-a--- bcd-e- The character '-' signifies an empty space on the screen.
- A good version from the discussion
great explanation:
https://leetcode.com/problems/sentence-screen-fitting/discuss/90845/21ms-18-lines-Java-solution/95272
Imagine an infinite sentence that are concatenated by words from the given sentence, infiStr. We want to cut the infiStr properly and put a piece at each row of the screen. We maintain a pointer ptr. The ptr points to a position at infiStr, where next row will start. Cutting the infiStr and putting a piece at a row can be simulated as advancing the pointer by cols positions. After advancing the pointer, if ptr points to a space, it means the piece can fit in row perfectly. If ptr points to the middle of a word, we must retreat the pointer to the beginning of the word, because a word cannot be split into two lines.
public class Solution { public int wordsTyping(String[] sentence, int rows, int cols) { String s = String.join(" ", sentence) + " "; int start = 0, l = s.length(); for (int i = 0; i < rows; i++) { start += cols; if (s.charAt(start % l) == ' ') { start++; } else { while (start > 0 && s.charAt((start-1) % l) != ' ') { start--; } } } return start / s.length(); } }
public int wordsTyping(String[] arr, int rows, int cols) { int result = 0, p = 0, r = 0, remain = cols; ArrayList<Integer> memo = new ArrayList<>(); memo.add(0); while(r < rows) { if((remain == cols && remain >= arr[p].length()) || remain >= 1+arr[p].length()) { // enough space in this line remain -= remain == cols ? arr[p].length() : (1+arr[p].length()); if(p+1 == arr.length) result++; p = (p+1) % arr.length; } else { r++; memo.add(result); if(p == 0) break; remain = cols; } } int mult = rows / r; result = mult * result + memo.get(rows - mult * r); return result; }
-
221. Maximal Square
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. Example: Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 Output: 4
- Check diagonals.
p1 p3
p2 p4
public int maximalSquare(char[][] matrix) { if(matrix.length == 0) return 0; int[][] dp = new int[matrix.length+1][matrix[0].length+1]; int result = 0; for(int i = 1; i < dp.length; i++) { for(int j = 1; j < dp[0].length; j++) { if(matrix[i-1][j-1] == '1') { dp[i][j] = 1 + Math.min(Math.min(dp[i-1][j-1], dp[i-1][j]), dp[i][j-1]); result = Math.max(result, dp[i][j] * dp[i][j]); } } } return result; }
-
252. Meeting Rooms
Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings. Example 1: Input: [[0,30],[5,10],[15,20]] Output: false Example 2: Input: [[7,10],[2,4]] Output: true
public boolean canAttendMeetings(int[][] intervals) { Arrays.sort(intervals, (a,b)->(Integer.compare(a[0], b[0]))); for(int i = 1; i < intervals.length; i++) { if(intervals[i-1][1] > intervals[i][0]) return false; } return true; }
-
118. Pascal's Triangle
Given a non-negative integer numRows, generate the first numRows of Pascal's triangle. In Pascal's triangle, each number is the sum of the two numbers directly above it. Example: Input: 5 Output: [ [1], [1,1], [1,2,1], [1,3,3,1], [1,4,6,4,1] ]
public List<List<Integer>> generate(int numRows) { List<List<Integer>> result = new ArrayList<>(); if(numRows == 0) return result; result.add(Arrays.asList(1)); for(int i = 1; i < numRows; i++) { ArrayList<Integer> temp = new ArrayList<>(); List<Integer> lastRow = result.get(i-1); temp.add(1); for(int j = 1; j < lastRow.size(); j++) { temp.add(lastRow.get(j-1) + lastRow.get(j)); } temp.add(1); result.add(temp); } return result; }
-
784. Letter Case Permutation
Given a string S, we can transform every letter individually to be lowercase or uppercase to create another string. Return a list of all possible strings we could create. Examples: Input: S = "a1b2" Output: ["a1b2", "a1B2", "A1b2", "A1B2"] Input: S = "3z4" Output: ["3z4", "3Z4"] Input: S = "12345" Output: ["12345"] Note: S will be a string with length between 1 and 12. S will consist only of letters or digits.
public List<String> letterCasePermutation(String S) { List<String> result = new ArrayList<>(); generate(result, S, ""); return result; } public void generate(List<String> result, String s, String buffer) { if(buffer.length() == s.length()) { result.add(buffer); return; } int i = buffer.length(); char c = s.charAt(i); generate(result, s, buffer + c); if(Character.isLetter(c)) { String next = buffer; if(c >= 'a' && c <= 'z') { next += Character.toUpperCase(c); } else { next += Character.toLowerCase(c); } generate(result, s, next); } }
-
179. Largest Number
Given a list of non negative integers, arrange them such that they form the largest number.
Example 1:
Input: [10,2] Output: “210”
Example 2:
Input: [3,30,34,5,9] Output: “9534330”
Note: The result may be very large, so you need to return a string instead of an integer.
public String largestNumber(int[] nums) { PriorityQueue<String> q = new PriorityQueue<String>((a,b)->{ String s1 = a+b, s2 = b+a; return s2.compareTo(s1); }); for(int i : nums) { q.offer("" + i); } String r = ""; while(!q.isEmpty()) { r += q.poll(); } if(r.charAt(0) == '0') return "0"; return r; }
-
377. Combination Sum IV
Given an integer array with all positive numbers and no duplicates, find the number of possible combinations that add up to a positive integer target. Example: nums = [1, 2, 3] target = 4 The possible combination ways are: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1) Note that different sequences are counted as different combinations. Therefore the output is 7. Follow up: What if negative numbers are allowed in the given array? How does it change the problem? What limitation we need to add to the question to allow negative numbers? 1. either positive number or negative number should be used only one time 2. the maximum length of result set is given
public int combinationSum4(int[] candidates, int target) { int[] dp = new int[target+1]; dp[0] = 1; for(int i = 1; i < dp.length; i++) { for(int j : candidates) { if(i-j >= 0) { dp[i] += dp[i-j]; } } } return dp[target]; }
-
216. Combination Sum III
Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers. Note: All numbers will be positive integers. The solution set must not contain duplicate combinations. Example 1: Input: k = 3, n = 7 Output: [[1,2,4]] Example 2: Input: k = 3, n = 9 Output: [[1,2,6], [1,3,5], [2,3,4]]
public List<List<Integer>> combinationSum3(int k, int n) { List<List<Integer>> result = new ArrayList<>(); backtrack(k, n, result, new ArrayList<Integer>(), 1, 0); return result; } public void backtrack(int k, int n, List<List<Integer>> result, ArrayList<Integer> temp, int curr, int sum) { if(temp.size() > k) return; if(sum >= n) { if(sum == n && temp.size() == k) result.add(new ArrayList<>(temp)); return; } for(int i = curr; i < 10; i++) { temp.add(i); backtrack(k, n, result, temp, i+1, sum+i); temp.remove(temp.size()-1); } }
-
355. Design Twitter
Design a simplified version of Twitter where users can post tweets, follow/unfollow another user and is able to see the 10 most recent tweets in the user's news feed. Your design should support the following methods: postTweet(userId, tweetId): Compose a new tweet. getNewsFeed(userId): Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. follow(followerId, followeeId): Follower follows a followee. unfollow(followerId, followeeId): Follower unfollows a followee. Example: Twitter twitter = new Twitter(); // User 1 posts a new tweet (id = 5). twitter.postTweet(1, 5); // User 1's news feed should return a list with 1 tweet id -> [5]. twitter.getNewsFeed(1); // User 1 follows user 2. twitter.follow(1, 2); // User 2 posts a new tweet (id = 6). twitter.postTweet(2, 6); // User 1's news feed should return a list with 2 tweet ids -> [6, 5]. // Tweet id 6 should precede tweet id 5 because it is posted after tweet id 5. twitter.getNewsFeed(1); // User 1 unfollows user 2. twitter.unfollow(1, 2); // User 1's news feed should return a list with 1 tweet id -> [5], // since user 1 is no longer following user 2. twitter.getNewsFeed(1);
- HashMap
Succeed on first submission.
class Twitter { HashMap<Integer, HashSet<Integer>> fmap; HashMap<Integer, ArrayList<int[]>> tmap; int index = 0; /** Initialize your data structure here. */ public Twitter() { fmap = new HashMap<>(); tmap = new HashMap<>(); } /** Compose a new tweet. */ public void postTweet(int userId, int tweetId) { ArrayList<int[]> ts = tmap.getOrDefault(userId, new ArrayList<>()); ts.add(new int[] {tweetId, index++}); tmap.put(userId, ts); } /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */ public List<Integer> getNewsFeed(int userId) { ArrayList<Integer> result = new ArrayList<>(); HashSet<Integer> set = fmap.getOrDefault(userId, new HashSet<>()); HashSet<Integer> users = new HashSet<>(set); users.add(userId); HashMap<Integer, Integer> map = new HashMap<>(); for(int u : users) map.put(u, tmap.containsKey(u) ? tmap.get(u).size()-1 : -1); for(int i = 0; i < 10; i++) { int max = -1; int choose = -1; int tId = -1; for(int u : users) { if(!tmap.containsKey(u)) continue; ArrayList<int[]> ts = tmap.get(u); int p = map.get(u); if(p == -1) continue; int[] t = ts.get(p); if(t[1] > max) { max = t[1]; choose = u; tId = t[0]; } } if(max == -1) break; map.put(choose, map.get(choose)-1); result.add(tId); } return result; } /** Follower follows a followee. If the operation is invalid, it should be a no-op. */ public void follow(int followerId, int followeeId) { if(followerId == followeeId) return; HashSet<Integer> set = fmap.getOrDefault(followerId, new HashSet<>()); set.add(followeeId); fmap.put(followerId, set); } /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ public void unfollow(int followerId, int followeeId) { if(!fmap.containsKey(followerId) || !fmap.get(followerId).contains(followeeId)) return; HashSet<Integer> set = fmap.getOrDefault(followerId, new HashSet<>()); set.remove(followeeId); fmap.put(followerId, set); } } /** * Your Twitter object will be instantiated and called as such: * Twitter obj = new Twitter(); * obj.postTweet(userId,tweetId); * List<Integer> param_2 = obj.getNewsFeed(userId); * obj.follow(followerId,followeeId); * obj.unfollow(followerId,followeeId); */
-
599. Minimum Index Sum of Two Lists
599. Minimum Index Sum of Two Lists
Suppose Andy and Doris want to choose a restaurant for dinner, and they both have a list of favorite j restaurants represented by strings. You need to help them find out their common interest with the least list index sum. If there is a choice tie between answers, output all of them with no order requirement. You could assume there always exists an answer. Example 1: Input: ["Shogun", "Tapioca Express", "Burger King", "KFC"] ["Piatti", "The Grill at Torrey Pines", "Hungry Hunter Steakhouse", "Shogun"] Output: ["Shogun"] Explanation: The only restaurant they both like is "Shogun". Example 2: Input: ["Shogun", "Tapioca Express", "Burger King", "KFC"] ["KFC", "Shogun", "Burger King"] Output: ["Shogun"] Explanation: The restaurant they both like and have the least index sum is "Shogun" with index sum 1 (0+1). Note: The length of both lists will be in the range of [1, 1000]. The length of strings in both lists will be in the range of [1, 30]. The index is starting from 0 to the list length minus 1. No duplicates in both lists.
- HashMap
public String[] findRestaurant(String[] list1, String[] list2) { String[] l = list1.length > list2.length ? list1 : list2; String[] s = l == list1 ? list2 : list1; HashMap<String, Integer> map = new HashMap<>(); for(int i = 0; i < l.length; i++) { map.put(l[i], i); } ArrayList<String> temp = new ArrayList<>(); int sum = Integer.MAX_VALUE; for(int i = 0; i < s.length; i++) { if(map.containsKey(s[i])) { if(i + map.get(s[i]) == sum) { temp.add(s[i]); } else if(i + map.get(s[i]) < sum) { temp.clear(); temp.add(s[i]); sum = i + map.get(s[i]); } } } String[] result = new String[temp.size()]; for(int i = 0; i < result.length; i++) result[i] = temp.get(i); return result; }
-
1096. Brace Expansion II
Under a grammar given below, strings can represent a set of lowercase words. Let's use R(expr) to denote the set of words the expression represents. Grammar can best be understood through simple examples: Single letters represent a singleton set containing that word. R("a") = {"a"} R("w") = {"w"} When we take a comma delimited list of 2 or more expressions, we take the union of possibilities. R("{a,b,c}") = {"a","b","c"} R("{ {a,b},{b,c} }") = {"a","b","c"} (notice the final set only contains each word at most once) When we concatenate two expressions, we take the set of possible concatenations between two words where the first word comes from the first expression and the second word comes from the second expression. R("{a,b}{c,d}") = {"ac","ad","bc","bd"} R("a{b,c}{d,e}f{g,h}") = {"abdfg", "abdfh", "abefg", "abefh", "acdfg", "acdfh", "acefg", "acefh"} Formally, the 3 rules for our grammar: For every lowercase letter x, we have R(x) = {x} For expressions e_1, e_2, ... , e_k with k >= 2, we have R({e_1,e_2,...}) = R(e_1) ∪ R(e_2) ∪ ... For expressions e_1 and e_2, we have R(e_1 + e_2) = {a + b for (a, b) in R(e_1) × R(e_2)}, where + denotes concatenation, and × denotes the cartesian product. Given an expression representing a set of words under the given grammar, return the sorted list of words that the expression represents. Example 1: Input: "{a,b}{c,{d,e} }" Output: ["ac","ad","ae","bc","bd","be"] Example 2: Input: "{ {a,z},a{b,c},{ab,z} }" Output: ["a","ab","ac","z"] Explanation: Each distinct word is written only once in the final answer. Constraints: 1 <= expression.length <= 50 expression[i] consists of '{', '}', ','or lowercase English letters. The given expression represents a set of words based on the grammar given in the description.
- Recursion
Spend too much time on this problem. Good thing is passing on first try.
public List<String> braceExpansionII(String expression) { HashSet<String> set = divide(expression); ArrayList<String> result = new ArrayList<>(set); Collections.sort(result); return result; } public HashSet<String> divide(String expression) { // add or multiply ??? check the number of curly brackets HashSet<String> result = new HashSet<>(); if(!expression.contains("{")) { result.add(expression); return result; } ArrayList<HashSet<String>> temp = new ArrayList<>(); if(isAdd(expression)) { int pre = 1, i = 1, lc = 0, rc = 0; while(i < expression.length()-1) { char c = expression.charAt(i); if(c == '{') lc++; else if(c == '}') rc++; else if(c == ',' && lc == rc) { String sub = expression.substring(pre, i); HashSet<String> set = divide(sub); temp.add(set); pre = i+1; } i++; } String sub = expression.substring(pre, expression.length()-1); HashSet<String> set = divide(sub); temp.add(set); add(temp, result); } else { int pre = 0, i = 0, lc = 0, rc = 0; while(i < expression.length()) { char c = expression.charAt(i); if(c == '{') { if(rc == lc) { String sub = expression.substring(pre, i); HashSet<String> set = divide(sub); temp.add(set); pre = i; } lc++; } else if(c == '}') { rc++; } else if(lc > 0 && rc == lc && expression.charAt(i-1) == '}') { String sub = expression.substring(pre, i); HashSet<String> set = divide(sub); temp.add(set); pre = i; } i++; } String sub = expression.substring(pre, expression.length()); HashSet<String> set = divide(sub); temp.add(set); multiply(temp, result, "", 0); } return result; } public boolean isAdd(String s) { int lc = 0, rc = 0; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == '{') lc++; else if(c == '}') rc++; else if(rc == lc) return false; // a single character outside brackets aaaa{}aaa{} if(lc > 0 && rc == lc && i < s.length()-1) // {}{}{} return false; } return true; // { {},sdf{},sfd{}{} } } public void add(ArrayList<HashSet<String>> temp, HashSet<String> result) { HashSet<String> buffer = new HashSet<>(); for(HashSet<String> set : temp) buffer.addAll(set); result.addAll(buffer); } public void multiply(ArrayList<HashSet<String>> temp, HashSet<String> result, String buffer, int curr) { if(curr == temp.size()) result.add(buffer); else { for(String s : temp.get(curr)) { multiply(temp, result, buffer+s, curr+1); } } }
-
40. Combination Sum II
Given a collection of candidate numbers (candidates) and a target number (target), find all unique combinations in candidates where the candidate numbers sums to target. Each number in candidates may only be used once in the combination. Note: All numbers (including target) will be positive integers. The solution set must not contain duplicate combinations. Example 1: Input: candidates = [10,1,2,7,6,1,5], target = 8, A solution set is: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ] Example 2: Input: candidates = [2,5,2,1,2], target = 5, A solution set is: [ [1,2,2], [5] ]
- HashMap
public List<List<Integer>> combinationSum2(int[] candidates, int target) { Arrays.sort(candidates); List<List<Integer>> result = new ArrayList<>(); backtrack(result, candidates, new ArrayList<Integer>(), target, 0, 0); return result; } public void backtrack(List<List<Integer>> result, int[] candidates, ArrayList<Integer> temp, int target, int sum, int start) { if(sum >= target) { if(sum == target) result.add(new ArrayList<>(temp)); return; } for(int i = start; i < candidates.length; i++) { if(i > start && candidates[i] == candidates[i-1]) continue; temp.add(candidates[i]); backtrack(result, candidates, temp, target, sum+candidates[i], i+1); temp.remove(temp.size()-1); } }
-
205. Isomorphic Strings
Given two strings s and t, determine if they are isomorphic. Two strings are isomorphic if the characters in s can be replaced to get t. All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself. Example 1: Input: s = "egg", t = "add" Output: true Example 2: Input: s = "foo", t = "bar" Output: false Example 3: Input: s = "paper", t = "title" Output: true Note: You may assume both s and t have the same length.
- HashMap
public boolean isIsomorphic(String s, String t) { HashSet<Character> used = new HashSet<>(); HashMap<Character, Character> map = new HashMap<>(); for(int i = 0; i < s.length(); i++) { char a = s.charAt(i), b = t.charAt(i); if(!map.containsKey(a)) { if(used.contains(b)) return false; used.add(b); map.put(a, b); } else if(map.get(a) != b) return false; } return true; }
-
743. Network Delay Time
There are N network nodes, labelled 1 to N. Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, and w is the time it takes for a signal to travel from source to target. Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1. Example 1: Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2 Output: 2
Note: N will be in the range [1, 100]. K will be in the range [1, N]. The length of times will be in the range [1, 6000]. All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 0 <= w <= 100.
- MST
public int networkDelayTime(int[][] times, int N, int K) { int result = 0; ArrayList<int[]>[] ns = new ArrayList[N+1]; for(int[] t : times) { if(ns[t[0]] == null) ns[t[0]] = new ArrayList<>(); ns[t[0]].add(new int[] {t[1], t[2], 0}); } PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(a[2]-b[2])); boolean[] visited = new boolean[N+1]; q.offer(new int[] {K, 0, 0}); int vCnt = 0; while(!q.isEmpty()) { int[] c = q.poll(); if(visited[c[0]]) continue; visited[c[0]] = true; result = c[2]; vCnt++; if(vCnt == N) break; if(ns[c[0]] == null) continue; for(int[] next : ns[c[0]]) { next[2] = c[2] + next[1]; // accumulative time q.offer(next); } } return vCnt == N ? result : -1; }
-
947. Most Stones Removed with Same Row or Column
947. Most Stones Removed with Same Row or Column
On a 2D plane, we place stones at some integer coordinate points. Each coordinate point may have at most one stone. Now, a move consists of removing a stone that shares a column or row with another stone on the grid. What is the largest possible number of moves we can make? Example 1: Input: stones = [[0,0],[0,1],[1,0],[1,2],[2,1],[2,2]] Output: 5 Example 2: Input: stones = [[0,0],[0,2],[1,1],[2,0],[2,2]] Output: 3 Example 3: Input: stones = [[0,0]] Output: 0
- UnionFind
public int removeStones(int[][] stones) { UnionFind uf = new UnionFind(stones); for(int[] s : stones) { uf.union(s[0], s[1]+10000); } int cnt = 0; for(int i = 0; i < 20000; i++) { if(uf.arr[i] == i) cnt++; } return stones.length - cnt; } class UnionFind { int[] arr; public UnionFind(int[][] stones) { arr = new int[20000]; Arrays.fill(arr, -1); for(int[] s : stones) { int x = s[0], y = s[1]; arr[x] = x; arr[y+10000] = y+10000; } } public int find(int x) { if(arr[x] != x) { arr[x] = find(arr[x]); } return arr[x]; } public void union(int x, int y) { int a = find(x), b = find(y); if(a != b) { arr[a] = b; } } }
- DFS
public int removeStones(int[][] stones) { ArrayList<int[]>[] xs = new ArrayList[10000], ys = new ArrayList[10000]; for(int[] s : stones) { int x = s[0], y = s[1]; if(xs[x] == null) xs[x] = new ArrayList<>(); if(ys[y] == null) ys[y] = new ArrayList<>(); xs[x].add(s); ys[y].add(s); } HashSet<int[]> visited = new HashSet<int[]>(); int cnt = 0; for(int[] s : stones) { if(!visited.contains(s)) { cnt++; visited.add(s); mark(s, visited, xs, ys); } } return stones.length - cnt; } public void mark(int[] curr, HashSet<int[]> visited, ArrayList<int[]>[] xs, ArrayList<int[]>[] ys) { int x = curr[0], y = curr[1]; for(int[] neighbor : xs[x]) { if(!visited.contains(neighbor)) { visited.add(neighbor); mark(neighbor, visited, xs, ys); } } for(int[] neighbor : ys[y]) { if(!visited.contains(neighbor)) { visited.add(neighbor); mark(neighbor, visited, xs, ys); } } }
-
222. Count Complete Tree Nodes
222. Count Complete Tree Nodes
Given a complete binary tree, count the number of nodes. Note: Definition of a complete binary tree from Wikipedia: In a complete binary tree every level, except possibly the last, is completely filled, and all nodes in the last level are as far left as possible. It can have between 1 and 2h nodes inclusive at the last level h. Example: Input: 1 / \ 2 3 / \ / 4 5 6 Output: 6
- Divide and conquer
public int countNodes(TreeNode root) { if(root == null) return 0; int l = getH(root.left), r = getH(root.right); if(l > r) { // r is complete return 1 + height2count(r) + countNodes(root.left); } else { // l is complete return 1 + height2count(l) + countNodes(root.right); } } public int getH(TreeNode root) { if(root == null) return -1; return 1 + getH(root.left); } public int height2count(int h) { return (int)Math.pow(2, h+1) - 1; }
-
987. Vertical Order Traversal of a Binary Tree
987. Vertical Order Traversal of a Binary Tree
Given a binary tree, return the vertical order traversal of its nodes values. For each node at position (X, Y), its left and right children respectively will be at positions (X-1, Y-1) and (X+1, Y-1). Running a vertical line from X = -infinity to X = +infinity, whenever the vertical line touches some nodes, we report the values of the nodes in order from top to bottom (decreasing Y coordinates). If two nodes have the same position, then the value of the node that is reported first is the value that is smaller. Return an list of non-empty reports in order of X coordinate. Every report will have a list of values of nodes. Example 1: Input: [3,9,20,null,null,15,7] Output: [[9],[3,15],[20],[7]] Explanation: Without loss of generality, we can assume the root node is at position (0, 0): Then, the node with value 9 occurs at position (-1, -1); The nodes with values 3 and 15 occur at positions (0, 0) and (0, -2); The node with value 20 occurs at position (1, -1); The node with value 7 occurs at position (2, -2).
- Sorting
public List<List<Integer>> verticalTraversal(TreeNode root) { PriorityQueue<Node> q = new PriorityQueue<>((a,b)->{ int c = Integer.compare(a.c, b.c); if(c != 0) return c; int r = Integer.compare(a.r, b.r); if(r != 0) return r; return Integer.compare(a.v, b.v); }); traverse(root, q, 0, 0); List<List<Integer>> result = new ArrayList<>(); Node pre = null; ArrayList<Integer> line = null; while(!q.isEmpty()) { if(pre == null || pre.c != q.peek().c) { if(pre != null) result.add(line); line = new ArrayList<>(); } pre = q.poll(); line.add(pre.v); if(q.isEmpty()) { result.add(line); } } return result; } public void traverse(TreeNode root, PriorityQueue<Node> q, int r, int c) { if(root == null) return; q.offer(new Node(root.val, r, c)); traverse(root.left, q, r+1, c-1); traverse(root.right, q, r+1, c+1); } class Node { int v; int r; int c; public Node(int v, int r, int c) { this.v = v; this.r = r; this.c = c; } }
-
394. Decode String
Given an encoded string, return its decoded string. The encoding rule is: k[encoded_string], where the encoded_string inside the square brackets is being repeated exactly k times. Note that k is guaranteed to be a positive integer. You may assume that the input string is always valid; No extra white spaces, square brackets are well- formed, etc. Furthermore, you may assume that the original data does not contain any digits and that digits are only for those repeat numbers, k. For example, there won't be input like 3a or 2[4]. Examples: s = "3[a]2[bc]", return "aaabcbc". s = "3[a2[c]]", return "accaccacc". s = "2[abc]3[cd]ef", return "abcabccdcdcdef".
- Recursion
Another approach is to use stack.
public String decodeString(String s) { if(!s.contains("[")) return s; int repeat = 0; String result = "", temp = ""; for(int i = 0; i < s.length(); i++) { if(Character.isDigit(s.charAt(i))) { repeat = repeat * 10 + (s.charAt(i)-'0'); } else if(s.charAt(i) == '[') { int r = i+1, lc = 1; while(lc != 0) { if(s.charAt(r) == '[') lc++; else if(s.charAt(r) == ']') lc--; r++; } temp = decodeString(s.substring(i+1, r-1)); while(repeat > 0) { result += temp; repeat--; } temp = ""; i = r-1; } else { result += s.charAt(i); } } return result; }
- Stack
public String decodeString(String s) { Stack<String> stack = new Stack<>(); String res = ""; for(int i = 0; i < s.length(); i++) { if(Character.isDigit(s.charAt(i))) { // push the previous string and num for the next string into the stack String num = ""; while(Character.isDigit(s.charAt(i))) { num += s.charAt(i++); } stack.push(res); stack.push(num); res = ""; } else if(s.charAt(i) == '[') { // do nothing } else if(s.charAt(i) == ']') { int mul = Integer.valueOf(stack.pop()); // num for the current string String pre = stack.pop(); // previous string StringBuilder sb = new StringBuilder(pre); // add privous string while(mul-->0) sb.append(res); // multiply current string res = sb.toString(); } else { res += s.charAt(i); } } return res; }
-
241. Different Ways to Add Parentheses
241. Different Ways to Add Parentheses
Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *. Example 1: Input: "2-1-1" Output: [0, 2] Explanation: ((2-1)-1) = 0 (2-(1-1)) = 2 Example 2: Input: "2*3-4*5" Output: [-34, -14, -10, -10, 10] Explanation: (2*(3-(4*5))) = -34 ((2*3)-(4*5)) = -14 ((2*(3-4))*5) = -10 (2*((3-4)*5)) = -10 (((2*3)-4)*5) = 10
- Bottom Up
public List<Integer> diffWaysToComputeBottomUp(String input) { ArrayList<Integer> result = new ArrayList<>(); ArrayList<Integer> nums = new ArrayList<>(); ArrayList<Character> ops = new ArrayList<>(); int num = 0; for(char c : input.toCharArray()) { if(Character.isDigit(c)) { num = num * 10 + (c-'0'); } else { nums.add(num); ops.add(c); num = 0; } } nums.add(num); ArrayList<Integer>[][] dp = new ArrayList[nums.size()][nums.size()]; for(int i = 0; i < dp.length; i++) { dp[i][i] = new ArrayList<>(); dp[i][i].add(nums.get(i)); } for(int w = 2; w <= dp.length; w++) { for(int i = 0; i + w - 1 < dp.length; i++) { int r = i + w - 1; dp[i][r] = new ArrayList<>(); for(int j = i; j < i + w - 1; j++) { ArrayList<Integer> left = dp[i][j], right = dp[j+1][r]; char o = ops.get(j); for(int ln : left) { for(int rn : right) { if(o == '+') dp[i][r].add(ln + rn); else if(o == '-') dp[i][r].add(ln - rn); else dp[i][r].add(ln * rn); } } } } } return dp[0][dp.length-1]; }
- Recursion
public List<Integer> diffWaysToCompute(String input) { List<Integer> result = new ArrayList<>(); boolean isNumber = true; for(int i = 0; i < input.length(); i++) { if(!Character.isDigit(input.charAt(i))) { isNumber = false; // recursion List<Integer> left = diffWaysToCompute(input.substring(0, i)); List<Integer> right = diffWaysToCompute(input.substring(i+1)); for(int l : left) { for(int r : right) { if(input.charAt(i) == '+') result.add(l + r); else if(input.charAt(i) == '-') result.add(l - r); else result.add(l * r); } } } } if(isNumber) result.add(Integer.valueOf(input)); return result; }
- Bottom Up
-
315. Count of Smaller Numbers After Self
315. Count of Smaller Numbers After Self
- MergeSort
public List<Integer> countSmaller(int[] nums) { ArrayList<Integer> result = new ArrayList<>(); int[][] arr = new int[nums.length][2]; for(int i = 0; i < nums.length; i++) { arr[i][0] = nums[i]; arr[i][1] = i; } int[] buffer = new int[nums.length]; split(arr, buffer, 0, nums.length-1); for(int i = 0; i < buffer.length; i++) result.add(buffer[i]); return result; } public void split(int[][] nums, int[] result, int start, int end) { if(start >= end) return; int mid = start + (end-start) / 2; split(nums, result, start, mid); split(nums, result, mid+1, end); int l = start, r = mid+1, p = 0; int[][] temp = new int[end-start+1][]; while(l <= mid && r <= end) { if(nums[l][0] <= nums[r][0]) { // pop left when equals result[nums[l][1]] += r-mid-1; temp[p++] = nums[l++]; } else { temp[p++] = nums[r++]; } } while(l <= mid) { result[nums[l][1]] += r-mid-1; temp[p++] = nums[l++]; } while(r <= end) temp[p++] = nums[r++]; System.arraycopy(temp, 0, nums, start, temp.length); }
-
Eaton Capstone Project Summary
We worked on Eaton’s requirement of analysing customer behavior by leveraging the web-click activity on Eaton’s website. Another requirement was to assess the customers’ reputation through sentiment analysis of news articles. A secondary requirement was to develop an anonymizer tool that can encrypt and decrypt user-selected columns in a file, to allow sharing of confidential data with external vendors.
-
300. Longest Increasing Subsequence
300. Longest Increasing Subsequence
Given an unsorted array of integers, find the length of longest increasing subsequence. Example: Input: [10,9,2,5,3,7,101,18] Output: 4 Explanation: The longest increasing subsequence is [2,3,7,101], therefore the length is 4. Note: There may be more than one LIS combination, it is only necessary for you to return the length. Your algorithm should run in O(n2) complexity. Follow up: Could you improve it to O(n log n) time complexity?
- Binary Search DP
public int lengthOfLIS(int[] nums) { if(nums.length == 0) return 0; int[] dp = new int[nums.length]; dp[0] = nums[0]; int len = 1; for(int i = 1; i < nums.length; i++) { // first greater than nums[i] int l = 0, r = len; while(l < r) { int mid = l + (r-l) / 2; if(dp[mid] >= nums[i]) r = mid; else l = mid + 1; } if(l == len) { dp[len++] = nums[i]; } else { dp[l] = nums[i]; } } return len; }
-
163. Missing Ranges
- A better solution, shrink lower boundary
public List<String> findMissingRanges(int[] nums, int lower, int upper) { List<String> list = new ArrayList<String>(); long l = lower; for(int n : nums){ long justBelow = (long)n - 1; if(l == justBelow) list.add(l+""); else if(l < justBelow) list.add(l + "->" + justBelow); l = (long)n+1; } if(l == upper) list.add(l+""); else if(l < upper) list.add(l + "->" + upper); return list; }
- My Trival solution
public List<String> findMissingRanges(int[] nums, int lower, int upper) { List<String> result = new ArrayList<>(); int l = 0, r = nums.length; int start = 0, end = nums.length-1; while(start < nums.length && nums[start] < lower) start++; while(end >= 0 && nums[end] > upper) end--; if(start > end) { if(upper == lower) result.add("" + upper); else result.add(lower+"->"+upper); return result; } if((long)nums[start]-lower == 1) result.add(lower+""); else if((long)nums[start]-lower > 1) result.add(lower + "->" + ((long)nums[start]-1)); for(int i = start; i < end; i++) { if((long)nums[i]+2 == nums[i+1]) { result.add(nums[i]+1+""); } else if((long)nums[i+1]-(long)nums[i] > 2) { result.add((long)nums[i]+1 + "->" + ((long)nums[i+1]-1)); } } if(nums[end] == (long)upper-1) result.add(upper+""); else if(nums[end] < upper-1) result.add((long)nums[end]+1 + "->" + (upper)); return result; }
- A better solution, shrink lower boundary
-
1011. Capacity To Ship Packages Within D Days
1011. Capacity To Ship Packages Within D Days
A conveyor belt has packages that must be shipped from one port to another within D days. The i-th package on the conveyor belt has a weight of weights[i]. Each day, we load the ship with packages on the conveyor belt (in the order given by weights). We may not load more weight than the maximum weight capacity of the ship. Return the least weight capacity of the ship that will result in all the packages on the conveyor belt being shipped within D days. Example 1: Input: weights = [1,2,3,4,5,6,7,8,9,10], D = 5 Output: 15 Explanation: A ship capacity of 15 is the minimum to ship all the packages in 5 days like this: 1st day: 1, 2, 3, 4, 5 2nd day: 6, 7 3rd day: 8 4th day: 9 5th day: 10 Note that the cargo must be shipped in the order given, so using a ship of capacity 14 and splitting the packages into parts like (2, 3, 4, 5), (1, 6, 7), (8), (9), (10) is not allowed. Example 2: Input: weights = [3,2,2,4,1,4], D = 3 Output: 6 Explanation: A ship capacity of 6 is the minimum to ship all the packages in 3 days like this: 1st day: 3, 2 2nd day: 2, 4 3rd day: 1, 4 Example 3: Input: weights = [1,2,3,1,1], D = 4 Output: 3 Explanation: 1st day: 1 2nd day: 2 3rd day: 3 4th day: 1, 1 Note: 1 <= D <= weights.length <= 50000 1 <= weights[i] <= 500
- Totally the same with split array largest sum, why one is a medium and another is hard?
public int shipWithinDaysDP(int[] weights, int D) { if(weights.length == 0 || D == 0) return 0; int[] pre = new int[weights.length]; pre[0] = weights[0]; for(int i = 1; i < weights.length; i++) pre[i] = pre[i-1] + weights[i]; int[][] dp = new int[D][weights.length]; for(int i = 1; i < dp.length; i++) Arrays.fill(dp[i], Integer.MAX_VALUE); System.arraycopy(pre, 0, dp[0], 0, pre.length); for(int i = 1; i < dp.length; i++) { for(int j = i; j < dp[0].length; j++) { for(int k = i-1; k < j; k++) { dp[i][j] = Math.min(dp[i][j], Math.max(dp[i-1][k], pre[j]-pre[k])); } } } return dp[dp.length-1][dp[0].length-1]; }
- Binary Search
public int shipWithinDays(int[] weights, int D) { int l = 0, r = 0; for(int n : weights) { l = Math.max(l, n); r += n; } while(l < r) { int mid = l + (r-l) / 2; if(canSplit(weights, D, mid)) { r = mid; } else l = mid + 1; } return r; } public boolean canSplit(int[] arr, int d, int target) { int sum = 0; int cnt = 0; for(int n : arr) { if(n > target) return false; if(n + sum > target) { cnt++; sum = n; } else { sum += n; } } return cnt+1 <= d; }
-
334. Increasing Triplet Subsequence
334. Increasing Triplet Subsequence
Given an unsorted array return whether an increasing subsequence of length 3 exists or not in the array. Formally the function should: Return true if there exists i, j, k such that arr[i] < arr[j] < arr[k] given 0 ≤ i < j < k ≤ n-1 else return false. Note: Your algorithm should run in O(n) time complexity and O(1) space complexity. Example 1: Input: [1,2,3,4,5] Output: true Example 2: Input: [5,4,3,2,1] Output: false
- Longest Increasing subsequent
public boolean increasingTriplet(int[] nums) { if(nums.length == 0) return false; int[] dp = new int[3]; int len = 1; dp[0] = nums[0]; for(int i = 1; i < nums.length; i++) { int j = 0; for(; j < len; j++) { if(nums[i] <= dp[j]) { dp[j] = nums[i]; break; } } if(j == len) { dp[len++] = nums[i]; if(len == 3) return true; } } return false; }
-
359. Logger Rate Limiter
Design a logger system that receive stream of messages along with its timestamps, each message should be printed if and only if it is not printed in the last 10 seconds. Given a message and a timestamp (in seconds granularity), return true if the message should be printed in the given timestamp, otherwise returns false. It is possible that several messages arrive roughly at the same time. Example: Logger logger = new Logger(); // logging string "foo" at timestamp 1 logger.shouldPrintMessage(1, "foo"); returns true; // logging string "bar" at timestamp 2 logger.shouldPrintMessage(2,"bar"); returns true; // logging string "foo" at timestamp 3 logger.shouldPrintMessage(3,"foo"); returns false; // logging string "bar" at timestamp 8 logger.shouldPrintMessage(8,"bar"); returns false; // logging string "foo" at timestamp 10 logger.shouldPrintMessage(10,"foo"); returns false; // logging string "foo" at timestamp 11 logger.shouldPrintMessage(11,"foo"); returns true;
- If the timestamp is monotonic increased, a linkedhashmap with least recent used element to be removed can be used to remove unused map entry.
class Logger { HashMap<String, Integer> map; /** Initialize your data structure here. */ public Logger() { map = new HashMap<>(); } /** Returns true if the message should be printed in the given timestamp, otherwise returns false. If this method returns false, the message will not be printed. The timestamp is in seconds granularity. */ public boolean shouldPrintMessage(int timestamp, String message) { if(map.containsKey(message) && map.get(message)+10 > timestamp) return false; map.put(message, timestamp); return true; } }
-
975. Odd Even Jump
You are given an integer array A. From some starting index, you can make a series of jumps. The (1st, 3rd, 5th, ...) jumps in the series are called odd numbered jumps, and the (2nd, 4th, 6th, ...) jumps in the series are called even numbered jumps. You may from index i jump forward to index j (with i < j) in the following way: During odd numbered jumps (ie. jumps 1, 3, 5, ...), you jump to the index j such that A[i] <= A[j] and A[j] is the smallest possible value. If there are multiple such indexes j, you can only jump to the smallest such index j. During even numbered jumps (ie. jumps 2, 4, 6, ...), you jump to the index j such that A[i] >= A[j] and A[j] is the largest possible value. If there are multiple such indexes j, you can only jump to the smallest such index j. (It may be the case that for some index i, there are no legal jumps.) A starting index is good if, starting from that index, you can reach the end of the array (index A.length - 1) by jumping some number of times (possibly 0 or more than once.) Return the number of good starting indexes. Example 1: Input: [10,13,12,14,15] Output: 2 Explanation: From starting index i = 0, we can jump to i = 2 (since A[2] is the smallest among A[1], A[2], A[3], A[4] that is greater or equal to A[0]), then we can't jump any more. From starting index i = 1 and i = 2, we can jump to i = 3, then we can't jump any more. From starting index i = 3, we can jump to i = 4, so we've reached the end. From starting index i = 4, we've reached the end already. In total, there are 2 different starting indexes (i = 3, i = 4) where we can reach the end with some number of jumps. Example 2: Input: [2,3,1,1,4] Output: 3 Explanation: From starting index i = 0, we make jumps to i = 1, i = 2, i = 3: During our 1st jump (odd numbered), we first jump to i = 1 because A[1] is the smallest value in (A[1], A[2], A[3], A[4]) that is greater than or equal to A[0]. During our 2nd jump (even numbered), we jump from i = 1 to i = 2 because A[2] is the largest value in (A[2], A[3], A[4]) that is less than or equal to A[1]. A[3] is also the largest value, but 2 is a smaller index, so we can only jump to i = 2 and not i = 3. During our 3rd jump (odd numbered), we jump from i = 2 to i = 3 because A[3] is the smallest value in (A[3], A[4]) that is greater than or equal to A[2]. We can't jump from i = 3 to i = 4, so the starting index i = 0 is not good. In a similar manner, we can deduce that: From starting index i = 1, we jump to i = 4, so we reach the end. From starting index i = 2, we jump to i = 3, and then we can't jump anymore. From starting index i = 3, we jump to i = 4, so we reach the end. From starting index i = 4, we are already at the end. In total, there are 3 different starting indexes (i = 1, i = 3, i = 4) where we can reach the end with some number of jumps. Example 3: Input: [5,1,3,4,2] Output: 3 Explanation: We can reach the end from starting indexes 1, 2, and 4. Note: 1 <= A.length <= 20000 0 <= A[i] < 100000
The key is to build the two arrays whose value is the next jumpable index of the input array
To build them, use either TreeMap (build as grow) and Monotonic Stacks (Sort by value)
- TreeMap
public int oddEvenJumps(int[] arr) { TreeMap<Integer, Integer> map = new TreeMap<>(); int[] odd = new int[arr.length], even = new int[arr.length]; for(int i = arr.length-1; i >= 0; i--) { Integer up = map.ceilingKey(arr[i]), down = map.floorKey(arr[i]); even[i] = down == null ? -1 : map.get(down); odd[i] = up == null ? -1 : map.get(up); map.put(arr[i], i); } boolean[][] dp = new boolean[2][arr.length]; dp[0][arr.length-1] = true; dp[1][arr.length-1] = true; int cnt = 1; for(int i = arr.length-2; i >= 0; i--) { dp[0][i] = odd[i] == -1 ? false : dp[1][odd[i]]; dp[1][i] = even[i] == -1 ? false : dp[0][even[i]]; if(dp[0][i]) cnt++; } return cnt; }
- Monotonic Stack
public int oddEvenJumps(int[] arr) { int[] odd = build(arr, true); int[] even = build(arr, false); boolean[][] dp = new boolean[2][arr.length]; dp[0][arr.length-1] = true; // odd dp[1][arr.length-1] = true; // even int result = 1; for(int i = arr.length-2; i >= 0; i--) { dp[0][i] = (odd[i] == -1) ? false : dp[1][odd[i]]; dp[1][i] = (even[i] == -1) ? false : dp[0][even[i]]; result += (dp[0][i] ? 1 : 0); } return result; } public int[] build(int[] arr, boolean odd) { Integer[] index = new Integer[arr.length]; for(int i = 0; i < arr.length; i++) index[i] = i; Arrays.sort(index, (i,j)->(arr[i] == arr[j] ? i-j : (odd ? arr[i]-arr[j] : arr[j]-arr[i]))); int[] result = new int[arr.length]; Arrays.fill(result, -1); Stack<Integer> stack = new Stack<>(); for(int i = 0; i < index.length; i++) { while(!stack.isEmpty() && index[i] > stack.peek()) { result[stack.pop()] = index[i]; } stack.push(index[i]); } return result; }
-
410. Split Array Largest Sum
Given an array which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays. Note: If n is the length of array, assume the following constraints are satisfied: 1 ≤ n ≤ 1000 1 ≤ m ≤ min(50, n) Examples: Input: nums = [7,2,5,10,8] m = 2 Output: 18 Explanation: There are four ways to split nums into two subarrays. The best way is to split it into [7,2,5] and [10,8], where the largest sum among the two subarrays is only 18.
- Binary Search canSplit(array, m, x): true if the array can be splited into m parts with the largest subarray sum is less than or equal to x if(canSplit(x)) is true, then canSplit(y) that y > x is true, so the answer space is like
…F F F F F T(the answer) T T T T…
This is a typical binary search problem.
public int splitArray(int[] nums, int m) { long l = Integer.MAX_VALUE, r = 0; for(int n : nums) { r += n; l = Math.min(l, n); } while(l < r) { long mid = l + (r-l) / 2; if(canSplit(nums, m, mid)) { r = mid; } else { l = mid + 1; } } return (int)r; }
- DynamicProgramming dp[i][j]: the min max subarray sum form 0 to j, divided into i parts
public int splitArray(int[] nums, int m) { int[] prefix = new int[nums.length]; prefix[0] = nums[0]; for(int i = 1; i < prefix.length; i++) prefix[i] = prefix[i-1]+nums[i]; int[][] dp = new int[m+1][nums.length]; for(int i = 0; i < dp.length; i++) { Arrays.fill(dp[i], Integer.MAX_VALUE); } for(int j = 0; j < dp[0].length; j++) dp[1][j] = prefix[j]; for(int i = 2; i < dp.length; i++) { for(int j = i-1; j < nums.length; j++) { for(int k = 0; k < j; k++) { dp[i][j] = Math.min(dp[i][j], Math.max(dp[i-1][k], (prefix[j]-prefix[k]))); } } } return dp[m][nums.length-1]; }
-
683. K Empty Slots
You have N bulbs in a row numbered from 1 to N. Initially, all the bulbs are turned off. We turn on exactly one bulb everyday until all bulbs are on after N days. You are given an array bulbs of length N where bulbs[i] = x means that on the (i+1)th day, we will turn on the bulb at position x where i is 0-indexed and x is 1-indexed. Given an integer K, find out the minimum day number such that there exists two turned on bulbs that have exactly K bulbs between them that are all turned off. If there isn't such day, return -1. Example 1: Input: bulbs: [1,3,2] K: 1 Output: 2 Explanation: On the first day: bulbs[0] = 1, first bulb is turned on: [1,0,0] On the second day: bulbs[1] = 3, third bulb is turned on: [1,0,1] On the third day: bulbs[2] = 2, second bulb is turned on: [1,1,1] We return 2 because on the second day, there were two on bulbs with one off bulb between them.
- Sliding Window: jump on fail and success
public int kEmptySlots(int[] bulbs, int K) { if(bulbs.length-2 < K) return -1; int[] days = new int[bulbs.length+1]; for(int i = 0; i < bulbs.length; i++) days[bulbs[i]] = i+1; int l = 1, r = 2; int result = bulbs.length+1; while(r < days.length && l+K+1 < days.length) { if(r-l-1 == K) { result = Math.min(result, Math.max(days[l], days[r])); l = r; } else if(days[r] < days[l] || days[r] < days[l+K+1]) { l = r; } r++; } return result == bulbs.length+1 ? -1 : result; }
- Deque sliding window
public int kEmptySlots(int[] bulbs, int K) { if(bulbs.length-2 < K) return -1; Deque<Integer> q = new ArrayDeque<>(); int[] days = new int[bulbs.length+1]; for(int i = 0; i < bulbs.length; i++) days[bulbs[i]] = i+1; for(int i = 2; i < 2+K; i++) { while(!q.isEmpty() && days[i] < q.peekLast()) q.pollLast(); q.offerLast(days[i]); } int result = bulbs.length+1; for(int i = 2+K; i <= bulbs.length; i++) { int earliest = q.isEmpty() ? bulbs.length+1 : q.peekFirst(); if(earliest > days[i] && earliest > days[i-K-1]) result = Math.min(result, Math.max(days[i], days[i-K-1])); if(!q.isEmpty()) { if(q.peekFirst() == days[i-K]) q.pollFirst(); while(!q.isEmpty() && days[i] < q.peekLast()) q.pollLast(); q.offerLast(days[i]); } } return result == bulbs.length+1 ? -1 : result; }
- PriorityQueue
public int kEmptySlotsPriorityQueue(int[] bulbs, int K) { if(bulbs.length - 2 < K) return -1; PriorityQueue<Integer> q = new PriorityQueue<>(); int[] days = new int[bulbs.length+1]; for(int i = 0; i < bulbs.length; i++) days[bulbs[i]] = i+1; for(int i = 2; i-2 < K; i++) q.offer(days[i]); int result = bulbs.length+1; for(int i = K+2; i < days.length; i++) { int earliest = q.isEmpty() ? bulbs.length + 1 : q.peek(); // k == 0 ? int l = i-K-1; if(earliest > days[l] && earliest > days[i]) result = Math.min(result, Math.max(days[l], days[i])); if(!q.isEmpty()) { // K > 0 q.remove(days[l+1]); q.offer(days[i]); } } return result == bulbs.length+1 ? -1 : result; }
- Easy
public int kEmptySlots(int[] bulbs, int K) { TreeMap<Integer, Integer> map = new TreeMap<>(); for(int b : bulbs) { Integer low = map.floorKey(b); Integer high = map.ceilingKey(b); if(low == null) low = b; if(high == null) high = b; if(b - low - 1 == K || high - b - 1 == K) return map.size()+1; map.put(b,0); } return -1; }
-
388. Longest Absolute File Path
388. Longest Absolute File Path
Suppose we abstract our file system by a string in the following manner: The string "dir\n\tsubdir1\n\tsubdir2\n\t\tfile.ext" represents: dir subdir1 subdir2 file.ext The directory dir contains an empty sub-directory subdir1 and a sub-directory subdir2 containing a file file.ext. The string "dir\n\tsubdir1\n\t\tfile1.ext\n\t\tsubsubdir1\n\tsubdir2\n\t\tsubsubdir2\n\t\t\tfile2.ext" represents: dir subdir1 file1.ext subsubdir1 subdir2 subsubdir2 file2.ext The directory dir contains two sub-directories subdir1 and subdir2. subdir1 contains a file file1.ext and an empty second-level sub-directory subsubdir1. subdir2 contains a second-level sub-directory subsubdir2 containing a file file2.ext. We are interested in finding the longest (number of characters) absolute path to a file within our file system. For example, in the second example above, the longest absolute path is "dir/subdir2/subsubdir2/ file2.ext", and its length is 32 (not including the double quotes). Given a string representing the file system in the above format, return the length of the longest absolute path to file in the abstracted file system. If there is no file in the system, return 0. Note: The name of a file contains at least a . and an extension. The name of a directory or sub-directory will not contain a .. Time complexity required: O(n) where n is the size of the input string. Notice that a/aa/aaa/file1.txt is not the longest file path, if there is another path aaaaaaaaaaaaaaaaaaaaa/ sth.png.
A stranger case: “root1\n abc.txt” result == length of “ abc.txt”
- split string
public int lengthLongestPath(String input) { String[] arr = input.split("\n"); if(arr.length == 1 && arr[0].contains(".")) return arr[0].length(); int len = 1; int result = 0; Stack<String> stack = new Stack<>(); stack.push(""); for(int i = 0; i < arr.length; i++) { String s = arr[i]; int j = s.lastIndexOf("\t"); int tCnt = j+1; s = s.substring(j+1, s.length()); while(tCnt < stack.size()) { String prev = stack.pop(); len -= prev.length()+1; } len += 1+s.length(); stack.push(s); if(s.contains(".")) { result = Math.max(result, len-1); } } return result; }
- Stack stores the length of dirs. len is the curr length of absolute path
A slower but easy to code approach is to preprocess the data by spliting intput by \n
public int lengthLongestPath(String input) { Stack<Integer> stack = new Stack<>(); int i = 0; while(i < input.length() && input.charAt(i) != '\n') i++; if(i == input.length()) return input.contains(".") ? input.length() : 0; stack.push(i); int len = i; int result = 0; while(i < input.length()) { int level = 1; int j = i+1; while(input.charAt(j) == '\t') { j++; level++; } // now j is at the start point of the next directory int k = j; boolean isFile = false; while(k < input.length() && input.charAt(k) != '\n') { if(input.charAt(k) == '.') isFile = true; k++; } // k is at the next \n\t... while(level <= stack.size()) { int reduce = stack.pop(); len -= reduce+1; } len += k-j+1; stack.push(k-j); if(isFile) result = Math.max(result, len); i = k; } return result; } }
-
767. Reorganize String
Given a string S, check if the letters can be rearranged so that two characters that are adjacent to each other are not the same. If possible, output any possible result. If not possible, return the empty string. Example 1: Input: S = "aab" Output: "aba" Example 2: Input: S = "aaab" Output: "" Note: S will consist of lowercase letters and have length in range [1, 500].
- Similar to CPU cool down time
class Solution { public String reorganizeString(String S) { int[] count = new int[26]; for(char c : S.toCharArray()) count[c-'a']++; int[][] c2c = new int[26][]; for(int i = 0; i < 26; i++) c2c[i] = new int[] {i, count[i]}; char p = 'A'; String result = ""; while(true) { Arrays.sort(c2c, (a,b)->(a[1]-b[1])); int i = 25; while(i >= 0 && c2c[i][1] == 0) i--; if(i == -1) break; if('a' + c2c[i][0] == p) { i--; while(i >= 0 && c2c[i][1] == 0) i--; if(i == -1) return ""; } c2c[i][1]--; result += (char)('a' + c2c[i][0]); p = (char)('a' + c2c[i][0]); } return result; } }
- PriorityQueue
public String reorganizeString(String S) { int[][] count = new int[26][]; for(int i = 0; i < 26; i++) count[i] = new int[] {i, 0}; for(char c : S.toCharArray()) count[c-'a'][1]++; PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(b[1]-a[1] == 0 ? a[0]-b[0] : b[1]-a[1])); for(int[] c : count) if(c[1] > 0) q.offer(c); String r = ""; while(!q.isEmpty()) { int[] most = q.poll(); if(q.isEmpty()) { if(r.length()-1 >= 0 && r.charAt(r.length()-1) == (char)(most[0]+'a')) return ""; r += (char)(most[0]+'a'); } else { int[] second = q.poll(); r += (char)(most[0]+'a') + "" + (char)(second[0]+'a'); second[1]--; if(second[1] > 0) q.offer(second); } most[1]--; if(most[1] > 0) q.offer(most); } return r; }
-
416. Partition Equal Subset Sum
416. Partition Equal Subset Sum
Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal. Note: Each of the array element will not exceed 100. The array size will not exceed 200. Example 1: Input: [1, 5, 11, 5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11]. Example 2: Input: [1, 2, 3, 5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.
- 01 Knapsack
See Coin Change
public boolean canPartition(int[] nums) { int sum = 0; for(int i = 0; i < nums.length; i++) sum += nums[i]; if(sum % 2 == 1) return false; int target = sum / 2; boolean[] dp = new boolean[target+1]; dp[0] = true; for(int coin : nums) { for(int t = target; t >= 1; t--) { if(t-coin >= 0) dp[t] = dp[t] || dp[t-coin]; } } return dp[target]; } public boolean canPartitionDPN(int[] nums) { int sum = 0; for(int i = 0; i < nums.length; i++) sum += nums[i]; if(sum % 2 == 1) return false; int target = sum / 2; boolean[][] dp = new boolean[nums.length+1][target+1]; dp[0][0] = true; for(int t = 1; t <= target; t++) { for(int i = 1; i <= nums.length; i++) { dp[i][t] = dp[i-1][t] || (t-nums[i-1] >= 0 ? dp[i-1][t-nums[i-1]] : false); } } return dp[nums.length][target];
- Brutal
class Solution { public boolean canPartition(int[] nums) { int sum = 0; for(int i = 0; i < nums.length; i++) sum += nums[i]; if(sum % 2 == 1) return false; System.out.println(sum / 2); ArrayList<Integer> arr = new ArrayList<>(); for(int i : nums) arr.add(i); Collections.sort(arr, Collections.reverseOrder()); for(int i = 0; i < nums.length; i++) nums[i] = arr.get(i); boolean result = backtrack(nums, 0, 0, sum / 2, 0); return result; } public boolean backtrack(int[] nums, int start, int sum, int target, int abandon) { if(sum > target || start == nums.length || abandon > target) return false; if(sum == target) return true; boolean result = false; for(int i = start; i < nums.length; i++) { abandon += (i > start ? nums[i-1] : 0); result |= backtrack(nums, i+1, sum+nums[i], target, abandon); if(result) return true; } return result; } }
-
708. Insert into a Cyclic Sorted List
708. Insert into a Cyclic Sorted List
Given a node from a cyclic linked list which is sorted in ascending order, write a function to insert a value into the list such that it remains a cyclic sorted list. The given node can be a reference to any single node in the list, and may not be necessarily the smallest value in the cyclic list. If there are multiple suitable places for insertion, you may choose any place to insert the new value. After the insertion, the cyclic list should remain sorted. If the list is empty (i.e., given node is null), you should create a new single cyclic list and return the reference to that single node. Otherwise, you should return the original given node. The following example may help you understand the problem better:
In the figure above, there is a cyclic sorted list of three elements. You are given a reference to the node with value 3, and we need to insert 2 into the list.
The new node should insert between node 1 and node 3. After the insertion, the list should look like this, and we should still return node 3.
- trival
class Solution { public Node insert(Node head, int insertVal) { Node n = new Node(insertVal); if(head == null) { n.next = n; return n; } if(head.next == head) { head.next = n; n.next = head; return head; } Node largest = head; while(largest.next.val >= largest.val && largest.next != head) largest = largest.next; Node smallest = largest.next; if(n.val <= smallest.val || n.val >= largest.val) { largest.next = n; n.next = smallest; return head; } Node prev = head; while(!(prev.val <= n.val && prev.next.val >= n.val)) { prev = prev.next; if(prev == head) break; } n.next = prev.next; prev.next = n; return head; } }
-
545. Boundary of Binary Tree
Given a binary tree, return the values of its boundary in anti-clockwise direction starting from root. Boundary includes left boundary, leaves, and right boundary in order without duplicate nodes. (The values of the nodes may still be duplicates.) Left boundary is defined as the path from root to the left-most node. Right boundary is defined as the path from root to the right-most node. If the root doesn't have left subtree or right subtree, then the root itself is left boundary or right boundary. Note this definition only applies to the input binary tree, and not applies to any subtrees. The left-most node is defined as a leaf node you could reach when you always firstly travel to the left subtree if exists. If not, travel to the right subtree. Repeat until you reach a leaf node. The right-most node is also defined by the same way with left and right exchanged. Example 1 Input: 1 \ 2 / \ 3 4 Ouput: [1, 3, 4, 2] Explanation: The root doesn't have left subtree, so the root itself is left boundary. The leaves are node 3 and 4. The right boundary are node 1,2,4. Note the anti-clockwise direction means you should output reversed right boundary. So order them in anti-clockwise without duplicates and we have [1,3,4,2]. Example 2 Input: ____1_____ / \ 2 3 / \ / 4 5 6 / \ / \ 7 8 9 10 Ouput: [1,2,4,7,8,9,10,6,3] Explanation: The left boundary are node 1,2,4. (4 is the left-most node according to definition) The leaves are node 4,7,8,9,10. The right boundary are node 1,3,6,10. (10 is the right-most node). So order them in anti-clockwise without duplicate nodes we have [1,2,4,7,8,9,10,6,3].
- A fast coded answer
Searching for leftmost and rightmost and leaves are overlapped, so i’ll try to optimize the solution. Hope to solve it with one pass inorder traverse
A State Machine solution is also feasible, here are four states: on left boundary, right boundary, leaves, internal nodes.
class Solution { public List<Integer> boundaryOfBinaryTree(TreeNode root) { ArrayList<Integer> result = new ArrayList<>(); if(root == null) return result; HashSet<TreeNode> added = new HashSet<>(); // left boundary TreeNode curr = root; added.add(curr); result.add(curr.val); if(curr.left != null) { curr = curr.left; while(curr != null) { added.add(curr); result.add(curr.val); if(curr.left != null) curr = curr.left; else curr = curr.right; } } // right boundary ArrayList<Integer> temp = new ArrayList<>(); curr = root; if(curr.right != null) { curr = curr.right; while(curr != null) { if(!added.contains(curr)) { temp.add(curr.val); added.add(curr); } curr = curr.right != null ? curr.right : curr.left; } } // collect leaves collect(root, added, result); for(int i = temp.size()-1; i >= 0; i--) result.add(temp.get(i)); return result; } public void collect(TreeNode root, HashSet<TreeNode> added, ArrayList<Integer> result) { if(root == null) return; if(root.left == null && root.right == null && !added.contains(root)) { result.add(root.val); added.add(root); } collect(root.left, added, result); collect(root.right, added, result); } }
-
670. Maximum Swap
Given a non-negative integer, you could swap two digits at most once to get the maximum valued number. Return the maximum valued number you could get. Example 1: Input: 2736 Output: 7236 Explanation: Swap the number 2 and the number 7. Example 2: Input: 9973 Output: 9973 Explanation: No swap. Note: The given number is in the range [0, 108]
- Build a right max number array
class Solution { public int maximumSwap(int num) { int len = 0; int n = num; while(n > 0) { len++; n /= 10; } int[] rightMax = new int[len], arr = new int[len]; n = num; for(int i = len-1; i >= 0; i--) { arr[i] = n % 10; n /= 10; rightMax[i] = (i+1 <= len-1) ? Math.max(rightMax[i+1], arr[i+1]) : -1; } for(int i = 0; i < len; i++) { if(rightMax[i] > arr[i]) { int max = i, j = i+1; for(; j < len; j++) { max = arr[max] <= arr[j] ? j : max; } int temp = arr[max]; arr[max] = arr[i]; arr[i] = temp; break; } } int result = 0; for(int i : arr) result = result * 10 + i; return result; }
- Sorting Very Slow
class Solution { public int maximumSwap(int num) { ArrayList<int[]> arr = new ArrayList<>(); int n = num; while(n > 0) { arr.add(new int[] {arr.size(), n % 10}); n = n / 10; } ArrayList<int[]> origin = new ArrayList<>(arr); Collections.reverse(origin); Collections.sort(arr, (a,b)->(a[1]-b[1]==0? a[0]-b[0] : b[1]-a[1])); for(int i = 0; i < arr.size(); i++) { if(arr.get(i)[1] == origin.get(i)[1]) continue; int j = i; while(j-1 >= 0 && arr.get(j)[1] == arr.get(j-1)[1]) { j--; } int oi = arr.size()-1-arr.get(j)[0]; int[] temp = origin.get(oi); origin.set(oi, origin.get(i)); origin.set(i, temp); break; } int result = 0; for(int i = 0; i < origin.size(); i++) { result = result * 10 + origin.get(i)[1]; } return result; }
-
1057. Campus Bikes
On a campus represented as a 2D grid, there are N workers and M bikes, with N <= M. Each worker and bike is a 2D coordinate on this grid. Our goal is to assign a bike to each worker. Among the available bikes and workers, we choose the (worker, bike) pair with the shortest Manhattan distance between each other, and assign the bike to that worker. (If there are multiple (worker, bike) pairs with the same shortest Manhattan distance, we choose the pair with the smallest worker index; if there are multiple ways to do that, we choose the pair with the smallest bike index). We repeat this process until there are no available workers. The Manhattan distance between two points p1 and p2 is Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|. Return a vector ans of length N, where ans[i] is the index (0-indexed) of the bike that the i-th worker is assigned to. Example 1: Input: workers = [[0,0],[2,1]], bikes = [[1,2],[3,3]] Output: [1,0] Explanation: Worker 1 grabs Bike 0 as they are closest (without ties), and Worker 0 is assigned Bike 1. So the output is [1, 0]. Example 2: Input: workers = [[0,0],[1,1],[2,0]], bikes = [[1,0],[2,2],[2,1]] Output: [0,2,1] Explanation: Worker 0 grabs Bike 0 at first. Worker 1 and Worker 2 share the same distance to Bike 2, thus Worker 1 is assigned to Bike 2, and Worker 2 will take Bike 1. So the output is [0,2,1]. Note: 0 <= workers[i][j], bikes[i][j] < 1000 All worker and bike locations are distinct. 1 <= workers.length <= bikes.length <= 1000
- Bucket Sorting
public int[] assignBikes(int[][] workers, int[][] bikes) { ArrayList<int[]>[] buckets = new ArrayList[2000]; for(int i = 0; i < buckets.length; i++) buckets[i] = new ArrayList<int[]>(); for(int i = 0; i < workers.length; i++) { for(int j = 0; j < bikes.length; j++) { int d = Math.abs(workers[i][0]-bikes[j][0])+Math.abs(workers[i][1]-bikes[j][1]); buckets[d].add(new int[]{i,j}); } } boolean[] vw = new boolean[workers.length], vb = new boolean[bikes.length]; int[] result = new int[workers.length]; for(int i = 0; i < buckets.length; i++) { for(int[] p : buckets[i]) { if(vw[p[0]] || vb[p[1]]) continue; vw[p[0]] = true; vb[p[1]] = true; result[p[0]] = p[1]; } } return result; }
- Simple Sorting
public int[] assignBikes(int[][] workers, int[][] bikes) { boolean[] vw = new boolean[workers.length], vb = new boolean[bikes.length]; ArrayList<int[]> arr = new ArrayList<>(); for(int i = 0; i < workers.length; i++) { for(int j = 0; j < bikes.length; j++) { int[] distance = new int[] {i, j, Math.abs(workers[i][0]-bikes[j][0])+Math.abs(workers[i][1]-bikes[j][1])}; arr.add(distance); } } Collections.sort(arr, (a,b)->(a[2]-b[2] == 0 ? a[0]-b[0] : a[2]-b[2])); int[] result = new int[workers.length]; for(int[] p : arr) { if(vw[p[0]] || vb[p[1]]) continue; vw[p[0]] = true; vb[p[1]] = true; result[p[0]] = p[1]; } return result; }
- TLE 27/28 PriorityQueue
class Solution { public int[] assignBikes(int[][] workers, int[][] bikes) { PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(a[2]-b[2] == 0 ? a[0]-b[0] : a[2]-b[2])); int[][][] wb = new int[workers.length][bikes.length][]; for(int i = 0; i < workers.length; i++) { for(int j = 0; j < bikes.length; j++) { int[] distance = new int[] {i, j, Math.abs(workers[i][0]-bikes[j][0])+Math.abs(workers[i][1]-bikes[j][1])}; wb[i][j] = distance; q.offer(distance); } } int[] result = new int[workers.length]; while(!q.isEmpty()) { int[] curr = q.poll(); int w = curr[0], b = curr[1]; result[w] = b; for(int i = 0; i < workers.length; i++) q.remove(wb[i][b]); for(int i = 0; i < bikes.length; i++) q.remove(wb[w][i]); } return result; } }
-
759. Employee Free Time
We are given a list schedule of employees, which represents the working time for each employee. Each employee has a list of non-overlapping Intervals, and these intervals are in sorted order. Return the list of finite intervals representing common, positive-length free time for all employees, also in sorted order. Example 1: Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]] Output: [[3,4]] Explanation: There are a total of three employees, and all common free time intervals would be [-inf, 1], [3, 4], [10, inf]. We discard any intervals that contain inf as they aren't finite. Example 2: Input: schedule = [[[1,3],[6,7]],[[2,4]],[[2,5],[9,12]]] Output: [[5,6],[7,9]] (Even though we are representing Intervals in the form [x, y], the objects inside are Intervals, not lists or arrays. For example, schedule[0][0].start = 1, schedule[0][0].end = 2, and schedule[0][0][0] is not defined.) Also, we wouldn't include intervals like [5, 5] in our answer, as they have zero length. Note: schedule and schedule[i] are lists with lengths in range [1, 50]. 0 <= schedule[i].start < schedule[i].end <= 10^8. NOTE: input types have been changed on June 17, 2019. Please reset to default code definition to get new method signature.
- the comparator is kind of tricky, but building a linkedlist of intervals is also troublesome
The main logic is the same with Merge K sorted LinkedList
class Solution { // merge intervals public List<Interval> employeeFreeTime(List<List<Interval>> schedule) { int[] ps = new int[schedule.size()]; // the pointers to the current head interval for every person ArrayList<Interval> arr = new ArrayList<>(); PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->(schedule.get(a).get(ps[a]).start - schedule.get(b).get(ps[b]).start)); for(int i = 0; i < ps.length; i++) { q.offer(i); } arr.add(schedule.get(q.peek()).get(0)); while(!q.isEmpty()) { int index = q.poll(); Interval i = schedule.get(index).get(ps[index]), j = arr.get(arr.size()-1); // j.start <= i.start if(ps[index]+1 < schedule.get(index).size()) { ps[index]++; q.offer(index); } if(i.start <= j.end) { // merge j.end = Math.max(j.end, i.end); } else { // add new arr.add(i); } } ArrayList<Interval> result = new ArrayList<>(); for(int i = 1; i < arr.size(); i++) { result.add(new Interval(arr.get(i-1).end, arr.get(i).start)); } return result; } }
-
680. Valid Palindrome II
Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining. Note: Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000. Example: Given the following 3x6 height map: [ [1,4,3,1,3,2], [3,2,1,3,2,4], [2,3,3,2,3,1] ] Return 4. The above image represents the elevation map [[1,4,3,1,3,2],[3,2,1,3,2,4],[2,3,3,2,3,1]] before the rain.
After the rain, water is trapped between the blocks. The total volume of water trapped is 4.
- 地方包围中央,不断垒高墙
build a wall surround the matrix, and push the boundary toward the center. The forwarding direction is the neighbors of the lowest point of the wall, because the water volume of these points are available.
class Solution { public int trapRainWater(int[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return 0; PriorityQueue<int[]> q = new PriorityQueue<>((x,y)->(matrix[x[0]][x[1]]-matrix[y[0]][y[1]])); boolean[][] visited = new boolean[matrix.length][matrix[0].length]; for(int i = 0; i < matrix.length; i++) { q.offer(new int[]{i, 0}); q.offer(new int[]{i, matrix[0].length-1}); visited[i][0] = true; visited[i][matrix[0].length-1] = true; } for(int j = 1; j < matrix[0].length-1; j++) { q.offer(new int[]{0, j}); q.offer(new int[]{matrix.length-1, j}); visited[0][j] = true; visited[matrix.length-1][j] = true; } int max = 0; // monotonic increase // the point with the lowest height of the wall int result = 0; int[] xs = {0,0,1,-1}, ys = {1,-1,0,0}; while(!q.isEmpty()) { int[] n = q.poll(); int lowest = matrix[n[0]][n[1]]; if(lowest > max) max = lowest; for(int k = 0; k < 4; k++) { int i = xs[k] + n[0], j = ys[k] + n[1]; if(i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length || visited[i][j]) continue; int neighbor = matrix[i][j]; if(neighbor < max) result += max-neighbor; q.offer(new int[]{i, j}); visited[i][j] = true; } } return result; } }
-
430. Flatten a Multilevel Doubly Linked List
430. Flatten a Multilevel Doubly Linked List
You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below. Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list. Example: Input: 1---2---3---4---5---6--NULL | 7---8---9---10--NULL | 11--12--NULL Output: 1-2-3-7-8-11-12-9-10-4-5-6-NULL
- Preprocess the special case: the tail has a child.
/* // Definition for a Node. class Node { public int val; public Node prev; public Node next; public Node child; public Node() {} public Node(int _val,Node _prev,Node _next,Node _child) { val = _val; prev = _prev; next = _next; child = _child; } }; */ class Solution { public Node flatten(Node head) { // System.out.println(head.val); build(head); return head; } public Node build(Node head) { // System.out.println(head.val); if(head == null) return null; Node tail = head, prev = head; while(tail != null) { if(tail.next == null && tail.child != null) { Node child = tail.child; tail.next = child; child.prev = tail; tail.child = null; } prev = tail; tail = tail.next; } tail = prev; Node curr = head; while(curr != tail) { Node next = curr.next; if(curr.child != null) { Node child = curr.child; curr.child = null; Node childTail = build(child); curr.next = child; child.prev = curr; childTail.next = next; next.prev = childTail; } curr = next; } return tail; } }
-
109. Convert Sorted List to Binary Search Tree
109. Convert Sorted List to Binary Search Tree
Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1. Example: Given the sorted linked list: [-10,-3,0,5,9], One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: 0 / \ -3 9 / / -10 5
- Naive recursion
Another option is to convert the list into an array. Trade off space for time
public TreeNode sortedListToBST(ListNode head) { return build(head); } public TreeNode build(ListNode head) { if(head == null) return null; if(head.next == null) return new TreeNode(head.val); ListNode fast = head, slow = head, prev = head; while(fast != null && fast.next != null) { fast = fast.next.next; prev = slow; slow = slow.next; } prev.next = null; TreeNode root = new TreeNode(slow.val); root.left = build(head); root.right = build(slow.next); return root; }
- a logN space approach
class Solution { ListNode head; public TreeNode sortedListToBST(ListNode head) { this.head = head; ListNode curr = head; int len = 0; while(curr != null) { len++; curr = curr.next; } TreeNode root = build(0, len-1); return root; } public TreeNode build(int l, int r) { if(l > r) return null; int mid = l + (r-l) / 2; TreeNode left = build(l, mid-1); TreeNode root = new TreeNode(head.val); head = head.next; root.left = left; root.right = build(mid+1, r); return root; }
-
282. Expression Add Operators
Given a string that contains only digits 0-9 and a target value, return all possibilities to add binary operators (not unary) +, -, or * between the digits so they evaluate to the target value. Example 1: Input: num = "123", target = 6 Output: ["1+2+3", "1*2*3"] Example 2: Input: num = "232", target = 8 Output: ["2*3+2", "2+3*2"]
- Brutal force
public class Solution { public List<String> addOperators(String num, int target) { List<String> result = new ArrayList<>(); calculate(result, num, "", target, 0, 0, 0); return result; } public void calculate(List<String> result, String s, String path, int target, long value, int start, long multi) { if(start == s.length() && value == target) { result.add(path); } for(int i = start; i < s.length(); i++) { if(i > start && s.charAt(start) == '0') return; Long curr = Long.valueOf(s.substring(start, i+1)); if(start == 0) { calculate(result, s, path + curr, target, curr, i+1, curr); } else { calculate(result, s, path + "+" + curr, target, value+curr, i+1, curr); calculate(result, s, path + "-" + curr, target, value-curr, i+1, -curr); calculate(result, s, path + "*" + curr, target, value-multi+curr*multi, i+1, curr*multi); } } } }
-
680. Valid Palindrome II
Given a non-empty string s, you may delete at most one character. Judge whether you can make it a palindrome. Example 1: Input: "aba" Output: True Example 2: Input: "abca" Output: True Explanation: You could delete the character 'c'. Note: The string will only contain lowercase characters a-z. The maximum length of the string is 50000.
- easy
class Solution { public boolean validPalindrome(String s) { int l = 0, r = s.length()-1; boolean deleted = false; while(l < r) { if(s.charAt(l) == s.charAt(r)) { l++; r--; } else { break; } } if(l >= r) return true; return check(s, l+1, r) || check(s, l, r-1); } public boolean check(String s, int l, int r) { while(l < r) { if(s.charAt(l++) != s.charAt(r--)) return false; } return true; } }
-
943. Find the Shortest Superstring
943. Find the Shortest Superstring
Given an array A of strings, find any smallest string that contains each string in A as a substring. We may assume that no string in A is substring of another string in A. Example 1: Input: ["alex","loves","leetcode"] Output: "alexlovesleetcode" Explanation: All permutations of "alex","loves","leetcode" would also be accepted. Example 2: Input: ["catg","ctaagt","gcta","ttca","atgcatc"] Output: "gctaagttcatgcatc" Note: 1 <= A.length <= 12 1 <= A[i].length <= 20
This is a travelling salesman problem… The answer is the shortest/longest distance of a weighted directed graph… Each String can be regarded as a node, and the overlapped length are the reward of edges
- A fast coded TLE version 58 / 72 test cases passed.
TODO: A iterative DP Solution
class Solution { String result = ""; int max; HashSet<String> visited = new HashSet<>(); HashMap<Integer, Integer> map = new HashMap<>(); public String shortestSuperstring(String[] A) { for(String s : A) result += s; max = result.length(); HashSet<Integer> set = new HashSet<>(); map.put(0, 0); build(A, "", set, 0, 0); return result; } public void build(String[] arr, String s, HashSet<Integer> set, int mask, int saved) { if(map.containsKey(mask)) { if(map.get(mask) > saved) return; map.put(mask, saved); } if(s.length() >= result.length()) return; if(set.size() == arr.length) { if(s.length() < result.length()) result = s; } for(int j = 0; j < arr.length; j++) { int nextMask = mask | (1 << j); if(set.contains(j)) continue; set.add(j); String curr = arr[j]; boolean overlap = false; if(s.contains(curr)) { build(arr, s, set, nextMask, saved + curr.length()); } else { // curr[i:] overlap for(int i = curr.length()-1; i > 0; i--) { String right = curr.substring(i); if(s.startsWith(right)) { build(arr, curr.substring(0, i) + s, set, nextMask, saved + right.length()); overlap = true; } } // curr[:i] overlap for(int i = 1; i < curr.length(); i++) { String left = curr.substring(0, i); if(s.endsWith(left)) { build(arr, s + curr.substring(i), set, nextMask, saved + left.length()); overlap = true; } } } if(!overlap) { build(arr, s + curr, set, nextMask, saved); build(arr, curr + s, set, nextMask, saved); } set.remove(j); } }
-
150. Evaluate Reverse Polish Notation
150. Evaluate Reverse Polish Notation
Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, *, /. Each operand may be an integer or another expression. Note: Division between two integers should truncate toward zero. The given RPN expression is always valid. That means the expression would always evaluate to a result and there won't be any divide by zero operation. Example 1: Input: ["2", "1", "+", "3", "*"] Output: 9 Explanation: ((2 + 1) * 3) = 9 Example 2: Input: ["4", "13", "5", "/", "+"] Output: 6 Explanation: (4 + (13 / 5)) = 6 Example 3: Input: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] Output: 22 Explanation: ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 = ((10 * (6 / (12 * -11))) + 17) + 5 = ((10 * (6 / -132)) + 17) + 5 = ((10 * 0) + 17) + 5 = (0 + 17) + 5 = 17 + 5 = 22
- easy
class Solution { public int evalRPN(String[] tokens) { Stack<Integer> stack = new Stack<>(); for(String s : tokens) { if(s.equals("+")) stack.push(stack.pop()+stack.pop()); else if(s.equals("-")) stack.push(-stack.pop()+stack.pop()); else if(s.equals("*")) stack.push(stack.pop()*stack.pop()); else if(s.equals("/")) { int divisor = stack.pop(), dividend = stack.pop(); stack.push(dividend / divisor); } else { stack.push(Integer.valueOf(s)); } } return stack.pop(); } }
-
136. Single Number
Design a Phone Directory which supports the following operations: get: Provide a number which is not assigned to anyone. check: Check if a number is available or not. release: Recycle or release a number. Example: // Init a phone directory containing a total of 3 numbers: 0, 1, and 2. PhoneDirectory directory = new PhoneDirectory(3); // It can return any available phone number. Here we assume it returns 0. directory.get(); // Assume it returns 1. directory.get(); // The number 2 is available, so return true. directory.check(2); // It returns 2, the only number that is left. directory.get(); // The number 2 is no longer available, so return false. directory.check(2); // Release number 2 back to the pool. directory.release(2); // Number 2 is available again, return true. directory.check(2);
- HashSet
class PhoneDirectory { HashSet<Integer> set = new HashSet<>(); /** Initialize your data structure here @param maxNumbers - The maximum numbers that can be stored in the phone directory. */ public PhoneDirectory(int maxNumbers) { for(int i = 0; i < maxNumbers; i++) set.add(i); } /** Provide a number which is not assigned to anyone. @return - Return an available number. Return -1 if none is available. */ public int get() { if(set.isEmpty()) return -1; int n = set.iterator().next(); set.remove(n); return n; } /** Check if a number is available or not. */ public boolean check(int number) { return set.contains(number); } /** Recycle or release a number. */ public void release(int number) { set.add(number); } }
- A late adding version
class PhoneDirectory { HashSet<Integer> released = new HashSet<>(); int p; int max; /** Initialize your data structure here @param maxNumbers - The maximum numbers that can be stored in the phone directory. */ public PhoneDirectory(int maxNumbers) { max = maxNumbers; } /** Provide a number which is not assigned to anyone. @return - Return an available number. Return -1 if none is available. */ public int get() { if(released.isEmpty() && p == max) return -1; if(released.isEmpty()) return p++; int v = released.iterator().next(); released.remove(v); return v; } /** Check if a number is available or not. */ public boolean check(int number) { return number >= p || released.contains(number); } /** Recycle or release a number. */ public void release(int number) { if(number > p-1) return; released.add(number); } }
-
353. Design Snake Game
Given a non-empty array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Example 1: Input: [2,2,1] Output: 1 Example 2: Input: [4,1,2,1,2] Output: 4
- Double ended queue
I tried to use a hashset to record the body of the snake, but it is much slower than a leaner search over the queue. why???
class SnakeGame { ArrayDeque<Node> q = new ArrayDeque<>(); int p; int score; int[][] food; int n, m; /** Initialize your data structure here. @param width - screen width @param height - screen height @param food - A list of food positions E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. */ public SnakeGame(int w, int h, int[][] food) { q.offer(new Node(0, 0)); this.food = food; n = h; m = w; } class Node { int i; int j; public Node(int i, int j) { this.i = i; this.j = j; } } /** Moves the snake. @param direction - 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down @return The game's score after the move. Return -1 if game over. Game over when snake crosses the screen boundary or bites its body. */ public int move(String direction) { Node curr = q.peekLast(), tail = q.peekFirst(); int ni = curr.i, nj = curr.j, ti = tail.i, tj = tail.j; switch(direction) { case "U" : ni--; break; case "D" : ni++; break; case "L" : nj--; break; case "R" : nj++; } if(ni < 0 || nj < 0 || ni >= n || nj >= m) return -1; if(p == food.length || !(ni == food[p][0] && nj == food[p][1])) { // not eat, give up the tail q.pollFirst(); } else { score++; p++; } for(Node n : q) { if(ni == n.i && nj == n.j) return -1; } Node nh = new Node(ni, nj); q.offerLast(nh); return p; } }
- MLE failed on a 10000 * 10000 matrix input.
class SnakeGame { int[][] matrix; int fp; int score; Node head, tail; HashMap<String, int[]> map = new HashMap<>(); int[][] food; /** Initialize your data structure here. @param width - screen width @param height - screen height @param food - A list of food positions E.g food = [[1,1], [1,0]] means the first food is positioned at [1,1], the second is at [1,0]. */ public SnakeGame(int width, int height, int[][] food) { this.food = food; matrix = new int[height][width]; if(food.length > 0) matrix[food[fp][0]][food[fp++][1]] = 2; matrix[0][0] = 1; head = new Node(0, 0); tail = head; map.put("U", new int[]{-1,0}); map.put("D", new int[]{1,0}); map.put("L", new int[]{0,-1}); map.put("R", new int[]{0,1}); } class Node { int i; int j; Node next; public Node(int i, int j) { this.i = i; this.j = j; } } /** Moves the snake. @param direction - 'U' = Up, 'L' = Left, 'R' = Right, 'D' = Down @return The game's score after the move. Return -1 if game over. Game over when snake crosses the screen boundary or bites its body. */ public int move(String direction) { int[] d = map.get(direction); int i = head.i + d[0], j = head.j + d[1]; if(i < 0 || j < 0 || i >= matrix.length || j >= matrix[0].length) { return -1; } if(matrix[i][j] == 1 && !(i == tail.i && j == tail.j)) return -1; Node newHead = new Node(i, j); head.next = newHead; head = newHead; if(matrix[i][j] != 2) { matrix[tail.i][tail.j] = 0; Node newTail = tail.next; tail.next = null; tail = newTail; } if(matrix[i][j] == 2) { // food score++; if(fp < food.length) matrix[food[fp][0]][food[fp++][1]] = 2; } matrix[i][j] = 1; return score; }
-
97. Interleaving String
Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. Example 1: Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbcbcac" Output: true Example 2: Input: s1 = "aabcc", s2 = "dbbca", s3 = "aadbbbaccc" Output: false
- easy
Reminds me the Regular Expression Matching and Wild Matching
public boolean isInterleave(String s1, String s2, String s3) { if(s1.length() + s2.length() != s3.length()) return false; HashMap<Character, Integer> map = new HashMap<>(); for(char c : (s1+s2).toCharArray()) map.put(c, map.getOrDefault(c, 0)+1); for(char c : s3.toCharArray()) { if(!map.containsKey(c)) return false; map.put(c, map.get(c)-1); } for(int i : map.values()) if(i != 0) return false; int[][] memo = new int[s1.length()+1][s2.length()+1]; return check(s1, s2, s3, memo); } public boolean check(String s1, String s2, String s3, int[][] memo) { if(memo[s1.length()][s2.length()] != 0) return memo[s1.length()][s2.length()] == 1; if((s1+s2).equals(s3) || (s2+s1).equals(s3)) return true; if(s1.length() == 0 || s2.length() == 0) return false; boolean result = false; if(s1.charAt(0) == s3.charAt(0)) result |= check(s1.substring(1), s2, s3.substring(1), memo); if(!result && s2.charAt(0) == s3.charAt(0)) result |= check(s1, s2.substring(1), s3.substring(1), memo); memo[s1.length()][s2.length()] = result ? 1 : 2; return result; }
- DP Version
public boolean isInterleave(String s1, String s2, String s3) { if(s1.length() + s2.length() != s3.length()) return false; HashMap<Character, Integer> map = new HashMap<>(); for(char c : (s1+s2).toCharArray()) map.put(c, map.getOrDefault(c, 0)+1); for(char c : s3.toCharArray()) { if(!map.containsKey(c)) return false; map.put(c, map.get(c)-1); } for(int i : map.values()) if(i != 0) return false; boolean[][] dp = new boolean[s1.length()+1][s2.length()+1]; dp[0][0] = true; for(int i = 1; i <= s1.length(); i++) dp[i][0] = s3.startsWith(s1.substring(0,i)); for(int i = 1; i <= s2.length(); i++) dp[0][i] = s3.startsWith(s2.substring(0,i)); for(int k = 2; k <= s3.length(); k++) { for(int i = k-s2.length() > 1 ? k-s2.length() : 1; i < k && i <= s1.length(); i++) { int j = k - i; dp[i][j] = (dp[i-1][j] && s1.charAt(i-1) == s3.charAt(k-1)) || (dp[i][j-1] && s2.charAt(j-1) == s3.charAt(k-1)); } } return dp[s1.length()][s2.length()]; }
-
260. Single Number III
Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Example 1: Input: [2,2,3,2] Output: 3 Example 2: Input: [0,1,0,1,0,1,99] Output: 99
- Divide nums into two parts. Each of the two parts has one number.
public int[] singleNumber(int[] nums) { int ab = 0; for(int n : nums) ab ^= n; int firstOnePosition = 0; while(((ab >> firstOnePosition) & 1) != 1) firstOnePosition++; int a = 0, b = 0; for(int n : nums) { if(((n >> firstOnePosition) & 1) == 1) a ^= n; else b ^= n; } return new int[] { a,b }; }
-
137. Single Number II
Given a non-empty array of integers, every element appears three times except for one, which appears exactly once. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Example 1: Input: [2,2,3,2] Output: 3 Example 2: Input: [0,1,0,1,0,1,99] Output: 99
- 00, 10, 11 represents three states zero or three, one, two
public int singleNumber(int[] nums) { int once = 0, twice = 0; for(int i : nums) { once = ~twice & (once ^ i); twice = ~once & (twice ^ i); } return once; }
- A more understandable version Count the 1s at very bits for all the numbers, and module the repeated times
public int singleNumber(int[] nums) { int[] bits = new int[32]; for(int n : nums) { for(int i = 0; i < 32; i++) bits[31-i] += (n >> i) & 1; } int ans = 0; for(int i = 0; i < 32; i++) { if(bits[i] % 3 == 1) ans |= (1 << (31-i)); } return ans; }
-
136. Single Number
Given a non-empty array of integers, every element appears twice except for one. Find that single one. Note: Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? Example 1: Input: [2,2,1] Output: 1 Example 2: Input: [4,1,2,1,2] Output: 4
- xor
public int singleNumber(int[] nums) { int result = nums[0]; for(int i = 1; i < nums.length; i++) result ^= nums[i]; return result; }
-
472. Concatenated Words
Given a list of words (without duplicates), please write a program that returns all concatenated words in the given list of words. A concatenated word is defined as a string that is comprised entirely of at least two shorter words in the given array. Example: Input: ["cat","cats","catsdogcats","dog","dogcatsdog","hippopotamuses","rat","ratcatdogcat"] Output: ["catsdogcats","dogcatsdog","ratcatdogcat"] Explanation: "catsdogcats" can be concatenated by "cats", "dog" and "cats"; "dogcatsdog" can be concatenated by "dog", "cats" and "dog"; "ratcatdogcat" can be concatenated by "rat", "cat", "dog" and "cat". Note: The number of elements of the given array will not exceed 10,000 The length sum of elements in the given array will not exceed 600,000. All the input string will only include lower case letters. The returned elements order does not matter.
- Two heaps is a good friend of median of streaming data
Adding a memo pad made the code even slower… cleared the map every iteration
Another approach is DP, the same as Word Break
class Solution { Node head = new Node(); public List<String> findAllConcatenatedWordsInADict(String[] words) { if(words.length == 0) return new ArrayList<>(); // build the Tier for(String w : words) { if(w.equals("")) continue; Node curr = head; for(char c : w.toCharArray()) { if(curr.arr[c-'a'] == null) curr.arr[c-'a'] = new Node(); curr = curr.arr[c-'a']; } curr.w = w; } // search the Tier for every word: Backtrack List<String> result = new ArrayList<>(); for(String w : words) { if(w.equals("")) continue; Node curr = head; for(char c : w.toCharArray()) curr = curr.arr[c-'a']; curr.w = null; // the current word itself is not a legal sub word if(backtrack(w, map)) result.add(w); curr.w = w; } return result; } public boolean backtrack(String w) { if(w.length() == 0) return true; Node curr = head; boolean result = false; for(int i = 0; i < w.length(); i++) { char c = w.charAt(i); curr = curr.arr[c-'a']; if(curr == null) return false; if(curr.w != null) { result |= backtrack(w.substring(i+1)); } if(result) break; } return result; } class Node { Node[] arr = new Node[26]; boolean isWord; String w; } }
- DP
public List<String> findAllConcatenatedWordsInADict(String[] words) { HashSet<String> set = new HashSet<>(); for(String w : words) set.add(w); ArrayList<String> result = new ArrayList<>(); for(String w : words) { set.remove(w); if(wordBreak(set, w)) result.add(w); set.add(w); } return result; } public boolean wordBreak(HashSet<String> set, String s) { if(s.length() == 0) return false; 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; if(dp[i]) break; } } return dp[s.length()]; }
-
346. Moving Average from Data Stream
346. Moving Average from Data Stream
Given a stream of integers and a window size, calculate the moving average of all integers in the sliding window.
Example:
MovingAverage m = new MovingAverage(3); m.next(1) = 1 m.next(10) = (1 + 10) / 2 m.next(3) = (1 + 10 + 3) / 3 m.next(5) = (10 + 3 + 5) / 3
- array or queue
class MovingAverage { int[] arr; int p; int sum; int cnt; /** Initialize your data structure here. */ public MovingAverage(int size) { arr = new int[size]; } public double next(int val) { if(cnt < arr.length) { sum += val; arr[cnt++] = val; return sum / (double) cnt; } else { int removed = arr[p]; arr[p++] = val; if(p == arr.length) p = 0; sum -= removed; sum += val; return sum / (double) arr.length; } } }
-
480. Sliding Window Median
Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. Examples: [2,3,4] , the median is 3 [2,3], the median is (2 + 3) / 2 = 2.5 Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Your job is to output the median array for each window in the original array. For example, Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. Window position Median --------------- ----- [1 3 -1] -3 5 3 6 7 1 1 [3 -1 -3] 5 3 6 7 -1 1 3 [-1 -3 5] 3 6 7 -1 1 3 -1 [-3 5 3] 6 7 3 1 3 -1 -3 [5 3 6] 7 5 1 3 -1 -3 5 [3 6 7] 6 Therefore, return the median sliding window as [1,-1,-1,3,5,6]. Note: You may assume k is always valid, ie: k is always smaller than input array's size for non-empty array.
- Two heaps is a good friend of median of streaming data
class Solution { PriorityQueue<Integer> min = new PriorityQueue<>(); // minheap stores large values PriorityQueue<Integer> max = new PriorityQueue<>(Collections.reverseOrder()); public double[] medianSlidingWindow(int[] nums, int k) { for(int i = 0; i < k; i++) enque(nums[i]); double[] result = new double[nums.length-k+1]; result[0] = getMedian(); int j = 1; for(int i = 0; i < nums.length-k; i++) { int o = nums[i], v = nums[i+k]; if(o >= min.peek()) min.remove(o); else max.remove(o); enque(v); result[j++] = getMedian(); } return result; } public void enque(int v) { min.offer(v); max.offer(min.poll()); if(max.size() > min.size()) min.offer(max.poll()); } public double getMedian() { if(min.size() == max.size()) return (min.peek() + (double)max.peek()) / 2; return (double)min.peek(); }
- A Failed 1.5 hours try on doubly linked list. i shi le zhi le….
A right direcion at the very beginning is more important than fast coding T_T The two heap solution just takes 10 minutes
public double[] medianSlidingWindow(int[] nums, int k) { int[] original = new int[k]; System.arraycopy(nums, 0, original, 0, k); MyList l = new MyList(original); l.print(); for(int i = 0; i < nums.length - k; i++) { l.move(nums[i], nums[i+k]); double median = l.getMedian(); } return null; } class MyList { TreeMap<Integer, LinkedList<Node>> map = new TreeMap<>(); Node head; Node tail; Node mid; boolean isOdd; public MyList(int[] arr) { Arrays.sort(arr); isOdd = arr.length % 2 == 1; head = new Node(Integer.MIN_VALUE); tail = new Node(Integer.MAX_VALUE); head.next = tail; tail.prev = head; Node p = head; for(int v : arr) { Node curr = new Node(v); p.next.prev = curr; curr.next = p.next; p.next = curr; curr.prev = p; p = curr; LinkedList<Node> sub = map.getOrDefault(v, new LinkedList<Node>()); sub.add(curr); map.put(v, sub); } Node fast = head.next, slow = head.next; while(fast != tail && fast.next != tail) { fast = fast.next.next; slow = slow.next; } mid = slow; if(arr.length % 2 == 0) mid = mid.prev; System.out.println("init map=" + map); } public void move(int o, int n) { System.out.println("replace " + o + " -> " + n); if(o == n) return; LinkedList<Node> arr = map.get(o); Node moved = arr.removeLast(); if(arr.size()==0) map.remove(o); moved.val = n; arr = map.getOrDefault(n, new LinkedList<Node>()); arr.add(moved); map.put(n, arr); if(moved == mid) { if(n > o) mid = mid.next; else mid = mid.prev; } // Fail Reason: the position of mid throught the move........... if(moved.val <= moved.prev.val) { while(moved.val <= moved.prev.val) { Node next = moved.next, t = moved.prev; next.prev = t; t.next = next; moved.prev = t.prev; t.prev.next = moved; t.prev = moved; moved.next = t; } } if(moved.val >= moved.next.val) { while(moved.val >= moved.next.val) { // System.out.println("curr=" + moved.val + " next=" + moved.next.val); Node p = moved.prev, t = moved.next; p.next = t; t.prev = p; moved.next = t.next; t.next.prev = moved; t.next = moved; moved.prev = t; } } this.print(); System.out.println("median=" + mid.val); } public double getMedian() { if(isOdd) return (double)mid.val; return ((double)mid.val + mid.next.val) / 2; } public void print() { Node curr = head.next; System.out.print("L= "); while(curr != tail) { System.out.print(curr.val + " > "); curr = curr.next; } System.out.println(); } class Node { Node prev; Node next; int val; public Node(int v) {val = v;} public String toString() { return "" + val; } } }
-
787. Cheapest Flights Within K Stops
787. Cheapest Flights Within K Stops
There are n cities connected by m flights. Each fight starts from city u and arrives at v with a price w. Now given all the cities and flights, together with starting city src and the destination dst, your task is to find the cheapest price from src to dst with up to k stops. If there is no such route, output -1. Example 1: Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 1 Output: 200 Explanation: The graph looks like this: The cheapest price from city 0 to city 2 with at most 1 stop costs 200, as marked red in the picture. Example 2: Input: n = 3, edges = [[0,1,100],[1,2,100],[0,2,500]] src = 0, dst = 2, k = 0 Output: 500 Explanation: The graph looks like this: The cheapest price from city 0 to city 2 with at most 0 stop costs 500, as marked blue in the picture. Note: The number of nodes n will be in range [1, 100], with nodes labeled from 0 to n - 1. The size of flights will be in range [0, n * (n - 1) / 2]. The format of each flight will be (src, dst, price). The price of each flight will be in the range [1, 10000]. k is in the range of [0, n - 1]. There will not be any duplicated flights or self cycles.
The first idea came to my mind is dijkstra + limit. I did implement a dijkstra, but fail to record the steps from the src to each node because my code overwrote the steps when arrived at a node from different sources (some nodes exceed the step limits).
- Failed Dijkstra
public int findCheapestPriceFailed(int n, int[][] flights, int src, int dst, int K) { ArrayList<int[]>[] to = new ArrayList[n]; for(int i = 0; i < n; i++) to[i] = new ArrayList<int[]>(); for(int[] f : flights) { to[f[0]].add(new int[] {f[1], f[2]}); } int[] costs = new int[n]; Arrays.fill(costs, Integer.MAX_VALUE); costs[src] = 0; int[] steps = new int[n]; PriorityQueue<Integer> q = new PriorityQueue<>((x,y)->(costs[x]-costs[y])); q.offer(src); while(!q.isEmpty() && q.peek() != dst) { int curr = q.poll(); if(steps[curr] > K) continue; for(int[] f : to[curr]) { costs[f[0]] = Math.min(costs[f[0]], costs[curr]+f[1]); // update costs for next nodes steps[f[0]] = steps[curr] + 1; // !here comes the bug... q.offer(f[0]); } } return costs[dst] == Integer.MAX_VALUE ? -1 : costs[dst]; }
- Finally I get the correct solution. The key is put all the related information (steps, total costs, and stop id) into the pq
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) { ArrayList<int[]>[] to = new ArrayList[n]; // adjacent list: src -> (dsts and costs)s for(int i = 0; i < n; i++) to[i] = new ArrayList<int[]>(); for(int[] f : flights) to[f[0]].add(new int[] {f[1], f[2]}); PriorityQueue<int[]> q = new PriorityQueue<>((x,y)->(x[2]-y[2])); q.offer(new int[] {src, 0, 0}); while(!q.isEmpty()) { int[] curr = q.poll(); if(curr[0] == dst) return curr[2]; if(curr[1] > K) continue; for(int[] next : to[curr[0]]) { // next: {nextStop, cost of the edge} q.offer(new int[]{next[0], 1+curr[1], next[1]+curr[2]}); } } return -1; }
- A more understandable version
public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) { if(src == dst) return 0; ArrayList<int[]>[] graph = new ArrayList[n]; for(int i = 0; i < graph.length; i++) graph[i] = new ArrayList<>(); for(int[] f : flights) graph[f[0]].add(new int[]{f[1],f[2]}); PriorityQueue<City> q = new PriorityQueue<>((a,b)->(a.cost-b.cost)); for(int[] f : graph[src]) q.offer(new City(f[0], 0, f[1])); while(!q.isEmpty()) { City curr = q.poll(); if(curr.index == dst) { return curr.cost; } else if(curr.stop < K) { for(int[] f : graph[curr.index]) q.offer(new City(f[0], curr.stop+1, curr.cost+f[1])); } } return -1; } class City { int index; int stop; int cost; public City(int index, int stop, int cost) { this.index = index; this.stop = stop; this.cost = cost; } }
- DFS with early exit accepted but extremely slow
This trash might be what I came up with in an interview…
class Solution { // dijkstra with limit steps int result = Integer.MAX_VALUE; public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) { ArrayList<int[]>[] to = new ArrayList[n]; for(int i = 0; i < n; i++) to[i] = new ArrayList<int[]>(); for(int[] f : flights) { to[f[0]].add(new int[] {f[1], f[2]}); } boolean[] visited = new boolean[n]; find(dst, K, src, 0, to, 0, visited); return result == Integer.MAX_VALUE ? -1 : result; } public void find(int dst, int K, int curr, int step, ArrayList<int[]>[] to, int cost, boolean[] visited) { if(cost >= result) return; if(step > K+1) return; if(curr == dst) { result = Math.min(result, cost); return; } for(int[] f : to[curr]) { if(visited[f[0]]) continue; visited[f[0]] = true; find(dst, K, f[0], step+1, to, cost+f[1], visited); visited[f[0]] = false; } }
-
95. Unique Binary Search Trees II
95. Unique Binary Search Trees II
Given an integer n, generate all structurally unique BST's (binary search trees) that store values 1 ... n. Example: Input: 3 Output: [ [1,null,3,2], [3,2,null,1], [3,1,null,null,2], [2,1,3], [1,null,2,null,3] ] Explanation: The above output corresponds to the 5 unique BST's shown below: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
class Solution { public List<TreeNode> generateTrees(int n) { if(n == 0) return new ArrayList<>(); return generate(1, n); } public List<TreeNode> generate(int l, int r) { List<TreeNode> result = new ArrayList<TreeNode>(); if(l > r) result.add(null); for(int i = l; i <= r; i++) { List<TreeNode> left = generate(l, i-1), right = generate(i+1, r); for(TreeNode ln : left) { for(TreeNode rn : right) { TreeNode root = new TreeNode(i); root.left = ln; root.right = rn; result.add(root); } } } return result; } }
-
28. Implement strStr()
Implement strStr(). Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. Example 1: Input: haystack = "hello", needle = "ll" Output: 2 Example 2: Input: haystack = "aaaaa", needle = "bba" Output: -1 Clarification: What should we return when needle is an empty string? This is a great question to ask during an interview. For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C's strstr() and Java's indexOf().
-
TODO: implement KMP
-
Brutal Force
public int strStr(String s, String w) { if(w.length()==0) return 0; for(int i = 0; i < s.length() - w.length()+1 ; i++) { for(int j = 0; j < w.length(); j++) { if(s.charAt(i+j) != w.charAt(j)) break; if(j == w.length()-1) return i; } } return -1; }
-
-
863. All Nodes Distance K in Binary Tree
863. All Nodes Distance K in Binary Tree
We are given a binary tree (with root node root), a target node, and an integer value K. Return a list of the values of all nodes that have a distance K from the target node. The answer can be returned in any order. Example 1: Input: root = [3,5,1,6,2,0,8,null,null,7,4], target = 5, K = 2 Output: [7,4,1] Explanation: The nodes that are a distance 2 from the target node (with value 5) have values 7, 4, and 1. Note that the inputs "root" and "target" are actually TreeNodes. The descriptions of the inputs above are just serializations of these objects. Note: The given tree is non-empty. Each node in the tree has unique values 0 <= node.val <= 500. The target node is a node in the tree. 0 <= K <= 1000.
class Solution { // f(root.parent, K-1) + f(root.child, K-1) Stack<TreeNode> stack = new Stack<>(); public List<Integer> distanceK(TreeNode root, TreeNode target, int K) { List<Integer> result = new ArrayList<Integer>(); find(root, target); System.out.println(stack.size()); stack.push(target); HashSet<TreeNode> visited = new HashSet<>(); while(!stack.isEmpty() && K >= 0) { collect(stack.pop(), result, visited, K); K--; } return result; } public void collect(TreeNode root, List<Integer> result, HashSet<TreeNode> visited, int K) { if(root == null) return; if(visited.contains(root)) return; visited.add(root); if(K == 0) result.add(root.val); else { collect(root.left, result, visited, K-1); collect(root.right, result, visited, K-1); } } public boolean find(TreeNode root, TreeNode target) { if(root == null) return false; if(root == target) return true; stack.push(root); boolean onLeft = find(root.left, target); boolean onRight = find(root.right, target); if(!onLeft && !onRight) stack.pop(); return onLeft || onRight; } }
-
26. Remove Duplicates from Sorted Array
26. Remove Duplicates from Sorted Array
Given a sorted array nums, remove the duplicates in-place such that each element appear only once and return the new length. Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory. Example 1: Given nums = [1,1,2], Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. It doesn't matter what you leave beyond the returned length.
- Easy
class Solution { public int removeDuplicates(int[] nums) { if(nums.length == 0) return 0; int j = 1; for(int i = 1; i < nums.length; i++) { if(nums[i] != nums[i-1]) nums[j++] = nums[i]; } return j; } }
-
162. Find Peak Element
A peak element is an element that is greater than its neighbors. Given an input array nums, where nums[i] ≠ nums[i+1], find a peak element and return its index. The array may contain multiple peaks, in that case return the index to any one of the peaks is fine. You may imagine that nums[-1] = nums[n] = -∞. Example 1: Input: nums = [1,2,3,1] Output: 2 Explanation: 3 is a peak element and your function should return the index number 2.
- Easy
class Solution { public int findPeakElement(int[] nums) { int l = 0, r = nums.length-1; while(l+1 < r) { int mid = l + (r-l) / 2; int left = mid == 0 ? Integer.MIN_VALUE : nums[mid-1]; int right = mid == nums.length-1 ? Integer.MIN_VALUE : nums[mid+1]; if(nums[mid] > left && nums[mid] > right) return mid; else if(nums[mid] < left && nums[mid] > right) r = mid; else l = mid; } return nums[l] > nums[r] ? l : r; } }
Another version
public int findPeakElement(int[] nums) { int l = 0, r = nums.length-1; while(l <= r) { int mid = l + (r-l) / 2; int left = mid == 0 ? Integer.MIN_VALUE : nums[mid-1]; int right = mid == nums.length-1 ? Integer.MIN_VALUE : nums[mid+1]; if(nums[mid] >= left && nums[mid] >= right) return mid; else if(nums[mid] < left && nums[mid] > right) r = mid-1; else l = mid+1; } return nums[l] > nums[r] ? l : r; }
-
403. Frog Jump
A frog is crossing a river. The river is divided into x units and at each unit there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water. Given a list of stones' positions (in units) in sorted ascending order, determine if the frog is able to cross the river by landing on the last stone. Initially, the frog is on the first stone and assume the first jump must be 1 unit. If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. Note that the frog can only jump in the forward direction. Note: The number of stones is ≥ 2 and is < 1,100. Each stone's position will be a non-negative integer < 231. The first stone's position is always 0. Example 1: [0,1,3,5,6,8,12,17] There are a total of 8 stones. The first stone at the 0th unit, second stone at the 1st unit, third stone at the 3rd unit, and so on... The last stone at the 17th unit. Return true. The frog can jump to the last stone by jumping 1 unit to the 2nd stone, then 2 units to the 3rd stone, then 2 units to the 4th stone, then 3 units to the 6th stone, 4 units to the 7th stone, and 5 units to the 8th stone.
- Backtrack with memoizatino
One option is to use dp[n][n] to store start and step states
An optimization is to binary search tree times the next possible position as the stones array is ordered nextPosition = binarySearch(stones, start+1, stones.length, start+step(+1,-1,+0))
class Solution { boolean find = false; // stop recursion when find HashSet<String> set = new HashSet<>(); // only record the faild situation public boolean canCross(int[] stones) { jump(stones, 0, 0); return find; } public void jump(int[] stones, int start, int step) { if(start == stones.length-1) { find = true; return; } if(set.contains(start + "," + step)) return; int left = stones[start] + step-1, right = stones[start] + step+1; for(int i = start + 1; i < stones.length; i++) { if(stones[i] >= left && stones[i] <= right) { jump(stones, i, stones[i]-stones[start]); if(find) return; } else if(stones[i] > right) break; } set.add(start+","+step); } }
- DP O(N^2)
map< start, steps to jump to start >
public boolean canCross(int[] stones) { HashMap<Integer, HashSet<Integer>> map = new HashMap<>(); for(int i : stones) map.put(i, new HashSet<Integer>()); map.get(0).add(0); for(int i = 0; i < stones.length; i++) { for(int step : map.get(stones[i])) { for(int next = step-1; next <= step+1; next++) { if(next > 0 && map.containsKey(stones[i]+next)) map.get(stones[i]+next).add(next); } } } return map.get(stones[stones.length-1]).size() > 0; }
-
36. Valid Sudoku
Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: Each row must contain the digits 1-9 without repetition. Each column must contain the digits 1-9 without repetition. Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition. A partially filled sudoku which is valid. The Sudoku board could be partially filled, where empty cells are filled with the character '.'. Example 1: Input: [ ["5","3",".",".","7",".",".",".","."], ["6",".",".","1","9","5",".",".","."], [".","9","8",".",".",".",".","6","."], ["8",".",".",".","6",".",".",".","3"], ["4",".",".","8",".","3",".",".","1"], ["7",".",".",".","2",".",".",".","6"], [".","6",".",".",".",".","2","8","."], [".",".",".","4","1","9",".",".","5"], [".",".",".",".","8",".",".","7","9"] ] Output: true
HashSet[] cols = new HashSet[9], rows = new HashSet[9], subs = new HashSet[9]; public boolean isValidSudoku(char[][] board) { for(int i = 0; i < 9; i++) { cols[i] = new HashSet<Character>(); rows[i] = new HashSet<Character>(); subs[i] = new HashSet<Character>(); } for(int i = 0; i < board.length; i++) { for(int j = 0; j < board.length; j++) { int subKey = toKey(i, j); char v = board[i][j]; if(v == '.') continue; if(cols[j].contains(v) || rows[i].contains(v) || subs[subKey].contains(v)) return false; cols[j].add(v); rows[i].add(v); subs[subKey].add(v); } } return true; } public int toKey(int i, int j) { return i / 3 * 3 + j / 3; }
-
96. Unique Binary Search Trees
96. Unique Binary Search Trees
Given n, how many structurally unique BST's (binary search trees) that store values 1 ... n? Example: Input: 3 Output: 5 Explanation: Given n = 3, there are a total of 5 unique BST's: 1 3 3 2 1 \ / / / \ \ 3 2 1 1 3 2 / / \ \ 2 1 2 3
The key is the sequence N1,N2,N3,N4,…,NM has the same number of tree combination with the sequences 1,2,3,4,…,M. So we can break this problem down to smaller problems.
Also mathematically this problem is to calculate a Catalan number. C = C * 2 * (2 * i + 1) / (i + 2)
- Top Down
class Solution { public int numTrees(int n) { return build(n, new int[n+1]); } public int build(int n, int[] dp) { if(n <= 1) return 1; if(dp[n] > 0) return dp[n]; int result = 0; for(int i = 1; i <= n; i++) { result += build(i-1, dp) * build(n-i, dp); } dp[n] = result; return result; } }
- Bottom Up
public int numTrees(int n) { int[] dp = new int[n+1]; dp[0] = 1; dp[1] = 1; for(int i = 2; i <= n; i++) { for(int j = 1; j <= i; j++) { dp[i] += dp[j-1] * dp[i-j]; } } return dp[n]; }
-
87. Scramble String
Given a string s1, we may represent it as a binary tree by partitioning it to two non-empty substrings recursively. Below is one possible representation of s1 = "great": great / \ gr eat / \ / \ g r e at / \ a t To scramble the string, we may choose any non-leaf node and swap its two children. For example, if we choose the node "gr" and swap its two children, it produces a scrambled string "rgeat". rgeat / \ rg eat / \ / \ r g e at / \ a t We say that "rgeat" is a scrambled string of "great". Similarly, if we continue to swap the children of nodes "eat" and "at", it produces a scrambled string "rgtae". rgtae / \ rg tae / \ / \ r g ta e / \ t a We say that "rgtae" is a scrambled string of "great". Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1. Example 1: Input: s1 = "great", s2 = "rgeat" Output: true Example 2: Input: s1 = "abcde", s2 = "caebd" Output: false
Here is a iterative DP solution https://leetcode.com/problems/scramble-string/discuss/29396/Simple-iterative-DP-Java-solution-with-explanation
for some 1 <= q < k we have: F(i, j, k) = (F(i, j, q) AND F(i + q, j + q, k - q)) OR (F(i, j + k - q, q) AND F(i + q, j, k - q))
- Recursive
class Solution { public boolean isScramble(String s1, String s2) { if(s1.length() != s2.length()) return false; if(s1.equals(s2)) return true; if(s1.length() == 1) return false; int[] cnt = new int[26]; for(char c : s1.toCharArray()) cnt[c-'a']++; for(char c : s2.toCharArray()) cnt[c-'a']--; for(int i : cnt) if(i != 0) return false; for(int i = 1; i < s1.length(); i++) { String s11 = s1.substring(0, i), s12 = s1.substring(i); String s21 = s2.substring(0, i), s22 = s2.substring(i); boolean r1 = isScramble(s11, s21) && isScramble(s12, s22); if(r1) return true; s21 = s2.substring(0, s1.length()-i); s22 = s2.substring(s1.length()-i); boolean r2 = isScramble(s11, s22) && isScramble(s12, s21); if(r2) return true; } return false; } }
-
309. Best Time to Buy and Sell Stock with Cooldown
309. Best Time to Buy and Sell Stock with Cooldown
Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times) with the following restrictions: You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) Example: Input: [1,2,3,0,2] Output: 3 Explanation: transactions = [buy, sell, cooldown, buy, sell]
- DP
buy[i] : Maximum profit which end with buying on day i or end with buying on a day before i and takes rest until the day i since then.
sell[i] : Maximum profit which end with selling on day i or end with selling on a day before i and takes rest until the day i since then.
public int maxProfit(int[] p) { if(p.length == 0) return 0; int[] sell = new int[p.length], buy = new int[p.length]; buy[0] = -p[0]; for(int i = 1; i < p.length; i++) { sell[i] = Math.max(sell[i-1], buy[i-1]+p[i]); buy[i] = Math.max(buy[i-1], (i-2 >= 0 ? sell[i-2] : 0)-p[i]); } return sell[sell.length-1]; }
- Space Optimized
public int maxProfit(int[] prices) { if(prices.length == 0) return 0; int s = 0, s1 = 0, s2 = 0, b = 0, b1 = -prices[0]; for(int p : prices) { s = Math.max(s1, b1 + p); b = Math.max(b1, s2 - p); s2 = s1; s1 = s; b1 = b; } return s; }
-
1002. Find Common Characters
Given an array A of strings made only from lowercase letters, return a list of all characters that show up in all strings within the list (including duplicates). For example, if a character occurs 3 times in all strings but not 4 times, you need to include that character three times in the final answer. You may return the answer in any order. Example 1: Input: ["bella","label","roller"] Output: ["e","l","l"] Example 2: Input: ["cool","lock","cook"] Output: ["c","o"]
- Easy
int[26] is a good friend of all lower case letters
public List<String> commonChars(String[] A) { ArrayList<String> result = new ArrayList<>(); int[] cnt = new int[26], temp = new int[26]; Arrays.fill(cnt, Integer.MAX_VALUE); for(int i = 0; i < A.length; i++) { for(char c : A[i].toCharArray()) temp[c-'a']++; for(int j = 0; j < 26; j++) cnt[j] = Math.min(cnt[j], temp[j]); Arrays.fill(temp, 0); } for(int i = 0; i < 26; i++) for(int j = 0; j < cnt[i]; j++) result.add("" + (char)(i+'a')); return result; }
-
165. Compare Version Numbers
Compare two version numbers version1 and version2. If version1 > version2 return 1; if version1 < version2 return -1;otherwise return 0. You may assume that the version strings are non-empty and contain only digits and the . character. The . character does not represent a decimal point and is used to separate number sequences. For instance, 2.5 is not "two and a half" or "half way to version three", it is the fifth second-level revision of the second first-level revision. You may assume the default revision number for each level of a version number to be 0. For example, version number 3.4 has a revision number of 3 and 4 for its first and second level revision number. Its third and fourth level revision number are both 0. Example 1: Input: version1 = "0.1", version2 = "1.1" Output: -1 Example 2: Input: version1 = "1.0.1", version2 = "1" Output: 1
- easy
public int compareVersion(String version1, String version2) { String[] arr1 = version1.split("\\."), arr2 = version2.split("\\."); int i = 0, j = 0; while(i < arr1.length || j < arr2.length) { int a = i < arr1.length ? Integer.valueOf(arr1[i]) : 0, b = j < arr2.length ? Integer.valueOf(arr2[j]) : 0; if(a < b) return -1; else if(a > b) return 1; i++; j++; } return 0; }
-
440. K-th Smallest in Lexicographical Order
440. K-th Smallest in Lexicographical Order
Given integers n and k, find the lexicographically k-th smallest integer in the range from 1 to n. Note: 1 ≤ k ≤ n ≤ 109. Example: Input: n: 13 k: 2 Output: 10 Explanation: The lexicographical order is [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9], so the second smallest number is 10.
- My brutal force solution fail to pass. TLE 40/69
The solution set is a denary tree
The solution below is damn brilliant: Count the number of elements in a sub tree –> cnt.
if cnt > target: // the target is in current subtree dive into this tree by: num *= 10 else // the target is in the neighbors subtrees move to the next subtree by: num++
Note elements on the same level are consecutive increasing!!
https://leetcode.com/problems/k-th-smallest-in-lexicographical-order/discuss/92242/ConciseEasy-to-understand-Java-5ms-solution-with-Explaination
public int findKthNumber(int n, int k) { int curr = 1; k = k - 1; while (k > 0) { int steps = calSteps(n, curr, curr + 1); if (steps <= k) { curr += 1; k -= steps; } else { curr *= 10; k -= 1; } } return curr; } //use long in case of overflow public int calSteps(int n, long n1, long n2) { int steps = 0; while (n1 <= n) { steps += Math.min(n + 1, n2) - n1; n1 *= 10; n2 *= 10; } return steps; }
-
160. Intersection of Two Linked Lists
160. Intersection of Two Linked Lists
Write a program to find the node at which the intersection of two singly linked lists begins. For example, the following two linked lists: begin to intersect at node c1. Example 1: Input: intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 Output: Reference of the node with value = 8 Input Explanation: The intersected node's value is 8 (note that this must not be 0 if the two lists intersect). From the head of A, it reads as [4,1,8,4,5]. From the head of B, it reads as [5,0,1,8,4,5]. There are 2 nodes before the intersected node in A; There are 3 nodes before the intersected node in B.
- Easy
Two pointers is a good friends of LinkedList
public ListNode getIntersectionNode(ListNode a, ListNode b) { if(a == null || b == null) return null; ListNode result = null; int l1 = 0, l2 = 0; ListNode aa = a, bb = b, pa = null, pb = null; while(aa != null) { pa = aa; aa = aa.next; l1++; } while(bb != null) { pb = bb; bb = bb.next; l2++; } if(pa != pb) return null; ListNode lo = l1 > l2 ? a : b, sh = lo == a ? b : a; int offset = Math.abs(l1-l2); for(int i = 0; i < offset; i++) { lo = lo.next; } while(lo != null) { if(lo == sh) return lo; lo = lo.next; sh = sh.next; } return null; }
-
739. Daily Temperatures
Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead. For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0]. Note: The length of temperatures will be in the range [1, 30000]. Each temperature will be an integer in the range [30, 100].
- Simple problem
// find the first larger element with larger index // monotonic stack public int[] dailyTemperaturesMonotonicStack(int[] T) { Stack<Integer> stack = new Stack<>(); // stack.push(Integer.MAX_VALUE); int[] result = new int[T.length]; for(int i = 0; i < T.length; i++) { while(!stack.isEmpty() && T[stack.peek()] < T[i]) { int prev = stack.pop(); result[prev] = i - prev; } stack.push(i); } return result; }
-
983. Minimum Cost For Tickets
In a country popular for train travel, you have planned some train travelling one year in advance. The days of the year that you will travel is given as an array days. Each day is an integer from 1 to 365. Train tickets are sold in 3 different ways: a 1-day pass is sold for costs[0] dollars; a 7-day pass is sold for costs[1] dollars; a 30-day pass is sold for costs[2] dollars. The passes allow that many days of consecutive travel. For example, if we get a 7-day pass on day 2, then we can travel for 7 days: day 2, 3, 4, 5, 6, 7, and 8. Return the minimum number of dollars you need to travel every day in the given list of days. Example 1: Input: days = [1,4,6,7,8,20], costs = [2,7,15] Output: 11 Explanation: For example, here is one way to buy passes that lets you travel your travel plan: On day 1, you bought a 1-day pass for costs[0] = $2, which covered day 1. On day 3, you bought a 7-day pass for costs[1] = $7, which covered days 3, 4, ..., 9. On day 20, you bought a 1-day pass for costs[0] = $2, which covered day 20. In total you spent $11 and covered all the days of your travel. Example 2: Input: days = [1,2,3,4,5,6,7,8,9,10,30,31], costs = [2,7,15] Output: 17 Explanation: For example, here is one way to buy passes that lets you travel your travel plan: On day 1, you bought a 30-day pass for costs[2] = $15 which covered days 1, 2, ..., 30. On day 31, you bought a 1-day pass for costs[0] = $2 which covered day 31. In total you spent $17 and covered all the days of your travel.
- DP dp(i) : total cast from today to the end of this year
if this is a travel day dp(i)=min(dp(j1)+costs[0],dp(j7)+costs[1],dp(j30)+costs[2]) else dp(i) = dp(i+1)
public int mincostTickets(int[] days, int[] costs) { int[] dp = new int[366]; HashSet<Integer> set = new HashSet<>(); for(int i : days) set.add(i); for(int i = 365; i >= 1; i--) { if(set.contains(i)) { dp[i] = Math.min(costs[0] + (i+1 > 365 ? 0 : dp[i+1]), costs[1] + (i+7 > 365 ? 0 : dp[i+7])); dp[i] = Math.min(dp[i], costs[2] + (i+30 > 365 ? 0 : dp[i+30])); } else { dp[i] = i == 365 ? 0 : dp[i+1]; } } return dp[1]; }
- Another version depends on days array
dp(i)=min(dp(j1)+costs[0],dp(j7)+costs[1],dp(j30)+costs[2])
public int mincostTickets(int[] days, int[] costs) { int[] dp = new int[days.length], times = {1, 7, 30}; Arrays.fill(dp, Integer.MAX_VALUE); for(int i = days.length-1; i >=0 ; i--) { int j = i; for(int k = 0; k < 3; k++) { while(j < days.length && days[j] < days[i] + times[k]) j++; dp[i] = Math.min(dp[i], (j >= days.length ? 0 : dp[j]) + costs[k]); } } return dp[0]; }
-
498. Diagonal Traverse
Given a matrix of M x N elements (M rows, N columns), return all elements of the matrix in diagonal order as shown in the below image. Example: Input: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] Output: [1,2,4,7,5,3,6,8,9] Explanation:
Note: The total number of elements of the given matrix will not exceed 10,000.
- Linear function
public int[] findDiagonalOrder(int[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return new int[0]; int n = matrix.length, m = matrix[0].length; int[] result = new int[n*m]; int index = 0; boolean up = true; for(int k = 0; k < m+n-1; k++) { if(up) { int startJ = k - n + 1 < 0 ? 0 : k - n + 1; for(int j = startJ; j < m; j++) { int i = k-j; if(i < 0 || i >= n) break; result[index++] = matrix[i][j]; } } else { int startJ = k <= m-1 ? k : m-1; for(int j = startJ; j >= 0; j--) { int i = k-j; if(i < 0 || i >= n) continue; result[index++] = matrix[i][j]; } } up ^= true; } return result; }
-
977. Squares of a Sorted Array
977. Squares of a Sorted Array
Given an array of integers A sorted in non-decreasing order, return an array of the squares of each number, also in sorted non-decreasing order. Example 1: Input: [-4,-1,0,3,10] Output: [0,1,9,16,100] Example 2: Input: [-7,-3,2,3,11] Output: [4,9,9,49,121] Note: 1 <= A.length <= 10000 -10000 <= A[i] <= 10000 A is sorted in non-decreasing order.
- TwoPointers
public int[] sortedSquares(int[] A) { int[] result = new int[A.length]; int k = A.length-1; int l = 0, r = A.length-1; while(l <= r) { int left = Math.abs(A[l]); int right = Math.abs(A[r]); if(left > right) { result[k] = left * left; l++; } else { result[k] = right * right; r--; } k--; } return result; }
- The first time I just start search from around 0 … why have i done that ??? ```java
public int[] sortedSquares(int[] A) { if(A.length == 1) return new int[] {A[0]*A[0]}; int l = 0, r = A.length-1; while(l + 1 < r) { int mid = l + (r-l) / 2; if(A[mid] > 0) r = mid; else l = mid; } int[] result = new int[A.length]; int k = 0; while(l >= 0 || r < A.length) { int left = l >= 0 ? Math.abs(A[l]) : Integer.MAX_VALUE; int right = r < A.length ? Math.abs(A[r]) : Integer.MAX_VALUE; if(left < right) { result[k] = left * left; l–; } else { result[k] = right * right; r++; } k++; } return result; } ```
-
957. Prison Cells After N Days
957. Prison Cells After N Days
There are 8 prison cells in a row, and each cell is either occupied or vacant. Each day, whether the cell is occupied or vacant changes according to the following rules: If a cell has two adjacent neighbors that are both occupied or both vacant, then the cell becomes occupied. Otherwise, it becomes vacant. (Note that because the prison is a row, the first and the last cells in the row can't have two adjacent neighbors.) We describe the current state of the prison in the following way: cells[i] == 1 if the i-th cell is occupied, else cells[i] == 0. Given the initial state of the prison, return the state of the prison after N days (and N such changes described above.) Example 1: Input: cells = [0,1,0,1,1,0,0,1], N = 7 Output: [0,0,1,1,0,0,0,0] Explanation: The following table summarizes the state of the prison on each day: Day 0: [0, 1, 0, 1, 1, 0, 0, 1] Day 1: [0, 1, 1, 0, 0, 0, 0, 0] Day 2: [0, 0, 0, 0, 1, 1, 1, 0] Day 3: [0, 1, 1, 0, 0, 1, 0, 0] Day 4: [0, 0, 0, 0, 0, 1, 0, 0] Day 5: [0, 1, 1, 1, 0, 1, 0, 0] Day 6: [0, 0, 1, 0, 1, 1, 0, 0] Day 7: [0, 0, 1, 1, 0, 0, 0, 0] Example 2: Input: cells = [1,0,0,1,0,0,1,0], N = 1000000000 Output: [0,0,1,1,1,1,1,0] Note: cells.length == 8 cells[i] is in {0, 1} 1 <= N <= 10^9
- bit manipulation
public int[] prisonAfterNDays(int[] cells, int N) { HashMap<Integer, Integer> map = new HashMap<>(); ArrayList<Integer> arr = new ArrayList<>(); int num = 0; for(int i = 0; i <= 7; i++) num ^= (cells[7-i] << i); for(int k = 0; k < N; k++) { int next = 0; for(int j = 1; j <= 6; j++) { int l = (num >> (j+1)) & 1, r = (num >> (j-1)) & 1; if((l ^ r) == 0) next |= (1 << j); } num = next; if(map.containsKey(num)) break; map.put(num, 0); arr.add(num); } System.out.println(arr.size()); if(arr.size() < N) num = arr.get((N-1) % arr.size()); int[] result = new int[8]; for(int i = 7; i >= 0; i--) { if(((num >> i) & 1) == 1) result[7-i] = 1; } return result; }
public int[] prisonAfterNDays(int[] cells, int N) { HashMap<String, Integer> map = new HashMap<>(); ArrayList<String> arr = new ArrayList<>(); String key = null; for(int k = 0; k < N; k++) { int[] buffer = new int[cells.length]; for(int i = 1; i < cells.length-1; i++) { int l = cells[i-1], r = cells[i+1]; if(l == 0 && r == 0 || l == 1 && r == 1) buffer[i] = 1; } cells = buffer; key = toKey(cells); if(map.containsKey(key)) break; map.put(key, k); arr.add(key); } if(map.get(key) == arr.size()-1) return cells; int len = arr.size() - map.get(key); // why it is always 0??? why it is a perfect circle? // I can't prove but it passes all the cases // or to calculate the pre len before the circle int index = (N-1) % len; for(int i = 0; i < cells.length; i++) cells[i] = arr.get(index).charAt(i) - '0'; return cells; }
-
94. Binary Tree Inorder Traversal
94. Binary Tree Inorder Traversal
Given a binary tree, return the inorder traversal of its nodes' values. Example: Input: [1,null,2,3] 1 \ 2 / 3 Output: [1,3,2] Follow up: Recursive solution is trivial, could you do it iteratively?
public List<Integer> inorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); TreeNode curr = root; ArrayList<Integer> result = new ArrayList<>(); while(curr != null || !stack.isEmpty()) { while(curr != null) { stack.push(curr); curr = curr.left; } curr = stack.pop(); result.add(curr.val); curr = curr.right; } return result; }
-
721. Accounts Merge
Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account. Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some email that is common to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name. After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order. Example 1: Input: accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]] Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]] Explanation: The first and third John's are the same person as they have the common email "johnsmith@mail.com". The second John and Mary are different people as none of their email addresses are used by other accounts. We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], ['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted. Note: The length of accounts will be in the range [1, 1000]. The length of accounts[i] will be in the range [1, 10]. The length of accounts[i][j] will be in the range [1, 30].
- BFS build a graph
public List<List<String>> accountsMerge(List<List<String>> account) { HashMap<String, HashSet<Integer>> map = new HashMap<>(); boolean[] visited = new boolean[account.size()]; for(int i = 0; i < account.size(); i++) { List<String> l = account.get(i); String name = l.get(0); for(int j = 1; j < l.size(); j++) { String email = l.get(j); HashSet<Integer> set = map.getOrDefault(email, new HashSet<>()); set.add(i); map.put(email, set); } } List<List<String>> result = new ArrayList<>(); for(int i = 0; i < account.size(); i++) { if(visited[i]) continue; visited[i] = true; HashSet<String> temp = new HashSet<>(); ArrayDeque<Integer> q = new ArrayDeque<>(); q.offer(i); while(!q.isEmpty()) { int j = q.poll(); for(int k = 1; k < account.get(j).size(); k++) { String email = account.get(j).get(k); temp.add(email); for(int n : map.get(email)) { if(!visited[n]) { visited[n] = true; q.offer(n); } } } } LinkedList<String> sub = new LinkedList<>(temp); Collections.sort(sub); sub.addFirst(account.get(i).get(0)); result.add(sub); } return result; }
-
636. Exclusive Time of Functions
636. Exclusive Time of Functions
On a single threaded CPU, we execute some functions. Each function has a unique id between 0 and N-1. We store logs in timestamp order that describe when a function is entered or exited. Each log is a string with this format: "{function_id}:{"start" | "end"}:{timestamp}". For example, "0:start:3" means the function with id 0 started at the beginning of timestamp 3. "1:end:2" means the function with id 1 ended at the end of timestamp 2. A function's exclusive time is the number of units of time spent in this function. Note that this does not include any recursive calls to child functions. The CPU is single threaded which means that only one function is being executed at a given time unit. Return the exclusive time of each function, sorted by their function id. Example 1:
Input: n = 2 logs = ["0:start:0","1:start:2","1:end:5","0:end:6"] Output: [3, 4] Explanation: Function 0 starts at the beginning of time 0, then it executes 2 units of time and reaches the end of time 1. Now function 1 starts at the beginning of time 2, executes 4 units of time and ends at time 5. Function 0 is running again at the beginning of time 6, and also ends at the end of time 6, thus executing for 1 unit of time. So function 0 spends 2 + 1 = 3 units of total time executing, and function 1 spends 4 units of total time executing.
- Optimzed Solution, use only one stack to record starting indexs
public int[] exclusiveTime(int n, List<String> logs) { ArrayDeque<Integer> starts = new ArrayDeque<>(); int[] result = new int[n]; int prev = 0; for(String s : logs) { String[] arr = s.split(":"); int index = Integer.valueOf(arr[0]), time = Integer.valueOf(arr[2]); if(arr[1].equals("start")) { // start if(!starts.isEmpty()) result[starts.peek()] += time - prev; // prev -> next_start (exclusive) starts.push(index); prev = time; } else { int start = starts.poll(); result[start] += time - prev + 1; // prev -> end (inclusive) prev = time+1; } } return result; }
- The first complex solution … spend 50 minutes…
public int[] exclusiveTime(int n, List<String> logs) { ArrayDeque<int[]> starts = new ArrayDeque<>(), ends = new ArrayDeque<>(); int[] result = new int[n]; for(String s : logs) { String[] arr = s.split(":"); int index = Integer.valueOf(arr[0]), time = Integer.valueOf(arr[2]), status = arr[1].equals("start") ? 0 : 1; int[] log = new int[] {index, status, time}; if(log[1] == 0) { // start if(!starts.isEmpty()) { int[] ongoing = starts.peek(); int[] latest = (ends.isEmpty() || ongoing[2] > ends.peek()[2]) ? ongoing : ends.peek(); result[ongoing[0]] += log[2] - latest[2] - (latest == ongoing ? 0 : 1); } starts.push(log); } else { int[] ongoing = starts.poll(); int[] latest = (ends.isEmpty() || ongoing[2] > ends.peek()[2]) ? ongoing : ends.peek(); result[log[0]] += log[2] - latest[2] + (latest == ongoing ? 1 : 0); ends.push(log); } } return result; }
-
19. Remove Nth Node From End of List
19. Remove Nth Node From End of List
Given a linked list, remove the n-th node from the end of list and return its head. Example: Given linked list: 1->2->3->4->5, and n = 2. After removing the second node from the end, the linked list becomes 1->2->3->5. Note: Given n will always be valid. Follow up: Could you do this in one pass?
- TowPointers
public ListNode removeNthFromEnd(ListNode head, int n) { ListNode fast = head, slow = null; for(int i = 0; i <= n; i++) { if(fast == null) return head.next; fast = fast.next; } slow = head; while(fast != null) { fast = fast.next; slow = slow.next; } slow.next = slow.next.next; return head; }
- Recursive ```java
public ListNode removeNthFromEndRecursive(ListNode head, int n) { ListNode sentinel = new ListNode(0); sentinel.next = head; dfs(sentinel, n); return sentinel.next; }
public Result dfs(ListNode head, int target) { if(head == null) return new Result(null, 0); Result result = dfs(head.next, target); if(result.cnt == target) { head.next = result.n.next; result.n.next = null; } result.n = head; result.cnt++; return result; }
class Result { ListNode n; int cnt; public Result(ListNode n, int cnt) { this.n = n; this.cnt = cnt; } } ```
-
557. Reverse Words in a String III
557. Reverse Words in a String III
Given a string, you need to reverse the order of characters in each word within a sentence while still preserving whitespace and initial word order. Example 1: Input: "Let's take LeetCode contest" Output: "s'teL ekat edoCteeL tsetnoc" Note: In the string, each word is separated by single space and there will not be any extra space in the string.
public String reverseWords(String s) { StringBuilder sb = new StringBuilder(s); int i = 0, j = 0; while(i < s.length()) { while(i < s.length() && s.charAt(i) == ' ') i++; j = i; while(j < s.length() && s.charAt(j) != ' ') j++; reverse(sb, i, j-1); i = j; } return sb.toString(); } public void reverse(StringBuilder sb, int i, int j) { while(i < j) { char c = sb.charAt(i); sb.setCharAt(i, sb.charAt(j)); sb.setCharAt(j,c); i++; j--; } }
-
304. Range Sum Query 2D - Immutable
304. Range Sum Query 2D - Immutable
Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). Range Sum Query 2D The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8. Example: Given matrix = [ [3, 0, 1, 4, 2], [5, 6, 3, 2, 1], [1, 2, 0, 1, 5], [4, 1, 0, 1, 7], [1, 0, 3, 0, 5] ] sumRegion(2, 1, 4, 3) -> 8 sumRegion(1, 1, 2, 2) -> 11 sumRegion(1, 2, 2, 4) -> 12 Note: You may assume that the matrix does not change. There are many calls to sumRegion function. You may assume that row1 ≤ row2 and col1 ≤ col2.
- cache sums from (0,0) to (i,j)
class NumMatrix { int[][] sums; public NumMatrix(int[][] matrix) { if(!(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0)) { sums = new int[matrix.length][matrix[0].length]; for(int i = 0 ; i< sums.length; i++) { sums[i][0] = matrix[i][0]; for(int j = 1; j < matrix[0].length; j++) { sums[i][j] = matrix[i][j] + sums[i][j-1]; } } for(int i = 1; i < sums.length; i++) { for(int j = 0; j < sums[0].length; j++) { sums[i][j] += sums[i-1][j]; } } } } public int sumRegion(int row1, int col1, int row2, int col2) { if(sums == null) return -1; int total = sums[row2][col2]; int sub1 = row1 == 0 ? 0 : sums[row1-1][col2]; int sub2 = col1 == 0 ? 0 : sums[row2][col1-1]; int sub3 = row1 * col1 == 0 ? 0 : sums[row1-1][col1-1]; return total - sub1 - sub2 + sub3; } }
- Calculate all sums TLE pass 11/12
class NumMatrix { int[][][][] dp; int[][] matrix; public NumMatrix(int[][] matrix) { if(!(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0)) { this.matrix = matrix; int n = matrix.length, m = matrix[0].length; dp = new int[n][m][n][m]; for(int area = 1; area <= m*n; area++) { for(int a = 1; a <= area; a++) { if(area % a != 0) continue; int b = area / a; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { int r = i + a - 1, c = j + b - 1; if(r >= n || c >= m) continue; if(r == i && c == j) dp[i][j][r][c] = matrix[i][j]; else if(a==1) { dp[i][j][r][c] = dp[i][j][r][c-1] + matrix[r][c]; } else if(b==1) { dp[i][j][r][c] = dp[i][j][r-1][c] + matrix[r][c]; } else { dp[i][j][r][c] = dp[i][j][r][c-1] + dp[i][c][r][c]; } } } } } } System.out.println("fin"); } public int sumRegion(int row1, int col1, int row2, int col2) { if(matrix == null) return -1; return dp[row1][col1][row2][col2]; }
-
69. Sqrt(x)
- Too simple.
Reminds me the dame problem Palindrome Pairs
class Trie { Node head; class Node { Node[] arr = new Node[26]; String w; } /** Initialize your data structure here. */ public Trie() { head = new Node(); } /** Inserts a word into the trie. */ public void insert(String word) { Node n = head; for(char c : word.toCharArray()) { if(n.arr[c-'a'] == null) n.arr[c-'a'] = new Node(); n = n.arr[c-'a']; } n.w = word; } /** Returns if the word is in the trie. */ public boolean search(String word) { Node n = head; for(char c : word.toCharArray()) { n = n.arr[c-'a']; if(n == null) return false; } return n.w != null; } /** Returns if there is any word in the trie that starts with the given prefix. */ public boolean startsWith(String prefix) { Node n = head; for(char c : prefix.toCharArray()) { n = n.arr[c-'a']; if(n == null) return false; } return true; } }
-
75. Sort Colors
Given an array with n objects colored red, white or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white and blue. Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. Note: You are not suppose to use the library's sort function for this problem. Example: Input: [2,0,2,1,1,0] Output: [0,0,1,1,2,2] Follow up: A rather straight forward solution is a two-pass algorithm using counting sort. First, iterate the array counting number of 0's, 1's, and 2's, then overwrite array with total number of 0's, then 1's and followed by 2's. Could you come up with a one-pass algorithm using only constant space?
public void sortColors(int[] nums) { int a = 0, b = 0, c = 0; for(int i = 0; i < nums.length; i++) { if(nums[i] == 0) { swap(nums, i, a+b); swap(nums, a+b, a); a++; } else if(nums[i] == 1) { swap(nums, i, a+b); b++; } else { c++; } } } public void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
-
30. Substring with Concatenation of All Words
30. Substring with Concatenation of All Words
You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters. Example 1: Input: s = "barfoothefoobarman", words = ["foo","bar"] Output: [0,9] Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively. The output order does not matter, returning [9,0] is fine too. Example 2: Input: s = "wordgoodgoodgoodbestword", words = ["word","good","best","word"] Output: []
- HashMap
public List<Integer> findSubstring(String s, String[] words) { if(s.length() == 0 || words.length == 0) return new ArrayList<>(); int wl = words[0].length(), len = words.length * wl; ArrayList<Integer> result = new ArrayList<>(); for(int i = 0; i < s.length() - len + 1; i++) { if(check(s.substring(i, i+len), words, wl)) result.add(i); } return result; } public boolean check(String s, String[] words, int wl) { HashMap<String, Integer> map = new HashMap<>(), dict = new HashMap<>(); for(String w : words) dict.put(w, dict.getOrDefault(w, 0)+1); int cnt = 0; for(int i = 0; i < words.length; i++) { String w = s.substring(i*wl, (i+1)*wl); if(dict.containsKey(w)) { map.put(w, map.getOrDefault(w,0)+1); if(map.get(w) > dict.get(w)) return false; } else return false; } return true; }
-
539. Minimum Time Difference
Given a list of 24-hour clock time points in "Hour:Minutes" format, find the minimum minutes difference between any two time points in the list. Example 1: Input: ["23:59","00:00"] Output: 1 Note: The number of time points in the given list is at least 2 and won't exceed 20000. The input time is legal and ranges from 00:00 to 23:59.
- Build an array to store times
public int findMinDifference(List<String> timePoints) { boolean[] seconds = new boolean[60 * 24]; for(String s : timePoints) { int index = Integer.valueOf(s.substring(0,2))*60 + Integer.valueOf(s.substring(3,5)); if(seconds[index]) return 0; seconds[index] = true; } int result = 60 * 24; int first = -1, prev = -1; for(int i = 0; i < seconds.length; i++) { if(!seconds[i]) continue; if(first == -1) first = i; if(prev != -1) result = Math.min(result, i-prev); prev = i; } result = Math.min(result, first + 60*24 - prev); return result; }
-
69. Sqrt(x)
Implement int sqrt(int x). Compute and return the square root of x, where x is guaranteed to be a non-negative integer. Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned. Example 1: Input: 4 Output: 2 Example 2: Input: 8 Output: 2 Explanation: The square root of 8 is 2.82842..., and since the decimal part is truncated, 2 is returned.
- Binary search
public int mySqrt(int n) { int l = 0, r = n; while(l + 1 < r) { int mid = l + (r-l) / 2; long square = (long)mid * mid; if(square == n) return mid; else if(square < n) l = mid; else r = mid; } return (long)r * r <= n ? r : l; }
- Optimized Brutal Force: Reduce search Space
public int mySqrt(int n) { long x = n; long y = x; while(x/2 * x/2 > y) { x /= 2; } for(long i = x / 2; i <= x; i++) { long j = i * i; if(j == y) return (int)i; else if(j > y) return (int)i-1; } return -1; }
-
415. Add Strings
Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and num2. Note: The length of both num1 and num2 is < 5100. Both num1 and num2 contains only digits 0-9. Both num1 and num2 does not contain any leading zero. You must not use any built-in BigInteger library or convert the inputs to integer directly.
- TwoPointers
public String addStrings(String num1, String num2) { int i = num1.length()-1, j = num2.length()-1, carry = 0; String result = ""; while(i >= 0 || j >= 0) { int sum = (i >= 0 ? (num1.charAt(i--)-'0') : 0) + (j >= 0 ? (num2.charAt(j--)-'0') : 0) + carry; result = (sum % 10) + result; carry = sum / 10; } return carry == 1 ? '1' + result : result; }
-
71. Simplify Path
Given an absolute path for a file (Unix-style), simplify it. Or in other words, convert it to the canonical path. In a UNIX-style file system, a period . refers to the current directory. Furthermore, a double period .. moves the directory up a level. For more information, see: Absolute path vs relative path in Linux/Unix Note that the returned canonical path must always begin with a slash /, and there must be only a single slash / between two directory names. The last directory name (if it exists) must not end with a trailing /. Also, the canonical path must be the shortest string representing the absolute path. Example 1: Input: "/home/" Output: "/home" Explanation: Note that there is no trailing slash after the last directory name. Example 2: Input: "/../" Output: "/" Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go. Example 3: Input: "/home//foo/" Output: "/home/foo" Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one. Example 4: Input: "/a/./b/../../c/" Output: "/c" Example 5: Input: "/a/../../b/../c//.//" Output: "/c" Example 6: Input: "/a//b////c/d//././/.." Output: "/a/b/c"
- Back to front (Or use stack to process the path from front to rear)
public String simplifyPath(String path) { String[] arr = path.split("/+"); System.out.println(Arrays.toString(arr)); int up = 0; StringBuilder result = new StringBuilder(); for(int i = arr.length-1; i >= 0; i--) { String s = arr[i]; if(s.length() == 0 || s.equals(".")) continue; else if(s.equals("..")) up++; else if(up > 0) { up--; continue; } else result.insert(0, '/').insert(0, s); } result.insert(0, '/'); if(result.length() > 1) result.setLength(result.length()-1); return result.toString(); }
-
785. Is Graph Bipartite?
Given an undirected graph, return true if and only if it is bipartite. Recall that a graph is bipartite if we can split it's set of nodes into two independent subsets A and B such that every edge in the graph has one node in A and another node in B. The graph is given in the following form: graph[i] is a list of indexes j for which the edge between nodes i and j exists. Each node is an integer between 0 and graph.length - 1. There are no self edges or parallel edges: graph[i] does not contain i, and it doesn't contain any element twice. Example 1: Input: [[1,3], [0,2], [1,3], [0,2]] Output: true Explanation: The graph looks like this: 0----1 | | | | 3----2 We can divide the vertices into two groups: {0, 2} and {1, 3}. Example 2: Input: [[1,2,3], [0,2], [0,1,3], [0,2]] Output: false Explanation: The graph looks like this: 0----1 | \ | | \ | 3----2 We cannot find a way to divide the set of nodes into two independent subsets.
- BFS
class Solution { public boolean isBipartite(int[][] graph) { boolean[] visited = new boolean[graph.length]; for(int i = 0; i < graph.length; i++) { if(visited[i]) continue; if(!checkSubGraph(graph, visited, i)) return false; } return true; } public boolean checkSubGraph(int[][] graph, boolean[] visited, int start) { HashSet[] sets = new HashSet[2]; sets[0] = new HashSet<Integer>(); sets[1] = new HashSet<Integer>(); ArrayDeque<Integer> q = new ArrayDeque<>(); q.offer(start); sets[0].add(start); boolean[][] used = new boolean[graph.length][graph.length]; int currSet = 0, nextSet = 1; while(!q.isEmpty()) { int size = q.size(); for(int k = 0; k < size; k++) { int i = q.poll(); visited[i] = true; for(int j : graph[i]) { if(!used[i][j]) { if(sets[currSet].contains(j)) return false; used[i][j] = true; used[j][i] = true; q.offer(j); sets[nextSet].add(j); } } } currSet = currSet == 1 ? 0 : 1; nextSet = currSet == 1 ? 0 : 1; } return true; } }
-
240. Search a 2D Matrix II
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted in ascending from left to right. Integers in each column are sorted in ascending from top to bottom. Example: Consider the following matrix: [ [1, 4, 7, 11, 15], [2, 5, 8, 12, 19], [3, 6, 9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30] ] Given target = 5, return true. Given target = 20, return false.
- Divide And Conquer
public boolean searchMatrix(int[][] matrix, int target) { if(matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return false; return divide(matrix, target, 0, matrix[0].length-1, 0, matrix.length-1); } public boolean divide(int[][] matrix, int target, int c1, int c2, int r1, int r2) { if(c2 < c1 || r2 < r1) return false; int c = c1 + (c2-c1) / 2, r = r1 + (r2-r1) / 2; if(matrix[r][c] == target) return true; else if(matrix[r][c] > target) { return divide(matrix, target, c1, c-1, r1, r-1) || divide(matrix, target, c, c2, r1, r-1) || divide(matrix, target, c1, c-1, r, r2); } else { return divide(matrix, target, c+1, c2, r+1, r2) || divide(matrix, target, c+1, c2, r1, r) || divide(matrix, target, c1, c, r+1, r2); } }
- TwoPointers
public boolean searchMatrix(int[][] matrix, int target) { if(matrix.length == 0 || matrix[0].length == 0) return false; int i = 0, j = matrix[0].length-1; while(i < matrix.length && j >= 0) { if(matrix[i][j] == target) return true; else if(matrix[i][j] < target) i++; else j--; } return false; }
-
74. Search a 2D Matrix
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: Integers in each row are sorted from left to right. The first integer of each row is greater than the last integer of the previous row. Example 1: Input: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 3 Output: true Example 2: Input: matrix = [ [1, 3, 5, 7], [10, 11, 16, 20], [23, 30, 34, 50] ] target = 13 Output: false
- Binary Search
public boolean searchMatrix(int[][] matrix, int target) { if(matrix.length == 0 || matrix[0].length == 0) return false; int n = matrix.length, m = matrix[0].length; int l = 0, r = n * m - 1; while(l <= r) { int mid = l + (r-l) / 2; int i = mid / m, j = mid - i * m; if(matrix[i][j] == target) return true; else if(matrix[i][j] < target) { l = mid + 1; else r = mid - 1; } return false; }
-
244. Shortest Word Distance II
244. Shortest Word Distance II
Design a class which receives a list of words in the constructor, and implements a method that takes two words word1 and word2 and return the shortest distance between these two words in the list. Your method will be called repeatedly many times with different parameters. Example: Assume that words = ["practice", "makes", "perfect", "coding", "makes"]. Input: word1 = “coding”, word2 = “practice” Output: 3 Input: word1 = "makes", word2 = "coding" Output: 1 Note: You may assume that word1 does not equal to word2, and word1 and word2 are both in the list.
class WordDistance { HashMap<String, ArrayList<Integer>> map = new HashMap<>(); public WordDistance(String[] words) { for(int i = 0; i < words.length; i++) { String s = words[i]; ArrayList<Integer> arr = map.getOrDefault(s, new ArrayList<Integer>()); arr.add(i); map.put(s, arr); } } public int shortest(String word1, String word2) { int x = 0, y = 0, result = Integer.MAX_VALUE; ArrayList<Integer> a = map.get(word1), b = map.get(word2); while(x < a.size() && y < b.size()) { result = Math.min(result, Math.abs(a.get(x)-b.get(y))); if(a.get(x) < b.get(y)) x++; else y++; } return result; } }
-
448. Find All Numbers Disappeared in an Array
448. Find All Numbers Disappeared in an Array
Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements of [1, n] inclusive that do not appear in this array. Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. Example: Input: [4,3,2,7,8,2,3,1] Output: [5,6]
- negative as duplicate
public List<Integer> findDisappearedNumbers(int[] nums) { for(int i = 0; i < nums.length; i++) { int j = Math.abs(nums[i])-1; if(nums[j] > 0) nums[j] = -nums[j]; } ArrayList<Integer> result = new ArrayList<>(); for(int i = 0; i < nums.length; i++) { if(nums[i] > 0) result.add(i+1); } return result; }
-
442. Find All Duplicates in an Array
442. Find All Duplicates in an Array
Given an array of integers, 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. Find all the elements that appear twice in this array. Could you do it without extra space and in O(n) runtime? Example: Input: [4,3,2,7,8,2,3,1] Output: [2,3]
- negative as duplicate
I should have easily solved this problem, but … T_T
public List<Integer> findDuplicates(int[] nums) { ArrayList<Integer> result = new ArrayList<>(); for(int i = 0; i < nums.length; i++) { int index = Math.abs(nums[i])-1; if(nums[index] < 0) result.add(index+1); else nums[index] = -nums[index]; } return result; }
-
763. Partition Labels
A string S of lowercase letters is given. We want to partition this string into as many parts as possible so that each letter appears in at most one part, and return a list of integers representing the size of these parts. Example 1: Input: S = "ababcbacadefegdehijhklij" Output: [9,7,8] Explanation: The partition is "ababcbaca", "defegde", "hijhklij". This is a partition so that each letter appears in at most one part. A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. Note: S will have length in range [1, 500]. S will consist of lowercase letters ('a' to 'z') only.
- Greedy
public List<Integer> partitionLabels(String s) { HashMap<Character, Integer> map = new HashMap<>(); for(int i = s.length()-1; i >= 0; i--) { if(!map.containsKey(s.charAt(i))) { map.put(s.charAt(i), i); } } ArrayList<Integer> result = new ArrayList<>(); int i = 0, j = 0; while(j < s.length()) { for(int k = i; k <= j; k++) { j = Math.max(j, map.get(s.charAt(k))); } result.add(j-i+1); i = j + 1; j = i; } return result; }
-
438. Find All Anagrams in a String
438. Find All Anagrams in a String
Given a string s and a non-empty string p, find all the start indices of p's anagrams in s. Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100. The order of output does not matter. Example 1: Input: s: "cbaebabacd" p: "abc" Output: [0, 6] Explanation: The substring with start index = 0 is "cba", which is an anagram of "abc". The substring with start index = 6 is "bac", which is an anagram of "abc". Example 2: Input: s: "abab" p: "ab" Output: [0, 1, 2] Explanation: The substring with start index = 0 is "ab", which is an anagram of "ab". The substring with start index = 1 is "ba", which is an anagram of "ab". The substring with start index = 2 is "ab", which is an anagram of "ab".
- HashMap + sliding window
public List<Integer> findAnagrams(String s, String p) { ArrayList<Integer> result = new ArrayList<>(); if(s.length() < p.length()) return result; HashMap<Character, Integer> map = new HashMap<>(), dict = new HashMap<>(); for(char c : p.toCharArray()) dict.put(c, dict.getOrDefault(c, 0)+1); int target = dict.size(); int cnt = 0, len = 0; for(int i = 0; i < p.length(); i++) { char c = s.charAt(i); if(!dict.containsKey(c)) continue; map.put(c, map.getOrDefault(c, 0)+1); if(map.get(c).equals(dict.get(c))) cnt++; } if(cnt == target) result.add(0); for(int i = p.length(); i < s.length(); i++) { char c = s.charAt(i), prev = s.charAt(i-p.length()); if(dict.containsKey(prev)) { int v = map.get(prev); if(v == dict.get(prev)) cnt--; if(v == 1) map.remove(prev); else map.put(prev, v-1); } if(dict.containsKey(c)) { map.put(c, map.getOrDefault(c, 0)+1); if(map.get(c).equals(dict.get(c))) cnt++; if(cnt == target) result.add(i-p.length()+1); } } return result; }
- This is a bad idea. ex: 111010 111001 101010 101001
public List<Integer> findAnagrams(String s, String p) { ArrayList<Integer> result = new ArrayList<>(); int target = 0; for(char c : p.toCharArray()) { target ^= c; } int cnt = 0; int n = 0; for(int i = 0; i < s.length(); i++) { n ^= s.charAt(i); if(++cnt >= p.length()) { if(n == target) { System.out.println(i); if(target > 0 || target == 0 && s.substring(i-p.length()+1, i+1).equals(p)) result.add(i-p.length()+1); } n ^= s.charAt(i-p.length()+1); } } return result; }
-
148. Sort List
Sort a linked list in O(n log n) time using constant space complexity. Example 1: Input: 4->2->1->3 Output: 1->2->3->4 Example 2: Input: -1->5->3->4->0 Output: -1->0->3->4->5
- Iterative MergeSort Strict O(1) space
public ListNode sortList(ListNode head) { ListNode n = head; int cnt = 0; while(n != null) { n = n.next; cnt++; } ListNode sentinel = new ListNode(0), sent = null, prev = null, h1 = null, h2 = null, curr = null; sentinel.next = head; for(int len = 1; len < cnt; len *= 2) { sent = sentinel; h1 = sent.next; h2 = h1; while(true) { for(int k = 0; k < len && h2 != null; k++) { prev = h2; h2 = h2.next; } if(h2 == null) { sent.next = h1; break; } prev.next = null; ListNode copyH2 = h2; for(int k = 0; k < len-1 && copyH2 != null; k++) copyH2 = copyH2.next; ListNode nextH1 = null; if(copyH2 != null) { nextH1 = copyH2.next; copyH2.next = null; } if(h1.val < h2.val) { curr = h1; h1 = h1.next; } else { curr = h2; h2 = h2.next; } sent.next = curr; while(h1 != null && h2 != null) { if(h1.val < h2.val) { curr.next = h1; h1 = h1.next; } else { curr.next = h2; h2 = h2.next; } curr = curr.next; } ListNode h = h1 != null ? h1 : h2; while(h != null) { curr.next = h; h = h.next; curr = curr.next; } sent = curr; h1 = nextH1; h2 = h1; } } return sentinel.next; }
- Recursive MergeSort ```java
public ListNode sortList(ListNode head) { if(head == null || head.next == null) return head; return mergeSort(head); }
public ListNode mergeSort(ListNode head) { if(head == null || head.next == null) return head; ListNode fast = head, slow = head, prev = head; while(fast != null) { fast = fast.next; if(fast != null) { fast = fast.next; prev = slow; slow = slow.next; } } prev.next = null; ListNode h1 = mergeSort(head); ListNode h2 = mergeSort(slow);
ListNode curr; if(h1.val < h2.val) { curr = h1; h1 = h1.next; } else { curr = h2; h2 = h2.next; } ListNode newHead = curr; while(h1 != null && h2 != null) { if(h1.val < h2.val) { curr.next = h1; h1 = h1.next; } else { curr.next = h2; h2 = h2.next; } curr = curr.next; } ListNode h = h1 != null ? h1 : h2; while(h != null) { curr.next = h; h = h.next; curr = curr.next; } return newHead; } ```
-
29. Divide Two Integers
Given two integers dividend and divisor, divide two integers without using multiplication, division and mod operator. Return the quotient after dividing dividend by divisor. The integer division should truncate toward zero. Example 1: Input: dividend = 10, divisor = 3 Output: 3 Example 2: Input: dividend = 7, divisor = -3 Output: -2 Note: Both dividend and divisor will be 32-bit signed integers. The divisor will never be 0. Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For the purpose of this problem, assume that your function returns 231 − 1 when the division result overflows.
- Bit
Subtract from the most significant bits and accumulates the quotient
public int divide(int dividend, int divisor) { boolean neg = (dividend ^ divisor) < 0; long x = dividend > 0 ? dividend : -(long)dividend, y = divisor > 0 ? divisor : -(long)divisor; long quotient = 0; while(x >= y) { long move = 1, temp = y; while((temp << 1) <= x) { move = move << 1; temp = temp << 1; } x -= temp; quotient += move; } quotient = !neg ? quotient : ~(quotient-1); if(quotient >= Integer.MAX_VALUE) return Integer.MAX_VALUE; if(quotient <= Integer.MIN_VALUE) return Integer.MIN_VALUE; return (int)quotient; }
Add and Multiply subtract is to negate ~(ADD(a, -1))
public int add(int a, int b) { if(a == 0) return b; int aa = (a & b) << 1; int bb = a ^ b; return add(aa, bb); } public int multiply(int a, int b) { if(a < 0) { a = -a; b = -b; } if(a == 1) return b; int aa = a >> 1; int bb = b << 1; int m = multiply(aa, bb); return (a & 1) == 1 ? add(m, b) : m; }
-
59. Spiral Matrix II
Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. Example: Input: 3 Output: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
public List<Integer> spiralOrder(int[][] m) { if(m.length == 0 || m[0].length == 0) return new ArrayList<>(); int h = m.length, k = m[0].length; List<Integer> result = new ArrayList<>(); for(int i = 0; i < h/2 && i < k/2; i++) { for(int col = i; col < k - i - 1; col++) result.add(m[i][col]); for(int row = i; row < h - i - 1; row++) result.add(m[row][k-i-1]); for(int col = k-i-1; col > i; col--) result.add(m[h-i-1][col]); for(int row = h-i-1; row > i; row--) result.add(m[row][i]); } if(k >= h && h % 2 == 1) for(int col = h / 2; col <= k - h/2 - 1; col++) result.add(m[h/2][col]); if(k < h && k % 2 == 1) for(int row = k/2; row <= h - k/2 - 1; row++) result.add(m[row][k/2]); return result; }
-
315. Count of Smaller Numbers After Self
315. Count of Smaller Numbers After Self
You are given an integer array nums and you have to return a new counts array. The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i]. Example: Input: [5,2,6,1] Output: [2,1,1,0] Explanation: To the right of 5 there are 2 smaller elements (2 and 1). To the right of 2 there is only 1 smaller element (1). To the right of 6 there is 1 smaller element (1). To the right of 1 there is 0 smaller element.
- Brutal Force
public List<Integer> countSmaller(int[] nums) { int[] result = new int[nums.length]; for(int i = nums.length-2; i >= 0; i--) { for(int j = i+1; j < nums.length; j++) if(nums[j] < nums[i]) result[i]++; } ArrayList<Integer> arr = new ArrayList<>(); for(int i : result) arr.add(i); return arr; }
- MergeSort
class Solution { int[] result; public List<Integer> countSmaller(int[] nums) { result = new int[nums.length]; int[][] iv = new int[nums.length][2]; for(int i = 0; i < nums.length; i++) { iv[i][0] = i; iv[i][1] = nums[i]; } mergeSort(iv, 0, nums.length-1); List<Integer> arr = new ArrayList<>(); for(int i : result) arr.add(i); return arr; } public void mergeSort(int[][] iv, int start, int end) { if(start >= end) return; int mid = start + (end-start) / 2; mergeSort(iv, start, mid); mergeSort(iv, mid+1, end); int l = start, r = mid+1, i = 0; int[][] temp = new int[end-start+1][2]; while(l <= mid && r <= end) { if(iv[r][1] < iv[l][1]) { temp[i] = iv[r++]; } else { temp[i] = iv[l++]; result[temp[i][0]] += r-mid-1; } i++; } while(r <= end) temp[i++] = iv[r++]; while(l <= mid) { temp[i] = iv[l++]; result[temp[i][0]] += r-mid-1; i++; } for(int j = 0; j < temp.length; j++) iv[j+start] = temp[j]; } }
- TreeMap TLE pass 15/16
public List<Integer> countSmaller(int[] nums) { int min = Integer.MAX_VALUE; for(int i : nums) if(i < min) min = i; TreeMap<Node,Integer> map = new TreeMap<>((x,y)->{ int r = x.v-y.v; if(r == 0) return Integer.compare(x.i, y.i); return r; }); Node minNode = new Node(min-1, -1); LinkedList<Integer> result = new LinkedList<>(); for(int i = nums.length-1; i >= 0; i--) { Node curr = new Node(nums[i], i); int size = map.subMap(minNode, curr).size(); map.put(curr, i); result.addFirst(size); } return result; } class Node { int v; int i; public Node(int v, int i) { this.v = v; this.i = i; } }
-
818. Race Car
Your car starts at position 0 and speed +1 on an infinite number line. (Your car can go into negative positions.) Your car drives automatically according to a sequence of instructions A (accelerate) and R (reverse). When you get an instruction "A", your car does the following: position += speed, speed *= 2. When you get an instruction "R", your car does the following: if your speed is positive then speed = -1 , otherwise speed = 1. (Your position stays the same.) For example, after commands "AAR", your car goes to positions 0->1->3->3, and your speed goes to 1->2->4->- 1. Now for some target position, say the length of the shortest sequence of instructions to get there. Example 1: Input: target = 3 Output: 2 Explanation: The shortest instruction sequence is "AA". Your position goes from 0->1->3. Example 2: Input: target = 6 Output: 5 Explanation: The shortest instruction sequence is "AAARA". Your position goes from 0->1->3->7->7->6.
- Bottom UP DP
static int[][] dp = new int[10001][2]; public int racecar(int k) { if(dp[k][0] == 0) { for(int target = 1; target < dp.length; target++) { int n = 0; while((1 << n)-1 < target) n++; if((1 << n)-1 == target) { dp[target][0] = n; dp[target][1] = n+1; continue; } int remain = (1 << n)-1 - target; dp[target][0] = n + 1 + Math.min(dp[remain][1], dp[remain][0]+1); dp[target][1] = n + 1 + Math.min(dp[remain][0], dp[remain][1]+1); for(int i = 1; i < target; i++) { dp[target][0] = Math.min(dp[target][0], Math.min(dp[i][0] + 2 + dp[target-i][0], dp[i][1] + 1 + dp[target-i][0])); dp[target][1] = Math.min(dp[target][1], Math.min(dp[i][0] + 2 + dp[target-i][1], dp[i][1] + 1 + dp[target-i][1])); } } } return Math.min(dp[k][0], dp[k][1]); }
- Top Down DP with memo
class Solution { HashMap<Integer, Integer> map = new HashMap<>(); public int racecar(int target) { return dp(target); } public int dp(int target) { if(map.containsKey(target)) return map.get(target); int n = 0; while((1 << n)-1 < target) n++; if((1 << n)-1 == target) { return n; } int result = n + 1 + dp((1 << n)-1 - target); // A(n), R, dp for(int i = 0; i < n-1; i++) { result = Math.min(result, n - 1 + 1 + i + 1 + dp(target - (1 << (n-1)) + (1 << i))); //A(n-1), R,A(i),R + dp } map.put(target, result); return result; } }
-
964. Least Operators to Express Number
964. Least Operators to Express Number
Given a single positive integer x, we will write an expression of the form x (op1) x (op2) x (op3) x ... where each operator op1, op2, etc. is either addition, subtraction, multiplication, or division (+, -, *, or /). For example, with x = 3, we might write 3 * 3 / 3 + 3 - 3 which is a value of 3. When writing such an expression, we adhere to the following conventions: The division operator (/) returns rational numbers. There are no parentheses placed anywhere. We use the usual order of operations: multiplication and division happens before addition and subtraction. It's not allowed to use the unary negation operator (-). For example, "x - x" is a valid expression as it only uses subtraction, but "-x + x" is not because it uses negation. We would like to write an expression with the least number of operators such that the expression equals the given target. Return the least number of operators used. Example 1: Input: x = 3, target = 19 Output: 5 Explanation: 3 * 3 + 3 * 3 + 3 / 3. The expression contains 5 operations. Example 2: Input: x = 5, target = 501 Output: 8 Explanation: 5 * 5 * 5 * 5 - 5 * 5 * 5 + 5 / 5. The expression contains 8 operations.
- Dijkstra
public int leastOpsExpressTarget(int x, int target) { PriorityQueue<int[]> q = new PriorityQueue<>((a,b)->(a[0]-b[0])); HashSet<Integer> visited = new HashSet<>(); q.offer(new int[]{0,target}); while(!q.isEmpty()) { int[] arr = q.poll(); int cost = arr[0], remain = arr[1]; if(visited.contains(remain)) continue; visited.add(remain); if(remain == 0) return cost-1; int k = (int)Math.floor(Math.log(remain) / Math.log(x)); int xk = (int)Math.pow(x, k); q.offer(new int[]{cost+(k==0 ? 2:k), remain-xk}); q.offer(new int[]{cost+k+1, Math.abs(remain-xk*x)}); } return -1; }
- Top Down DP
HashMap<Integer, Integer> map = new HashMap<>(); public int leastOpsExpressTarget(int x, int target) { return dp(x, target)-1; } public int dp(int x, int t) { if(map.containsKey(t)) return map.get(t); if(t == 0) return 0; if(t < x) return Math.min(2*t, 2*(x-t)+1); int k = (int)Math.floor(Math.log(t) / Math.log(x)); int xk = (int)Math.pow(x, k); int t1 = t - xk, t2 = Math.abs(xk * x - t); int result = dp(x, t1)+k; if(t2 < t) result = Math.min(result, dp(x, t2)+k+1); map.put(t, result); return result; }
-
350. Intersection of Two Arrays II
350. Intersection of Two Arrays II
Given two arrays, write a function to compute their intersection. Example 1: Input: nums1 = [1,2,2,1], nums2 = [2,2] Output: [2,2] Example 2: Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4] Output: [4,9] Note: Each element in the result should appear as many times as it shows in both arrays. The result can be in any order. Follow up: What if the given array is already sorted? How would you optimize your algorithm? What if nums1's size is small compared to nums2's size? Which algorithm is better? What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once?
- Sort
public int[] intersect(int[] nums1, int[] nums2) { Arrays.sort(nums1); Arrays.sort(nums2); int x = 0, y = 0; ArrayList<Integer> arr = new ArrayList<>(); while(x < nums1.length && y < nums2.length) { if(nums1[x] < nums2[y]) x++; else if(nums1[x] > nums2[y]) y++; else { arr.add(nums1[x]); x++; y++; } } int[] result = new int[arr.size()]; for(int i = 0; i < result.length; i++) result[i] = arr.get(i); return result; }
- HashMap
public int[] intersect(int[] nums1, int[] nums2) { HashMap<Integer, Integer> map = new HashMap<>(); ArrayList<Integer> result = new ArrayList<>(); for(int i : nums1) map.put(i, map.getOrDefault(i, 0)+1); for(int i : nums2) { if(map.containsKey(i)) { result.add(i); if(map.get(i) == 1) map.remove(i); else map.put(i, map.get(i)-1); } } int[] arr = new int[result.size()]; for(int i = 0; i < arr.length; i++) arr[i] = result.get(i); return arr; }
-
250. Count Univalue Subtrees
Given a binary tree, count the number of uni-value subtrees. A Uni-value subtree means all nodes of the subtree have the same value. Example : Input: root = [5,1,5,5,5,null,5] 5 / \ 1 5 / \ \ 5 5 5 Output: 4
- Integer null indicates a sub tree with different values
class Solution { int cnt; public int countUnivalSubtrees(TreeNode root) { if(root == null) return 0; traverse(root); return cnt; } public Integer traverse(TreeNode root) { if(root.left == null && root.right == null) { cnt++; return root.val; } if(root.left == null || root.right == null) { TreeNode child = root.left == null ? root.right : root.left; Integer sub = traverse(child); if(sub != null && sub == root.val) { cnt++; return root.val; } } else { Integer left = traverse(root.left), right = traverse(root.right); if(left != null && right != null && left == right && left == root.val) { cnt++; return root.val; } } return null; } }
- Result class
int ans = 0; public int countUnivalSubtrees1(TreeNode root) { dfs(root); return ans; } public Result dfs(TreeNode root) { if(root == null) return null; Result l = dfs(root.left); Result r = dfs(root.right); int cnt = 1; if(l != null && (l.flag == 0 || l.val != root.val)) cnt = 0; if(r != null && (r.flag == 0 || r.val != root.val)) cnt = 0; ans += cnt; return new Result(root.val, cnt); } class Result { int val; int flag; public Result(int val, int flag) { this.val = val; this.flag = flag; } }
-
198. House Robber
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night. Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. Example 1: Input: [1,2,3,1] Output: 4 Explanation: Rob house 1 (money = 1) and then rob house 3 (money = 3). Total amount you can rob = 1 + 3 = 4. Example 2: Input: [2,7,9,3,1] Output: 12 Explanation: Rob house 1 (money = 2), rob house 3 (money = 9) and rob house 5 (money = 1). Total amount you can rob = 2 + 9 + 1 = 12.
- Brutal force DFS
public int rob(int[] nums) { int r = 0, notR = 0; for(int i = 0; i < nums.length; i++) { int thisR = notR + nums[i], thisNotR = Math.max(r, notR); r = thisR; notR = thisNotR; } return Math.max(r, notR); }
-
543. Diameter of Binary Tree
Given a binary tree, you need to compute the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or may not pass through the root. Example: Given a binary tree 1 / \ 2 3 / \ 4 5 Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. Note: The length of path between two nodes is represented by the number of edges between them.
- pre order traverse
int result = 0; public int diameterOfBinaryTree(TreeNode root) { if(root == null) return 0; traverse(root); return result-1; } public int traverse(TreeNode root) { if(root == null) return 0; int left = traverse(root.left); int right = traverse(root.right); result = Math.max(result, 1+right+left); return Math.max(left, right) + 1; }
-
465. Optimal Account Balancing
465. Optimal Account Balancing
A group of friends went on holiday and sometimes lent each other money. For example, Alice paid for Bill's lunch for $10. Then later Chris gave Alice $5 for a taxi ride. We can model each transaction as a tuple (x, y, z) which means person x gave person y $z. Assuming Alice, Bill, and Chris are person 0, 1, and 2 respectively (0, 1, 2 are the person's ID), the transactions can be represented as [[0, 1, 10], [2, 0, 5]]. Given a list of transactions between a group of people, return the minimum number of transactions required to settle the debt. Note: A transaction will be given as a tuple (x, y, z). Note that x ≠ y and z > 0. Person's IDs may not be linear, e.g. we could have the persons 0, 1, 2 or we could also have the persons 0, 2, 6. Example 1: Input: [[0,1,10], [2,0,5]] Output: 2 Explanation: Person #0 gave person #1 $10. Person #2 gave person #0 $5. Two transactions are needed. One way to settle the debt is person #1 pays person #0 and #2 $5 each. Example 2: Input: [[0,1,10], [1,0,1], [1,2,5], [2,0,5]] Output: 1 Explanation: Person #0 gave person #1 $10. Person #1 gave person #0 $1. Person #1 gave person #2 $5. Person #2 gave person #0 $5. Therefore, person #1 only need to give person #0 $4, and all debt is settled.
- Brutal force DFS
public int minTransfers(int[][] transactions) { HashMap<Integer, Integer> map = new HashMap<>(); for(int[] t : transactions) { map.put(t[0], map.getOrDefault(t[0],0) - t[2]); map.put(t[1], map.getOrDefault(t[1],0) + t[2]); } ArrayList<Integer> debt = new ArrayList<>(map.values()); return settle(0, debt); } public int settle(int start, ArrayList<Integer> debt) { while(start < debt.size() && debt.get(start) == 0) start++; if(start == debt.size()) return 0; int r = Integer.MAX_VALUE; for(int i = start + 1; i < debt.size(); i++) { if(debt.get(i) * debt.get(start) < 0) { debt.set(i, debt.get(i) + debt.get(start)); r = Math.min(r, 1 + settle(start+1, debt)); debt.set(i, debt.get(i) - debt.get(start)); } } return r; }
-
152. Maximum Product Subarray
Given an integer array nums, find the contiguous subarray within an array (containing at least one number) which has the largest product. Example 1: Input: [2,3,-2,4] Output: 6 Explanation: [2,3] has the largest product 6. Example 2: Input: [-2,0,-1] Output: 0 Explanation: The result cannot be 2, because [-2,-1] is not a subarray.
class Solution { public int maxProduct(int[] nums) { int min = nums[0], max = nums[0], r = nums[0]; for(int i = 1; i < nums.length; i++) { if(nums[i] < 0) { int temp = min; min = max; max = temp; } min = Math.min(nums[i], min * nums[i]); max = Math.max(nums[i], max * nums[i]); r = Math.max(r, max); } return r; } }
-
18. 4Sum
Given an array nums of n integers and an integer target, are there elements a, b, c, and d in nums such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target. Note: The solution set must not contain duplicate quadruplets. Example: Given array nums = [1, 0, -1, 0, -2, 2], and target = 0. A solution set is: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
public List<List<Integer>> fourSum(int[] nums, int target) { List<List<Integer>> result = new ArrayList<>(); Arrays.sort(nums); for(int i = 0; i < nums.length-3; i++) { if(i > 0 && nums[i] == nums[i-1]) continue; for(int j = i+1; j < nums.length-2; j++) { if(j > i+1 && nums[j] == nums[j-1]) continue; int t = target - nums[i] - nums[j]; int l = j + 1, r = nums.length - 1; while(l < r) { if(l > j+1 && nums[l] == nums[l-1]) { l++; continue; } int sum = nums[l] + nums[r]; if(sum < t) l++; else if(sum > t) r--; else { result.add(Arrays.asList(new Integer[]{nums[i],nums[j],nums[l],nums[r]})); l++; r--; } } } } return result; }
-
16. 3Sum Closest
Given an array nums of n integers and an integer target, find three integers in nums such that the sum is closest to target. Return the sum of the three integers. You may assume that each input would have exactly one solution. Example: Given array nums = [-1, 2, 1, -4], and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
- Based on classic three sum
public int threeSumClosest(int[] nums, int target) { int result = nums[0] + nums[1] + nums[2]; Arrays.sort(nums); for(int i = 0; i < nums.length - 2; i++) { int t = target - nums[i]; int l = i+1, r = nums.length-1; while(l < r) { int sum = nums[l] + nums[r]; if(sum < t) l++; else if(sum > t) r--; else return target; if(Math.abs(target - sum - nums[i]) < Math.abs(target - result)) result = sum + nums[i]; } } return result; }
-
329. Longest Increasing Path in a Matrix
329. Longest Increasing Path in a Matrix
Given an integer matrix, find the length of the longest increasing path. From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed). Example 1: Input: nums = [ [9,9,4], [6,6,8], [2,1,1] ] Output: 4 Explanation: The longest increasing path is [1, 2, 6, 9]. Example 2: Input: nums = [ [3,4,5], [3,2,6], [2,2,1] ] Output: 4 Explanation: The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.
- Recursive with memo pad
class Solution { int[] xs = {0,0,-1,1}; int[] ys = {1,-1,0,0}; public int longestIncreasingPath(int[][] matrix) { if(matrix.length == 0 || matrix[0].length == 0) return 0; int result = 0; int[][] memo = new int[matrix.length][matrix[0].length]; for(int i = 0; i < matrix.length; i++) { for(int j = 0; j < matrix[0].length; j++) { result = Math.max(result, backtrack(matrix, i, j,memo)); } } return result; } public int backtrack(int[][] matrix, int x, int y, int[][] memo) { if(memo[x][y] > 0) return memo[x][y]; int result = 0; for(int k = 0; k < 4; k++) { int nx = x + xs[k], ny = y + ys[k]; if(!check(matrix, nx, ny)) continue; if(matrix[nx][ny] <= matrix[x][y]) continue; int sub = backtrack(matrix, nx, ny, memo); result = Math.max(result, sub); } memo[x][y] = result + 1; return result+1; } public boolean check(int[][] matrix, int i, int j) { return i >= 0 && j >= 0 && matrix.length > i && j < matrix[0].length; } }
-
101. Symmetric Tree
Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For example, this binary tree [1,2,2,3,4,4,3] is symmetric: 1 / \ 2 2 / \ / \ 3 4 4 3 But the following [1,2,2,null,3,null,3] is not: 1 / \ 2 2 \ \ 3 3
- Iterative
class Solution { public boolean isSymmetric(TreeNode root) { if(root == null) return true; Queue<TreeNode> q = new LinkedList<>(); q.offer(root.left); q.offer(root.right); while(!q.isEmpty()) { TreeNode x = q.poll(), y = q.poll(); if(x == null && y == null) continue; if(x == null || y == null || x.val != y.val) return false; q.offer(x.left); q.offer(y.right); q.offer(x.right); q.offer(y.left); } return true; } }
- Recursive
public boolean isSymmetric2(TreeNode root) { if(root == null) return true; return check(root.left, root.right); } public boolean check(TreeNode x, TreeNode y) { if(x == null && y == null) return true; if(x == null || y == null) return false; if(x.val != y.val) return false; return check(x.left, y.right) && check(x.right, y.left); }
-
64. Minimum Path Sum
Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. Note: You can only move either down or right at any point in time. Example: Input: [ [1,3,1], [1,5,1], [4,2,1] ] Output: 7 Explanation: Because the path 1→3→1→1→1 minimizes the sum.
public int minPathSum(int[][] grid) { int[][] dp = new int[grid.length][grid[0].length]; dp[0][0] = grid[0][0]; for(int i = 1; i < grid.length; i++) dp[i][0] += dp[i-1][0] + grid[i][0]; for(int i = 1; i < grid[0].length; i++) dp[0][i] += dp[0][i-1] + grid[0][i]; for(int i = 1; i < grid.length; i++) { for(int j = 1; j < grid[0].length; j++) { dp[i][j] = grid[i][j] + Math.min(dp[i-1][j], dp[i][j-1]); } } return dp[grid.length-1][grid[0].length-1]; }
-
935. Knight Dialer
A chess knight can move as indicated in the chess diagram below:
This time, we place our chess knight on any numbered key of a phone pad (indicated above), and the knight makes N-1 hops. Each hop must be from one key to another numbered key. Each time it lands on a key (including the initial placement of the knight), it presses the number of that key, pressing N digits total. How many distinct numbers can you dial in this manner? Since the answer may be large, output the answer modulo 10^9 + 7. Example 1: Input: 1 Output: 10 Example 2: Input: 2 Output: 20
int MOD = 1000000007; public int knightDialer(int N) { int[][] dict = { {4,6},{6,8},{7,9},{4,8},{0,3,9},{},{0,1,7},{2,6},{1,3},{2,4} }; int[] cnt = new int[10]; Arrays.fill(cnt, 1); while(N > 1) { int[] nextCnt = new int[10]; for(int i = 0; i <= 9; i++) { for(int j : dict[i]) { nextCnt[j] += cnt[i]; nextCnt[j] %= MOD; } } cnt = nextCnt; N--; } int result = 0; for(int i = 0; i <= 9; i++) { result += cnt[i]; result %= MOD; } return result; }
-
929. Unique Email Addresses
Every email consists of a local name and a domain name, separated by the @ sign. For example, in alice@leetcode.com, alice is the local name, and leetcode.com is the domain name. Besides lowercase letters, these emails may contain '.'s or '+'s. If you add periods ('.') between some characters in the local name part of an email address, mail sent there will be forwarded to the same address without dots in the local name. For example, "alice.z@leetcode.com" and "alicez@leetcode.com" forward to the same email address. (Note that this rule does not apply for domain names.) If you add a plus ('+') in the local name, everything after the first plus sign will be ignored. This allows certain emails to be filtered, for example m.y+name@email.com will be forwarded to my@email.com. (Again, this rule does not apply for domain names.) It is possible to use both of these rules at the same time. Given a list of emails, we send one email to each address in the list. How many different addresses actually receive mails? Example 1: Input: ["test.email+alex@leetcode.com","test.e.mail+bob.cathy@leetcode.com","testemail+david@lee.tcode.com"] Output: 2 Explanation: "testemail@leetcode.com" and "testemail@lee.tcode.com" actually receive mails
- Buffer array
public int numUniqueEmails(String[] emails) { HashSet<String> set = new HashSet<>(); for(String e : emails) { String[] localAndDomain = e.split("@"); String local = localAndDomain[0].replaceAll("\\.", ""); int index = local.indexOf("+"); if(index > 0) local = local.substring(0, index); set.add(local+"@"+localAndDomain[1]); } return set.size(); }
-
635. Design Log Storage System
635. Design Log Storage System
You are given several logs that each log contains a unique id and timestamp. Timestamp is a string that has the following format: Year:Month:Day:Hour:Minute:Second, for example, 2017:01:01:23:59:59. All domains are zero-padded decimal numbers. Design a log storage system to implement the following functions: void Put(int id, string timestamp): Given a log's unique id and timestamp, store the log in your storage system. int[] Retrieve(String start, String end, String granularity): Return the id of logs whose timestamps are within the range from start to end. Start and end all have the same format as timestamp. However, granularity means the time level for consideration. For example, start = "2017:01:01:23:59:59", end = "2017:01:02:23:59:59", granularity = "Day", it means that we need to find the logs within the range from Jan. 1st 2017 to Jan. 2nd 2017. Example 1: put(1, "2017:01:01:23:59:59"); put(2, "2017:01:01:22:59:59"); put(3, "2016:01:01:00:00:00"); retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Year"); // return [1,2,3], because you need to return all logs within 2016 and 2017. retrieve("2016:01:01:01:01:01","2017:01:01:23:00:00","Hour"); // return [1,2], because you need to return all logs start from 2016:01:01:01 to 2017:01:01:23, where log 3 is left outside the range. Note: There will be at most 300 operations of Put or Retrieve. Year ranges from [2000,2017]. Hour ranges from [00,23]. Output for Retrieve has no order required.
- TreeMap
TreeMap<String, ArrayList<Integer>> map = new TreeMap<>(); HashMap<String, Integer> dict = new HashMap<>(); public LogSystem() { dict.put("Year", 0); dict.put("Month", 1); dict.put("Day", 2); dict.put("Hour", 3); dict.put("Minute", 4); dict.put("Second", 5); } public void put(int id, String timestamp) { ArrayList<Integer> arr = map.getOrDefault(timestamp, new ArrayList<Integer>()); arr.add(id); map.put(timestamp, arr); } public List<Integer> retrieve(String s, String e, String gra) { List<Integer> result = new ArrayList<>(); int i = dict.get(gra); String min = s.substring(0, i * 3 + 4), max = e.substring(0, i * 3 + 4); max += ":9"; // :9 is the largest lexicographically for(ArrayList<Integer> ids : map.subMap(min, max).values()) result.addAll(ids); return result; }
-
317. Shortest Distance from All Buildings
317. Shortest Distance from All Buildings
You want to build a house on an empty land which reaches all buildings in the shortest amount of distance. You can only move up, down, left and right. You are given a 2D grid of values 0, 1 or 2, where: Each 0 marks an empty land which you can pass by freely. Each 1 marks a building which you cannot pass through. Each 2 marks an obstacle which you cannot pass through. Example: Input: [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]] 1 - 0 - 2 - 0 - 1 | | | | | 0 - 0 - 0 - 0 - 0 | | | | | 0 - 0 - 1 - 0 - 0 Output: 7 Explanation: Given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2), the point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7. Note: There will be at least one building. If it is not possible to build such house according to the above rules, return -1.
- Brutal BFS starting from every 0
A good comment on why don’t start at 1 “Does anyone want to ask Why don’t we start from ‘0’? This is also what I am thinking. At the first glance, the time complexity of starting from buildings O(BMN) (B: # of buildings) and starting from empty places O(EMN) (E: # of empty places) might be the same. If in an interview, I think we can ask for clarification. If the empty places are far more than buildings, ex. we have 1 million empty places and only 1 building, starting from building is better. So it depends on how many empty places and buildings that we have. We are not going to say this way or that way is better, but it’s a kind of trade-off.” https://leetcode.com/problems/shortest-distance-from-all-buildings/discuss/76891/Java-solution-with-explanation-and-time-complexity-analysis/216592
class Solution { public int shortestDistance(int[][] grid) { int shortest = Integer.MAX_VALUE; int ones = 0; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { if(grid[i][j] == 1) ones++; } } for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { if(grid[i][j] == 0) { int distance = bfs(grid, i, j, ones); shortest = Math.min(shortest, distance); } } } return shortest == Integer.MAX_VALUE ? -1 : shortest; } int[] xs = {0,0,1,-1}, ys = {1,-1,0,0}; public int bfs(int[][] grid, int i, int j, int ones) { ArrayDeque<Integer> xq = new ArrayDeque<>(), yq = new ArrayDeque<>(); xq.offer(i); yq.offer(j); boolean[][] visited = new boolean[grid.length][grid[0].length]; visited[i][j] = true; int distance = 0; int level = 0; int oneCnt = 0; while(!xq.isEmpty()) { int size = xq.size(); level++; for(int k = 0; k < size; k++) { int x = xq.poll(), y = yq.poll(); for(int p = 0; p < 4; p++) { int nx = xs[p] + x, ny = ys[p] + y; if(nx >= 0 && ny >= 0 && nx < grid.length && ny < grid[0].length && !visited[nx][ny] && grid[nx][ny] != 2) { visited[nx][ny] = true; if(grid[nx][ny] == 1) { distance += level; oneCnt++; } else { xq.offer(nx); yq.offer(ny); } } } } } return oneCnt == ones ? distance : Integer.MAX_VALUE; } }
-
427. Construct Quad Tree
We want to use quad trees to store an N x N boolean grid. Each cell in the grid can only be true or false. The root node represents the whole grid. For each node, it will be subdivided into four children nodes until the values in the region it represents are all the same. Each node has another two boolean attributes : isLeaf and val. isLeaf is true if and only if the node is a leaf node. The val attribute for a leaf node contains the value of the region it represents. Your task is to use a quad tree to represent a given grid. The following example may help you understand the problem better: Given the 8 x 8 grid below, we want to construct the corresponding quad tree: It can be divided according to the definition above:
The corresponding quad tree should be as following, where each node is represented as a (isLeaf, val) pair.
For the non-leaf nodes, val can be arbitrary, so it is represented as *. Note: N is less than 1000 and guaranteened to be a power of 2. If you want to know more about the quad tree, you can refer to its wiki.
- Divide and Conquer
public Node construct(int[][] grid) { Node root = build(grid, 0, 0, grid.length); return root; } public Node build(int[][] grid, int row, int col, int len) { Node n = new Node(); int sum = 0; for(int i = row; i < row + len; i++) { for(int j = col; j < col + len; j++) { sum += grid[i][j]; } } if(sum == len * len) { n.isLeaf = true; n.val = true; } else if(sum == 0) { n.isLeaf = true; } else { int half = len / 2; n.topLeft = build(grid, row, col, half); n.topRight = build(grid, row, col + half, half); n.bottomLeft = build(grid, row + half, col, half); n.bottomRight = build(grid, row + half, col + half, half); } return n; }
-
234. Palindrome Linked List
Given a singly linked list, determine if it is a palindrome. Example 1: Input: 1->2 Output: false Example 2: Input: 1->2->2->1 Output: true Follow up: Could you do it in O(n) time and O(1) space?
- Reverse half list
public boolean isPalindrome(ListNode head) { if(head == null) return true; ListNode fast = head, slow = head; while(fast != null) { fast = fast.next; if(fast != null) { fast = fast.next; slow = slow.next; } } ListNode sentinel = new ListNode(0), curr = slow; sentinel.next = slow; while(curr.next != null) { ListNode moved = curr.next; curr.next = moved.next; moved.next = sentinel.next; sentinel.next = moved; } ListNode l1 = head, l2 = sentinel.next; while(l1 != slow) { if(l1.val != l2.val) return false; l1 = l1.next; l2 = l2.next; } return true; }
- Recursive with instance fields
ListNode curr; boolean isP; public boolean isPalindromeDFS(ListNode head) { curr = head; isP = true; dfs(head); return isP; } public void dfs(ListNode head) { if(head == null) { return; } dfs(head.next); if(curr.val != head.val) { isP = false; } curr = curr.next; }
-
105. Construct Binary Tree from Preorder and Inorder Traversal
105. Construct Binary Tree from Preorder and Inorder Traversal
Given preorder and inorder traversal of a tree, construct the binary tree. Note: You may assume that duplicates do not exist in the tree. For example, given preorder = [3,9,20,15,7] inorder = [9,3,15,20,7] Return the following binary tree: 3 / \ 9 20 / \ 15 7
- Recursive Optimized with hashmap to store inorder element -> index
class Solution { HashMap<Integer, Integer> map = new HashMap<>(); // inorder element -> index public TreeNode buildTree(int[] preorder, int[] inorder) { for(int i = 0; i < inorder.length; i++) map.put(inorder[i], i); return recursion(preorder, inorder, 0, preorder.length-1, 0, inorder.length-1); } public TreeNode recursion(int[] preorder, int[] inorder, int l, int r, int il, int ir) { if(preorder.length == 0) return null; if(l > r) return null; TreeNode root = new TreeNode(preorder[l]); if(l < r) { int i = map.get(preorder[l]); // while(i <= ir && inorder[i] != preorder[l]) i++; int leftLen = i - il, rightLen = ir - i; root.left = recursion(preorder, inorder, l+1, l+leftLen, il, i-1); root.right = recursion(preorder, inorder, l+leftLen+1, r, i+1, ir); } return root; }
-
48. Rotate Image
You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwise). Note: You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation. Example 1: Given input matrix = [ [1,2,3], [4,5,6], [7,8,9] ], rotate the input matrix in-place such that it becomes: [ [7,4,1], [8,5,2], [9,6,3] ] Example 2: Given input matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ], rotate the input matrix in-place such that it becomes: [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ]
- O(1) Space
public void rotate(int[][] matrix) { int offset = 0; while(true) { if(matrix.length - offset * 2 <= 1) break; int far = matrix.length - 1 - offset; for(int j = offset; j < far; j++) { int temp = matrix[offset][j]; matrix[offset][j] = matrix[far-(j-offset)][offset]; matrix[far-(j-offset)][offset] = matrix[far][far-(j-offset)]; matrix[far][far-(j-offset)] = matrix[j][far]; matrix[j][far] = temp; } offset++; } }
- Buffer array
public void rotate(int[][] matrix) { int offset = 0; while(true) { if(matrix.length - offset * 2 <= 1) break; int[] buffer = new int[matrix.length - offset * 2 - 1]; System.arraycopy(matrix[offset], offset, buffer, 0, buffer.length); System.out.println(Arrays.toString(buffer)); int far = offset + buffer.length; for(int k = offset; k < far; k++) matrix[offset][k] = matrix[far-(k-offset)][offset]; for(int k = offset; k < far; k++) matrix[far-(k-offset)][offset] = matrix[far][far-(k-offset)]; for(int k = offset; k < far; k++) matrix[far][far-(k-offset)] = matrix[k][far]; for(int k = offset; k < far; k++) matrix[k][far] = buffer[k-offset]; offset++; } }
-
37. Sudoku Solver
This problem reminds me the N-Queens problem: N-queens
Write a program to solve a Sudoku puzzle by filling the empty cells. A sudoku solution must satisfy all of the following rules: Each of the digits 1-9 must occur exactly once in each row. Each of the digits 1-9 must occur exactly once in each column. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid. Empty cells are indicated by the character '.'. A sudoku puzzle...
...and its solution numbers marked in red.
Note: The given board contain only digits 1-9 and the character '.'. You may assume that the given Sudoku puzzle will have a single unique solution. The given board size is always 9x9.
- BackTraking
class Solution { HashSet[] rows = new HashSet[9], cols = new HashSet[9], subs = new HashSet[9]; boolean find; public void solveSudoku(char[][] board) { for(int i = 0; i < 9; i++) rows[i] = new HashSet<Integer>(); for(int i = 0; i < 9; i++) cols[i] = new HashSet<Integer>(); for(int i = 0; i < 9; i++) subs[i] = new HashSet<Integer>(); for(int i = 0; i < 9; i++) for(int j = 0; j < 9; j++) if(board[i][j] != '.') visit(board[i][j]-'0', i, j, getSubIndex(i,j)); backtrack(board, 0, 0); } public void backtrack(char[][] board, int i, int j) { if(find) return; if(j == 9) { i++; j = 0; } if(i == 9) { find = true; return; } int subIndex = getSubIndex(i, j); if(board[i][j] != '.') backtrack(board, i, j+1); else { for(int k = 1; k <= 9; k++) { if(isValid(k, i, j, subIndex)) { board[i][j] = (char)(k + '0'); visit(k, i, j, subIndex); backtrack(board, i, j+1); if(find) return; cancel(k, i, j, subIndex); board[i][j] = '.'; } } } } public void visit(int k, int i, int j, int z) { rows[i].add(k); cols[j].add(k); subs[z].add(k); } public void cancel(int k, int i, int j, int z) { rows[i].remove(k); cols[j].remove(k); subs[z].remove(k); } public boolean isValid(int k, int i, int j, int z) { return !(rows[i].contains(k) || cols[j].contains(k) || subs[z].contains(k)); } public int getSubIndex(int i, int j) { return (i / 3) * 3 + (j / 3); } }
-
14. Longest Common Prefix
Write a function to find the longest common prefix string amongst an array of strings. If there is no common prefix, return an empty string "". Example 1: Input: ["flower","flow","flight"] Output: "fl" Example 2: Input: ["dog","racecar","car"] Output: "" Explanation: There is no common prefix among the input strings. Note: All given inputs are in lowercase letters a-z.
class Solution { public String longestCommonPrefix(String[] strs) { String result = ""; int i = 0; out:while(strs.length > 0) { if(i == strs[0].length()) break out; char curr = strs[0].charAt(i); for(String s : strs) { if(i == s.length() || s.charAt(i) != curr) break out; } i++; result += curr; } return result; } }
-
199. Binary Tree Right Side View
199. Binary Tree Right Side View
Given a binary tree, imagine yourself standing on the right side of it, return the values of the nodes you can see ordered from top to bottom. Example: Input: [1,2,3,null,5,null,4] Output: [1, 3, 4] Explanation: 1 <--- / \ 2 3 <--- \ \ 5 4 <---
- preorder traverse; right -> left
class Solution { int deepest = 0; public List<Integer> rightSideView(TreeNode root) { ArrayList<Integer> result = new ArrayList<>(); traverse(root, result, 1); return result; } public void traverse(TreeNode root, ArrayList<Integer> result, int level) { if(root == null) return; if(level == deepest + 1) { result.add(root.val); deepest = level; } traverse(root.right, result, level+1); traverse(root.left, result, level+1); } }
- preorder traverse; right -> left
-
50. Pow(x, n)
Implement pow(x, n), which calculates x raised to the power n (xn). Example 1: Input: 2.00000, 10 Output: 1024.00000 Example 2: Input: 2.10000, 3 Output: 9.26100 Example 3: Input: 2.00000, -2 Output: 0.25000 Explanation: 2-2 = 1/22 = 1/4 = 0.25 Note: -100.0 < x < 100.0 n is a 32-bit signed integer, within the range [−231, 231 − 1]
- Recursive
Another option is to change x -> 1/x, n -> -n if n is negative at the very beginning.
public double myPow(double x, int n) { if(n == 0) return 1; if(n == 1) return x; if(n == -1) return 1 / x; double divide = myPow(x, n / 2); double total = divide * divide; if(n % 2 != 0) total *= myPow(x, n % 2); return total; }
- Recursive
Another option is to change x -> 1/x, n -> -n if n is negative at the very beginning.
-
312. Burst Balloons
Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. Here left and right are adjacent indices of i. After the burst, the left and right * then becomes adjacent. Find the maximum coins you can collect by bursting the balloons wisely. Note: You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 Example: Input: [3,1,5,8] Output: 167 Explanation: nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167
- Bottom UP DP
public int maxCoins(int[] arr) { int[] nums = new int[arr.length+2]; nums[0] = 1; nums[nums.length-1] = 1; System.arraycopy(arr, 0, nums, 1, arr.length); int[][] dp = new int[nums.length][nums.length]; for(int window = 3; window <= nums.length; window++) { for(int l = 0; l+window-1 < nums.length; l++) { int r = l + window - 1; for(int i = l + 1; i < r; i++) dp[l][r] = Math.max(dp[l][r], nums[i]*nums[l]*nums[r] + dp[l][i] + dp[i][r]); } } return dp[0][nums.length-1]; }
- Recursive MEMO
public int maxCoins(int[] arr) { int[][] dp = new int[arr.length+2][arr.length+2]; int result = backtrack(arr, -1, arr.length, dp); return result; } public int backtrack(int[] arr, int l, int r, int[][] dp) { int result = 0; if(dp[l+1][r+1] > 0) return dp[l+1][r+1]; if(l + 1 == r) return 0; for(int i = l + 1; i < r; i++) { int center = arr[i] * (l >= 0 ? arr[l] : 1) * (r < arr.length ? arr[r] : 1); int left = backtrack(arr, l, i, dp), right = backtrack(arr, i, r, dp); result = Math.max(center + left + right, result); } dp[l+1][r+1] = result; return result; }
- Brutal force (TLE)
class Solution { HashMap<String, Integer> map = new HashMap<>(); HashSet<Integer> visited = new HashSet<>(); public int maxCoins(int[] arr) { return go(arr); } public int go(int[] arr) { String key = encode(arr); if(map.containsKey(key)) return map.get(key); int money = 0; for(int index = 0; index < arr.length; index++) { if(visited.contains(index)) continue; visited.add(index); int result = arr[index]; int left = index-1; while(visited.contains(left)) left--; int leftValue = left == -1 ? 1 : arr[left]; int right = index+1; while(visited.contains(right)) right++; int rightValue = right == arr.length ? 1 : arr[right]; result *= leftValue * rightValue; money = Math.max(money, go(arr) + result); visited.remove(index); } map.put(key, money); return money; } public String encode(int[] arr) { StringBuilder sb = new StringBuilder(); for(int i = 0; i < arr.length; i++) { if(!visited.contains(i)) sb.append(i).append('*'); } return sb.toString(); } }
-
43. Multiply Strings
Given two non-negative integers num1 and num2 represented as strings, return the product of num1 and num2, also represented as a string. Example 1: Input: num1 = "2", num2 = "3" Output: "6" Example 2: Input: num1 = "123", num2 = "456" Output: "56088" Note: The length of both num1 and num2 is < 110. Both num1 and num2 contain only digits 0-9. Both num1 and num2 do not contain any leading zero, except the number 0 itself. You must not use any built-in BigInteger library or convert the inputs to integer directly.
- Multiply all pairs of digits, and store the product in an array.
public String multiply(String num1, String num2) { int[] result = new int[num1.length()+num2.length()]; for(int i = num1.length()-1; i >= 0; i--) { for(int j = num2.length()-1; j >= 0; j--) { result[i+j+1] += (num1.charAt(i)-'0') * (num2.charAt(j)-'0'); } } int carry = 0; for(int i = result.length-1; i >= 0; i--) { int temp = (result[i]+carry) % 10; carry = (result[i]+carry) / 10; result[i] = temp; } StringBuilder sb = new StringBuilder(); for(int d : result) if(!(sb.length()==0 && d == 0)) sb.append(d); return sb.length() == 0 ? "0" : sb.toString(); }
-
70. Climbing Stairs (509 Fibonacci Number)
70. Climbing Stairs (509 Fibonacci Number)
You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? Note: Given n will be a positive integer. Example 1: Input: 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps Example 2: Input: 3 Output: 3 Explanation: There are three ways to climb to the top. 1. 1 step + 1 step + 1 step 2. 1 step + 2 steps 3. 2 steps + 1 step
- Simple DP
public int climbStairs(int n) { if(n <= 1) return n; int x = 1, y = 2; for(int i = 2; i < n; i++) { int z = x + y; x = y; y = z; } return y; }
-
128. Longest Consecutive Sequence
128. Longest Consecutive Sequence
Given an unsorted array of integers, find the length of the longest consecutive elements sequence. Your algorithm should run in O(n) complexity. Example: Input: [100, 4, 200, 1, 3, 2] Output: 4 Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. Therefore its length is 4.
public int longestConsecutive(int[] num) { HashMap<Integer,Integer> map = new HashMap<>(); HashSet<Integer> set = new HashSet<>(); for(int i : num) set.add(i); int result = 0; for(int i : num) { if(set.contains(i)) { int cnt = 0, curr = i; while(set.contains(curr)) { set.remove(curr++); cnt++; } if(map.containsKey(curr)) cnt += map.get(curr); map.put(i, cnt); result = Math.max(result, cnt); } } return result; }
- A smarter version
public int longestConsecutive2(int[] num) { HashSet<Integer> set = new HashSet<>(); int ans = 0; for(int n : num) set.add(n); for(int n : set) { if(!set.contains(n-1)) { int curr = n; int cnt = 1; while(set.contains(curr+1)) { curr++; cnt++; } ans = Math.max(ans, cnt); } } return ans; }
- A smarter version
-
92. Reverse Linked List II
Reverse a linked list from position m to n. Do it in one-pass. Note: 1 ≤ m ≤ n ≤ length of list. Example: Input: 1->2->3->4->5->NULL, m = 2, n = 4 Output: 1->4->3->2->5->NULL
public ListNode reverseBetween(ListNode head, int m, int n) { int cnt = 1; ListNode sentinel = new ListNode(0), prev = sentinel, curr = head; prev.next = curr; while(cnt < m) { prev = curr; curr = curr.next; cnt++; } while(cnt < n) { ListNode moved = curr.next; curr.next = moved.next; moved.next = prev.next; prev.next = moved; cnt++; } return sentinel.next; }
-
143. Reorder List
Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You may not modify the values in the list's nodes, only nodes itself may be changed. Example 1: Given 1->2->3->4, reorder it to 1->4->2->3. Example 2: Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
- Reverse the Second half of the list
class Solution { // middle reverse and reorder public void reorderList(ListNode head) { if(head == null || head.next == null) return; ListNode fast = head, slow = head; while(fast != null) { fast = fast.next; if(fast != null) { fast = fast.next; slow = slow.next; } } ListNode sentinel = new ListNode(0), curr = slow; sentinel.next = curr; while(curr.next != null) { ListNode moved = curr.next; curr.next = moved.next; moved.next = sentinel.next; sentinel.next = moved; } ListNode l1 = head, l2 = sentinel.next; curr = sentinel; while(l1 != slow) { curr.next = l1; l1 = l1.next; curr = curr.next; curr.next = l2; l2 = l2.next; curr = curr.next; } if(l2 != null) curr.next = l2; sentinel.next = null; } }
- Stack
public void reorderList2(ListNode head) { if(head == null || head.next == null) return; ListNode fast = head, slow = head; while(fast != null) { fast = fast.next; if(fast != null) { fast = fast.next; slow = slow.next; } } LinkedList<ListNode> stack = new LinkedList<>(); ListNode curr = slow; while(curr != null) { stack.push(curr); curr = curr.next; } ListNode prev = new ListNode(0); ListNode l1 = head, l2 = null; while(l1 != slow) { prev.next = l1; l1 = l1.next; prev = prev.next; l2 = stack.pop(); prev.next = l2; prev = prev.next; } if(!stack.isEmpty()) { prev.next = stack.pop(); prev = prev.next; } prev.next = null; }
-
426. Convert Binary Search Tree to Sorted Doubly Linked List
426. Convert Binary Search Tree to Sorted Doubly Linked List
Convert a BST to a sorted circular doubly-linked list in-place. Think of the left and right pointers as synonymous to the previous and next pointers in a doubly-linked list. Let's take the following BST as an example, it may help you understand the problem better:
We want to transform this BST into a circular doubly linked list. Each node in a doubly linked list has a predecessor and successor. For a circular doubly linked list, the predecessor of the first element is the last element, and the successor of the last element is the first element. The figure below shows the circular doubly linked list for the BST above. The "head" symbol means the node it points to is the smallest element of the linked list.
Specifically, we want to do the transformation in place. After the transformation, the left pointer of the tree node should point to its predecessor, and the right pointer should point to its successor. We should return the pointer to the first element of the linked list. The figure below shows the transformed BST. The solid line indicates the successor relationship, while the dashed line means the predecessor relationship.
- Recursive
class Solution { Node head; Node prev; public Node treeToDoublyList(Node root) { if(root == null) return null; traverse(root); head.left = prev; prev.right = head; return head; } public void traverse(Node root) { if(root == null) return; traverse(root.left); if(prev != null) { prev.right = root; root.left = prev; } else { head = root; } prev = root; traverse(root.right); } }
- Iterative
public Node treeToDoublyListIterative(Node root) { if(root == null) return root; Node curr = root, prev = null, head = null; LinkedList<Node> stack = new LinkedList<>(); while(!stack.isEmpty() || curr != null) { while(curr != null) { stack.push(curr); curr = curr.left; } curr = stack.pop(); if(prev == null) head = curr; else { prev.right = curr; curr.left = prev; } prev = curr; curr = curr.right; } head.left = prev; prev.right = head; return head; }
-
547. Friend Circles
There are N students in a class. Some of them are friends, while some are not. Their friendship is transitive in nature. For example, if A is a direct friend of B, and B is a direct friend of C, then A is an indirect friend of C. And we defined a friend circle is a group of students who are direct or indirect friends.
Given a N*N matrix M representing the friend relationship between students in the class. If M[i][j] = 1, then the ith and jth students are direct friends with each other, otherwise not. And you have to output the total number of friend circles among all the students.
Example 1:
Input: [[1,1,0], [1,1,0], [0,0,1]] Output: 2 Explanation:The 0th and 1st students are direct friends, so they are in a friend circle. The 2nd student himself is in a friend circle. So return 2.
Example 2:
Input: [[1,1,0], [1,1,1], [0,1,1]] Output: 1 Explanation:The 0th and 1st students are direct friends, the 1st and 2nd students are direct friends, so the 0th and 2nd students are indirect friends. All of them are in the same friend circle, so return 1.
Note:
N is in range [1,200]. M[i][i] = 1 for all students. If M[i][j] = 1, then M[j][i] = 1.
- UnionFind
class UnionFind { int[] p; int cnt; public UnionFind(int n) { p = new int[n]; for(int i = 0; i < n; i++) p[i] = i; cnt = n; } public int find(int x) { if(p[x] != x) p[x] = find(p[x]); return p[x]; } public void union(int x, int y) { int i = find(x), j = find(y); if(i != j) { cnt--; p[i] = j; } } } public int findCircleNum(int[][] M) { UnionFind uf = new UnionFind(M.length); for(int i = 0; i < M.length; i++) { for(int j = i+1; j< M.length; j++) { if(M[i][j] == 1) uf.union(i, j); } } return uf.cnt; } public int findCircleNumDFS(int[][] M) { HashSet<Integer> visited = new HashSet<>(); int cnt = 0; for(int i = 0; i < M.length; i++) { if(visited.contains(i)) continue; visited.add(i); cnt++; dfs(M, i, visited); } return cnt; }
- DFS i has been visited if M[i][i] == 1
public int findCircleNum(int[][] M) { int ans = 0; for(int i = 0; i < M.length; i++) { ans += mark(M, i); } return ans; } public int mark(int[][] M, int i) { if(M[i][i] == 0) return 0; M[i][i] = 0; for(int j = 0; j < M.length; j++) { if(M[i][j] == 1) mark(M, j); } return 1; }
-
773. Sliding Puzzle
On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, and an empty square represented by 0. A move consists of choosing 0 and a 4-directionally adjacent number and swapping it. The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]]. Given a puzzle board, return the least number of moves required so that the state of the board is solved. If it is impossible for the state of the board to be solved, return -1. Examples: Input: board = [[1,2,3],[4,0,5]] Output: 1 Explanation: Swap the 0 and the 5 in one move. Input: board = [[1,2,3],[5,4,0]] Output: -1 Explanation: No number of moves will make the board solved. Input: board = [[4,1,2],[5,0,3]] Output: 5 Explanation: 5 is the smallest number of moves that solves the board. An example path: After move 0: [[4,1,2],[5,0,3]] After move 1: [[4,1,2],[0,5,3]] After move 2: [[0,1,2],[4,5,3]] After move 3: [[1,0,2],[4,5,3]] After move 4: [[1,2,0],[4,5,3]] After move 5: [[1,2,3],[4,5,0]] Input: board = [[3,2,4],[1,5,0]] Output: 14 Note: board will be a 2 x 3 array as described above. board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5].
public int slidingPuzzle1(int[][] board) { HashMap<Integer, int[][]> map = new HashMap<>(); LinkedList<Integer> q = new LinkedList<>(); HashSet<Integer> visited = new HashSet<>(); int[] xs = {1,-1,0,0}, ys = {0,0,1,-1}; int key = toKey(board); visited.add(key); map.put(key, board); q.offer(key); int step = -1; while(!q.isEmpty()) { step++; int size = q.size(); for(int i = 0; i < size; i++) { int k = q.poll(); if(k == 123450) return step; int[][] matrix = map.get(k); for(int a = 0; a < matrix.length; a++) { for(int b = 0; b < matrix[0].length; b++) { if(matrix[a][b] == 0) { for(int j = 0; j < 4; j++) { int nx = xs[j] + a, ny = ys[j] + b; if(nx >= 0 && ny >= 0 && nx < matrix.length && ny < matrix[0].length) { int[][] next = new int[matrix.length][matrix[0].length]; for(int c = 0; c < matrix.length; c++) { for(int d = 0; d < matrix[0].length; d++) { next[c][d] = matrix[c][d]; } } int temp = next[a][b]; next[a][b] = next[nx][ny]; next[nx][ny] = temp; int nextKey = toKey(next); if(visited.contains(nextKey)) continue; visited.add(nextKey); q.offer(nextKey); map.put(nextKey, next); } } } } } } } return -1; }
-
755. Pour Water
We are given an elevation map, heights[i] representing the height of the terrain at that index. The width at each index is 1. After V units of water fall at index K, how much water is at each index? Water first drops at index K and rests on top of the highest terrain or water at that index. Then, it flows according to the following rules: If the droplet would eventually fall by moving left, then move left. Otherwise, if the droplet would eventually fall by moving right, then move right. Otherwise, rise at it's current position. Here, "eventually fall" means that the droplet will eventually be at a lower level if it moves in that direction. Also, "level" means the height of the terrain plus any water in that column. We can assume there's infinitely high terrain on the two sides out of bounds of the array. Also, there could not be partial water being spread out evenly on more than 1 grid block - each unit of water has to be in exactly one block. Example 1: Input: heights = [2,1,1,2,1,2,2], V = 4, K = 3 Output: [2,2,2,3,2,2,2] Explanation: # # # # ## # ### ######### 0123456 <- index The first drop of water lands at index K = 3: # # # w # ## # ### ######### 0123456 When moving left or right, the water can only move to the same level or a lower level. (By level, we mean the total height of the terrain plus any water in that column.) Since moving left will eventually make it fall, it moves left. (A droplet "made to fall" means go to a lower height than it was at previously.) # # # # ## w# ### ######### 0123456 Since moving left will not make it fall, it stays in place. The next droplet falls: # # # w # ## w# ### ######### 0123456 Since the new droplet moving left will eventually make it fall, it moves left. Notice that the droplet still preferred to move left, even though it could move right (and moving right makes it fall quicker.) # # # w # ## w# ### ######### 0123456 # # # # ##ww# ### ######### 0123456 After those steps, the third droplet falls. Since moving left would not eventually make it fall, it tries to move right. Since moving right would eventually make it fall, it moves right. # # # w # ##ww# ### ######### 0123456 # # # # ##ww#w### ######### 0123456 Finally, the fourth droplet falls. Since moving left would not eventually make it fall, it tries to move right. Since moving right would not eventually make it fall, it stays in place: # # # w # ##ww#w### ######### 0123456 The final answer is [2,2,2,3,2,2,2]: # ####### ####### 0123456
public int[] pourWater(int[] h, int V, int K) { for(int i = V; i > 0; i--) { int j = K; while(j-1 >= 0 && h[j] >= h[j-1]) j--; while(j+1 <= h.length-1 && h[j] >= h[j+1]) j++; if(j > K) { while(j-1 >= K && h[j] >= h[j-1]) j--; } h[j]++; } return h; }
-
24. Swap Nodes in Pairs
Given a linked list, swap every two adjacent nodes and return its head. You may not modify the values in the list's nodes, only nodes itself may be changed. Example: Given 1->2->3->4, you should return the list as 2->1->4->3.
public ListNode swapPairs(ListNode head) { ListNode sentinel = new ListNode(0); sentinel.next = head; ListNode curr = head, prev = sentinel; while(curr != null && curr.next != null) { ListNode n = curr.next; prev.next = n; curr.next = n.next; n.next = curr; prev = curr; curr = prev.next; } return sentinel.next; }
-
151. Reverse Words in a String
151. Reverse Words in a String
Given an input string, reverse the string word by word. Example 1: Input: "the sky is blue" Output: "blue is sky the" Example 2: Input: " hello world! " Output: "world! hello" Explanation: Your reversed string should not contain leading or trailing spaces.
public String reverseWords(String s) { StringBuilder result = new StringBuilder(), temp = new StringBuilder(); int i = s.length()-1; while(i >= 0) { while(i >= 0 && s.charAt(i) == ' ') i--; int j = i; while(i >= 0 && s.charAt(i) != ' ') i--; temp.setLength(0); if(j >= 0) { temp.append(s.substring(i+1, j+1)).append(' '); result.append(temp); } } if(result.length() == 0) return ""; else result.setLength(result.length()-1); return result.toString(); }
public String reverseWords(String s) { s = s.trim().replace("\\s+", " "); System.out.println(s); char[] arr = s.toCharArray(); int i = 0, j = 0; while(j < arr.length) { // System.out.println(j); while(j < arr.length && arr[j] != ' ') j++; reverse(arr, i, j-1); j++; i = j; } reverse(arr, 0, arr.length-1); String n = new String(arr); return n; } public void reverse(char[] arr, int i, int j) { while(i < j) { char temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; i++;j--; } }
-
218. The Skyline Problem
A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B).
The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0. For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] . The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour. For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]. Notes: The number of buildings in any input list is guaranteed to be in the range [0, 10000]. The input list is already sorted in ascending order by the left x position Li. The output list must be sorted by the x position. There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...[2 3], [4 5], [12 7], ...]
Fail to solve it by myself T_T
- Very Smart Solution
class Solution { public List<int[]> getSkyline(int[][] buildings) { List<int[]> result = new ArrayList<>(); List<int[]> height = new ArrayList<>(); for(int[] b:buildings) { height.add(new int[]{b[0], -b[2]}); height.add(new int[]{b[1], b[2]}); } Collections.sort(height, (a, b) -> { if(a[0] != b[0]) return a[0] - b[0]; return a[1] - b[1]; }); Queue<Integer> pq = new PriorityQueue<>((a, b) -> (b - a)); pq.offer(0); int prev = 0; for(int[] h:height) { if(h[1] < 0) { pq.offer(-h[1]); } else { pq.remove(h[1]); } int cur = pq.peek(); if(prev != cur) { result.add(new int[]{h[0], cur}); prev = cur; } } return result; } }
-
362. Design Hit Counter
Design a hit counter which counts the number of hits received in the past 5 minutes. Each function accepts a timestamp parameter (in seconds granularity) and you may assume that calls are being made to the system in chronological order (ie, the timestamp is monotonically increasing). You may assume that the earliest timestamp starts at 1. It is possible that several hits arrive roughly at the same time. Example: HitCounter counter = new HitCounter(); // hit at timestamp 1. counter.hit(1); // hit at timestamp 2. counter.hit(2); // hit at timestamp 3. counter.hit(3); // get hits at timestamp 4, should return 3. counter.getHits(4); // hit at timestamp 300. counter.hit(300); // get hits at timestamp 300, should return 4. counter.getHits(300); // get hits at timestamp 301, should return 3. counter.getHits(301); Follow up: What if the number of hits per second could be very large? Does your design scale?
- Two Array; Record the timestamps
private int[] times; private int[] hits; /** Initialize your data structure here. */ public HitCounter() { times = new int[300]; hits = new int[300]; } /** Record a hit. @param timestamp - The current timestamp (in seconds granularity). */ public void hit(int timestamp) { int index = timestamp % 300; if (times[index] != timestamp) { times[index] = timestamp; hits[index] = 1; } else { hits[index]++; } } /** Return the number of hits in the past 5 minutes. @param timestamp - The current timestamp (in seconds granularity). */ public int getHits(int timestamp) { int total = 0; for (int i = 0; i < 300; i++) { if (timestamp - times[i] < 300) { total += hits[i]; } } return total; }
- Array; Record Max Timestamp; clear on every action
public class HitCounter { int max; int[] hits; public HitCounter() { hits = new int[300]; } public void hit(int timestamp) { for(int i = max + 1; i <= timestamp; i++) hits[i%300] = 0; hits[timestamp % 300]++; max = timestamp; } public int getHits(int timestamp) { int sum = 0; for(int i = max + 1; i <= timestamp; i++) hits[i%300] = 0; for(int i = 0; i < 300; i++) sum += hits[i]; max = timestamp; return sum; } }
- TreeMap
class HitCounter { TreeMap<Integer, Integer> map = new TreeMap<>(); /** Initialize your data structure here. */ public HitCounter() {} /** Record a hit. @param timestamp - The current timestamp (in seconds granularity). */ public void hit(int timestamp) { int cnt = map.getOrDefault(timestamp, 0); map.put(timestamp, cnt+1); } /** Return the number of hits in the past 5 minutes. @param timestamp - The current timestamp (in seconds granularity). */ public int getHits(int timestamp) { int ans = 0; for(Integer v : map.tailMap(timestamp - 299).values()) ans += v; return ans; } }
- Queue; Each hit as an element; Not scalable
public class HitCounter { Queue<Integer> q = null; /** Initialize your data structure here. */ public HitCounter() { q = new LinkedList<Integer>(); } /** Record a hit. @param timestamp - The current timestamp (in seconds granularity). */ public void hit(int timestamp) { q.offer(timestamp); } /** Return the number of hits in the past 5 minutes. @param timestamp - The current timestamp (in seconds granularity). */ public int getHits(int timestamp) { while(!q.isEmpty() && timestamp - q.peek() >= 300) { q.poll(); } return q.size(); } }
-
1129. Shortest Path with Alternating Colors
1129. Shortest Path with Alternating Colors
Consider a directed graph, with nodes labelled 0, 1, ..., n-1. In this graph, each edge is either red or blue, and there could be self-edges or parallel edges. Each [i, j] in red_edges denotes a red directed edge from node i to node j. Similarly, each [i, j] in blue_edges denotes a blue directed edge from node i to node j. Return an array answer of length n, where each answer[X] is the length of the shortest path from node 0 to node X such that the edge colors alternate along the path (or -1 if such a path doesn't exist). Example 1: Input: n = 3, red_edges = [[0,1],[1,2]], blue_edges = [] Output: [0,1,-1]
public static final int RED = 0, BLUE = 1; public int[] shortestAlternatingPaths(int n, int[][] red_edges, int[][] blue_edges) { int[] color = new int[n], result = new int[n]; Arrays.fill(result, -1); result[0] = 0; HashSet<Integer>[][] graph = new HashSet[2][n]; for(int i = 0; i < n; i++) { graph[RED][i] = new HashSet<>(); graph[BLUE][i] = new HashSet<>(); } for(int[] e : red_edges) graph[RED][e[0]].add(e[1]); for(int[] e : blue_edges) graph[BLUE][e[0]].add(e[1]); search(result, RED, graph); for(int[] e : red_edges) graph[RED][e[0]].add(e[1]); for(int[] e : blue_edges) graph[BLUE][e[0]].add(e[1]); search(result, BLUE, graph); return result; } public void search(int[] result, int color, HashSet<Integer>[][] graph) { ArrayDeque<Integer> q = new ArrayDeque<>(); q.offer(0); int step = 0; while(!q.isEmpty()) { int size = q.size(); step++; for(int k = 0; k < size; k++) { int from = q.poll(); for(int to : graph[color][from]) { q.offer(to); result[to] = result[to] == -1 ? step : Math.min(step, result[to]); } graph[color][from] = new HashSet<>(); } color = color == RED ? BLUE : RED; } } }
-
236. Lowest Common Ancestor of a Binary Tree
236. Lowest Common Ancestor of a Binary Tree
class Solution { public Result search(TreeNode root, TreeNode p, TreeNode q) { if(root == null) return new Result(); Result left = search(root.left, p, q); if(left.ancestor != null) return left; if(left.p && root == q || left.q && root == p) return new Result(root, true, true); Result right = search(root.right, p, q); if(right.ancestor != null) return right; if(right.p && root == q || right.q && root == p) return new Result(root, true, true); if(left.q && right.p || left.p && right.q) return new Result(root, true, true); return new Result(null, left.p || right.p || root == p, left.q || right.q || root == q); } public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { Result r = search(root, p, q); return r.ancestor; } class Result { TreeNode ancestor; boolean p; boolean q; public Result() {} public Result(TreeNode n, boolean p, boolean q) { this.ancestor = n; this.p = p; this.q = q; } } }
-
158. Read N Characters Given Read4 II - Call multiple times
158. Read N Characters Given Read4 II - Call multiple times
Given a file and assume that you can only read the file using a given method read4, implement a method read to read n characters. Your method read may be called multiple times. Method read4: The API read4 reads 4 consecutive characters from the file, then writes those characters into the buffer array buf. The return value is the number of actual characters read. Note that read4() has its own file pointer, much like FILE *fp in C. Definition of read4: Parameter: char[] buf Returns: int Note: buf[] is destination not source, the results from read4 will be copied to buf[] Below is a high level example of how read4 works: File file("abcdefghijk"); // File is "abcdefghijk", initially file pointer (fp) points to 'a' char[] buf = new char[4]; // Create buffer with enough space to store characters read4(buf); // read4 returns 4. Now buf = "abcd", fp points to 'e' read4(buf); // read4 returns 4. Now buf = "efgh", fp points to 'i' read4(buf); // read4 returns 3. Now buf = "ijk", fp points to end of file Method read: By using the read4 method, implement the method read that reads n characters from the file and store it in the buffer array buf. Consider that you cannot manipulate the file directly. The return value is the number of actual characters read. Definition of read: Parameters: char[] buf, int n Returns: int Note: buf[] is destination not source, you will need to write the results to buf[] Example 1: File file("abc"); Solution sol; // Assume buf is allocated and guaranteed to have enough space for storing all characters from the file. sol.read(buf, 1); // After calling your read method, buf should contain "a". We read a total of 1 character from the file, so return 1. sol.read(buf, 2); // Now buf should contain "bc". We read a total of 2 characters from the file, so return 2. sol.read(buf, 1); // We have reached the end of file, no more characters can be read. So return 0. Example 2: File file("abc"); Solution sol; sol.read(buf, 4); // After calling your read method, buf should contain "abc". We read a total of 3 characters from the file, so return 3. sol.read(buf, 1); // We have reached the end of file, no more characters can be read. So return 0. Note: Consider that you cannot manipulate the file directly, the file is only accesible for read4 but not for read. The read function may be called multiple times. Please remember to RESET your class variables declared in Solution, as static/class variables are persisted across multiple test cases. Please see here for more details. You may assume the destination buffer array, buf, is guaranteed to have enough space for storing n characters. It is guaranteed that in a given test case the same buffer buf is called by read.
- Consume the buffer array first. If it runs out, break, else refill the buffer.
public class Solution extends Reader4 { char[] buffer = new char[4]; int len = 0; int p = 0; public int read(char[] buf, int n) { int cnt = 0; int i = 0; while(cnt < n) { while(p < len && cnt < n) { buf[i++] = buffer[p++]; cnt++; } if(p < len) break; int temp = read4(buffer); if(temp == 0) break; len = temp; p = 0; } return cnt; } }
public class Solution extends Reader4 { char[] buffer = new char[4]; int cnt = 0; int bp = 0; public int read(char[] buf, int n) { int p = 0; while(p < n) { if(bp == 0) cnt = read4(buffer); if(cnt == 0) break; while(bp < cnt && p < n) { buf[p++] = buffer[bp++]; } if(bp == cnt) bp = 0; } return p; } }
-
432. All O`one Data Structure
Implement a data structure supporting the following operations: Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string. Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string. GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "". GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "". Challenge: Perform all these in O(1) time complexity.
- HashMap + DoublyLinkedList
class AllOne { HashMap<String, Integer> map = new HashMap<>(); HashMap<Integer, Node> v2k = new HashMap<>(); Node head, tail; public AllOne() {} public void inc(String key) { // System.out.println("++" + key); if(!map.containsKey(key)) { Node nh = v2k.getOrDefault(1, new Node(1)); v2k.put(1, nh); nh.keys.add(key); if(head == null) { head = nh; tail = head; } else if(head != nh) { nh.next = head; head.prev = nh; head = nh; } } else { int v = map.get(key); Node curr = v2k.get(v), nn = v2k.getOrDefault(v+1, new Node(v+1)); v2k.put(v+1, nn); nn.keys.add(key); curr.keys.remove(key); if(nn != curr.next) { nn.next = curr.next; if(curr.next != null) curr.next.prev = nn; curr.next = nn; nn.prev = curr; } if(curr == tail) tail = nn; if(curr.keys.isEmpty()) { v2k.remove(curr.v); if(curr == head) { head = nn; nn.prev = null; } else { curr.prev.next = nn; nn.prev = curr.prev; } curr.next = null; curr.prev = null; } } map.put(key, map.getOrDefault(key, 0)+1); // print(); } public void dec(String key) { if(!map.containsKey(key)) return; // System.out.println("--" + key); int v = map.get(key); Node curr = v2k.get(v); curr.keys.remove(key); if(v == 1) { map.remove(key); if(curr.keys.isEmpty()) { v2k.remove(v); head = curr.next; if(curr == tail) tail = null; curr.prev = null; curr.next = null; } } else { map.put(key, v-1); Node np = v2k.getOrDefault(v-1, new Node(v-1)); v2k.put(v-1, np); np.keys.add(key); np.next = curr; if(curr == head) { head = np; } else if(np != curr.prev) { curr.prev.next = np; np.prev = curr.prev; } curr.prev = np; if(curr.keys.isEmpty()) { v2k.remove(v); if(curr == head) { head = np; } if(curr == tail) { tail = np; } else { np.next = curr.next; curr.next.prev = np; } curr.next = null; curr.prev = null; } } // print(); } public String getMinKey() { if(head == null) return ""; return head.keys.iterator().next(); } public String getMaxKey() { if(tail == null) return ""; return tail.keys.iterator().next(); } class Node { Node prev; Node next; int v; HashSet<String> keys = new HashSet<String>(); public Node(int v) { this.v = v; } public String toString() { return keys.toString(); } } public void print() { Node curr = head; System.out.println("v2k" + v2k); System.out.println("h=" + (curr == null ? "null" : head.keys) + " t=" + (tail == null ? "null" : tail.keys)); while(curr != null) { System.out.println(curr.v + " ->" + curr.keys); curr = curr.next; } System.out.println(); } }
- TreeMap nlogn
class AllOne { HashMap<String, Integer> map = new HashMap<>(); TreeMap<Integer, HashSet<String>> v2k = new TreeMap<>(); int min; int max; public AllOne() { } /** Inserts a new key <Key> with value 1. Or increments an existing key by 1. */ public void inc(String key) { map.put(key, map.getOrDefault(key, 0)+1); int v = map.get(key); if(!v2k.containsKey(v)) v2k.put(v, new HashSet<String>()); v2k.get(v).add(key); if(v > 1) { v2k.get(v-1).remove(key); if(v2k.get(v-1).isEmpty()) v2k.remove(v-1); } max = Math.max(max, v); if(v-1 == min && !v2k.containsKey(v-1)) min++; if(v-1 == 0) min = 1; } /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */ public void dec(String key) { if(!map.containsKey(key)) return; map.put(key, map.get(key)-1); int v = map.get(key); v2k.get(v+1).remove(key); if(v2k.get(v+1).isEmpty()) v2k.remove(v+1); if(v == 0) map.remove(key); if(!v2k.containsKey(v) && v != 0) v2k.put(v, new HashSet<String>()); if(v != 0) v2k.get(v).add(key); if(v2k.isEmpty()) { max = 0; min = 0; } else { if(v+1 == max && !v2k.containsKey(v+1)) max--; if(v == 0) { min = v2k.ceilingKey(1); } else if(v+1 == min && !v2k.containsKey(v+1)) { min--; } } } /** Returns one of the keys with maximal value. */ public String getMaxKey() { if(v2k.get(max) == null || v2k.get(max).isEmpty()) return ""; return v2k.get(max).iterator().next(); } /** Returns one of the keys with Minimal value. */ public String getMinKey() { if(v2k.get(min) == null || v2k.get(min).isEmpty()) return ""; return v2k.get(min).iterator().next(); } }
-
489. Robot Room Cleaner
Given a robot cleaner in a room modeled as a grid. Each cell in the grid can be empty or blocked. The robot cleaner with 4 given APIs can move forward, turn left or turn right. Each turn it made is 90 degrees. When it tries to move into a blocked cell, its bumper sensor detects the obstacle and it stays on the current cell. Design an algorithm to clean the entire room using only the 4 given APIs shown below. interface Robot { // returns true if next cell is open and robot moves into the cell. // returns false if next cell is obstacle and robot stays on the current cell. boolean move(); // Robot will stay on the same cell after calling turnLeft/turnRight. // Each turn will be 90 degrees. void turnLeft(); void turnRight(); // Clean the current cell. void clean(); } Example: Input: room = [ [1,1,1,1,1,0,1,1], [1,1,1,1,1,0,1,1], [1,0,1,1,1,1,1,1], [0,0,0,1,0,0,0,0], [1,1,1,1,1,1,1,1] ], row = 1, col = 3 Explanation: All grids in the room are marked by either 0 or 1. 0 means the cell is blocked, while 1 means the cell is accessible. The robot initially starts at the position of row=1, col=3. From the top left corner, its position is one row below and three columns right. Notes: The input is only given to initialize the room and the robot's position internally. You must solve this problem "blindfolded". In other words, you must control the robot using only the mentioned 4 APIs, without knowing the room layout and the initial robot's position. The robot's initial position will always be in an accessible cell. The initial direction of the robot will be facing up. All accessible cells are connected, which means the all cells marked as 1 will be accessible by the robot. Assume all four edges of the grid are all surrounded by wall.
int[] xs = {-1, 0, 1, 0}; int[] ys = {0, 1, 0, -1}; public void cleanRoom(Robot robot) { backtrack(robot, 0, 0, new HashSet<String>(), 0); } public void backtrack(Robot robot, int i, int j, HashSet<String> visited, int dir) { visited.add(i+","+j); robot.clean(); for(int p = 0; p < 4; p++) { int d = (dir + p) % 4; int x = i + xs[d], y = j + ys[d]; if(!visited.contains(x+","+y) && robot.move()) { backtrack(robot, x, y, visited, d); robot.turnRight(); // turn 180 to come back robot.turnRight(); robot.move(); robot.turnRight(); // turn to init direction robot.turnRight(); } robot.turnRight(); // next direction } }
-
57. Insert Interval
Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). You may assume that the intervals were initially sorted according to their start times. Example 1: Input: intervals = [[1,3],[6,9]], newInterval = [2,5] Output: [[1,5],[6,9]] Example 2: Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] Output: [[1,2],[3,10],[12,16]] Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10].
public int[][] insert(int[][] intervals, int[] newInterval) { List<int[]> result = new ArrayList<>(); int i = 0; while(i < intervals.length && intervals[i][1] < newInterval[0]) { result.add(intervals[i++]); } result.add(newInterval); while(i < intervals.length && intervals[i][0] <= result.get(result.size()-1)[1]) { int[] pre = result.get(result.size()-1), curr = intervals[i]; pre[1] = Math.max(pre[1], curr[1]); pre[0] = Math.min(pre[0], curr[0]); i++; } while(i < intervals.length) result.add(intervals[i++]); int[][] lists = new int[result.size()][2]; for(int j = 0; j < lists.length; j++) lists[j] = result.get(j); return lists; }
-
51. N-Queens
The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other. Given an integer n, return all distinct solutions to the n-queens puzzle. Each solution contains a distinct board configuration of the n-queens' placement, where 'Q' and '.' both indicate a queen and an empty space respectively.
Example: Input: 4 Output: [ [".Q..", // Solution 1 "...Q", "Q...", "..Q."], ["..Q.", // Solution 2 "Q...", "...Q", ".Q.."] ] Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above.
- Backtracking
public List<List<String>> solveNQueens(int n) { List<List<String>> result = new ArrayList<>(); HashSet<Integer> cols = new HashSet<>(); StringBuilder sb = new StringBuilder(); LinkedList<int[]> qs = new LinkedList<>(); for(int i = 0; i < n * n; i++) sb.append('.'); backtrack(result, cols, qs, sb, n, 0); return result; } public void backtrack(List<List<String>> result, HashSet<Integer> cols, LinkedList<int[]> qs, StringBuilder sb, int n, int row) { out:for(int i = 0; i < n; i++) { if(cols.contains(i)) continue; int sum = row + i, diff = row - i; for(int[] q : qs) { if(q[0] + q[1] == sum || q[0] - q[1] == diff) continue out; } cols.add(i); qs.add(new int[] {row, i}); sb.setCharAt(row*n+i, 'Q'); if(row == n-1) { ArrayList<String> temp = new ArrayList<>(); for(int j = 0; j < n; j++) { temp.add(sb.substring(j*n, j*n+n)); } result.add(temp); } backtrack(result, cols, qs, sb, n, row+1); qs.removeLast(); sb.setCharAt(row*n+i, '.'); cols.remove(i); }
-
212. Word Search II
Given a 2D board and a list of words from the dictionary, find all words in the board. Each word must be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word. Example: Input: board = [ ['o','a','a','n'], ['e','t','a','e'], ['i','h','k','r'], ['i','f','l','v'] ] words = ["oath","pea","eat","rain"] Output: ["eat","oath"]
- Tier
int[] xs = {0,0,1,-1}, ys = {1,-1,0,0}; public List<String> findWords(char[][] board, String[] words) { Node root = new Node(); // root represents empty string "" for(String w : words) { Node curr = root; for(int i = 0; i < w.length(); i++) { if(curr.arr[w.charAt(i)-'a'] == null) curr.arr[w.charAt(i)-'a'] = new Node(); curr = curr.arr[w.charAt(i)-'a']; } curr.w = w; } List<String> result = new ArrayList<>(); for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { backtrack(result, board, i, j, root); } } return result; } public void backtrack(List<String> result, char[][] board, int i, int j, Node curr) { char c = board[i][j]; if(curr.arr[c-'a'] == null) return; if(curr.arr[c-'a'].w != null) { result.add(curr.arr[c-'a'].w); curr.arr[c-'a'].w = null; } board[i][j] = '*'; for(int k = 0; k < 4; k++) { int x = i + xs[k], y = j + ys[k]; if(x >= 0 && y >= 0 && x < board.length && y < board[0].length && board[x][y] != '*') backtrack(result, board, x, y, curr.arr[c-'a']); } board[i][j] = c; } class Node { Node[] arr = new Node[26]; String w; }
-
125. Valid Palindrome
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. Note: For the purpose of this problem, we define empty string as valid palindrome. Example 1: Input: "A man, a plan, a canal: Panama" Output: true Example 2: Input: "race a car" Output: false
public boolean isPalindrome(String s) { int l = 0, r = s.length()-1; while(l < r) { while(l < r && !Character.isLetter(s.charAt(l)) && !Character.isDigit(s.charAt(l))) { l++; } while(l < r && !Character.isLetter(s.charAt(r)) && !Character.isDigit(s.charAt(r))) { r--; } if(l < r) { if(Character.toLowerCase(s.charAt(l)) != Character.toLowerCase(s.charAt(r)) ) return false; l++; r--; } } return true; }
-
449. Serialize and Deserialize BST
449. Serialize and Deserialize BST
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary search tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary search tree can be serialized to a string and this string can be deserialized to the original tree structure. The encoded string should be as compact as possible. Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless.
- DFS
public class Codec { public String serialize(TreeNode root) { if(root == null) return "null"; return "" + root.val + "," + serialize(root.left) + "," + serialize(root.right); } public TreeNode deserialize(String data) { String[] arr = data.split(","); LinkedList<String> list = new LinkedList<>(Arrays.asList(arr)); return dfs(list); } public TreeNode dfs(LinkedList<String> list) { String s = list.get(0); list.removeFirst(); if(s.equals("null")) return null; TreeNode root = new TreeNode(Integer.valueOf(s)); root.left = dfs(list); root.right = dfs(list); return root; } }
- BFS
public String serialize(TreeNode root) { Queue<TreeNode> q = new LinkedList<>(); q.offer(root); String r = ""; while(q.size() != 0) { TreeNode curr = q.poll(); r += (curr == null ? "null" : curr.val) + ","; if(curr == null) continue; q.offer(curr.left); q.offer(curr.right); } return r.substring(0, r.length()-1); } // Decodes your encoded data to tree. public TreeNode deserialize(String data) { if(data.equals("null")) return null; // System.out.println("data=" + data); String[] arr = data.split(","); Queue<TreeNode> q = new LinkedList<>(); int i = 0; TreeNode root = new TreeNode(Integer.valueOf(arr[i++])); q.offer(root); while(q.size() != 0) { TreeNode curr = q.poll(); String l = arr[i++], r = arr[i++]; // System.out.println("curr=" + curr.val + " l="+l + " r=" + r); if(!l.equals("null")) { curr.left = new TreeNode(Integer.valueOf(l)); q.offer(curr.left); } if(!r.equals("null")) { curr.right = new TreeNode(Integer.valueOf(r)); q.offer(curr.right); } } return root; }
-
34. Find First and Last Position of Element in Sorted Array
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 target value. 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]
- BinarySearch
public int[] searchRange(int[] nums, int target) { int l = 0, r = nums.length; while(l < r) { int mid = l + (r-l) / 2; if(nums[mid] < target) l = mid + 1; else { r = mid; } } if(l == nums.length || nums[l] != target) return new int[] {-1,-1}; r = l; while(r < nums.length && nums[r] == nums[l]) r++; return new int[] {l, r-1}; }
-
341. Flatten Nested List Iterator
341. Flatten Nested List Iterator
Given a nested list of integers, implement an iterator to flatten it. Each element is either an integer, or a list -- whose elements may also be integers or other lists. Example 1: Input: [[1,1],2,[1,1]] Output: [1,1,2,1,1] Explanation: By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,1,2,1,1].
- Stack
public class NestedIterator implements Iterator<Integer> { Stack<NestedInteger> stack; public NestedIterator(List<NestedInteger> nestedList) { stack = new Stack<>(); for(int i = nestedList.size()-1; i >= 0; i--) { stack.push(nestedList.get(i)); } } public Integer next() { return stack.pop().getInteger(); } public boolean hasNext() { if(stack.isEmpty()) return false; if(!stack.peek().isInteger()) { List<NestedInteger> nestedList = stack.pop().getList(); for(int i = nestedList.size()-1; i >= 0; i--) { stack.push(nestedList.get(i)); } hasNext(); // recursive flatten lists } return !stack.isEmpty(); // may flatten empty nested lists } }
- Stack
-
692. Top K Frequent Words
Given a non-empty list of words, return the k most frequent elements. Your answer should be sorted by frequency from highest to lowest. If two words have the same frequency, then the word with the lower alphabetical order comes first. Example 1: Input: ["i", "love", "leetcode", "i", "love", "coding"], k = 2 Output: ["i", "love"] Explanation: "i" and "love" are the two most frequent words. Note that "i" comes before "love" due to a lower alphabetical order.
- Sorting
public List<String> topKFrequentSort(String[] words, int k) { HashMap<String, Integer> map = new HashMap<>(); for(String s : words) { if(!map.containsKey(s)) map.put(s, 0); map.put(s, map.get(s)+1); } ArrayList<Map.Entry<String,Integer>> arr = new ArrayList<>(map.entrySet()); Collections.sort(arr, (x,y)->{ int r = Integer.compare(y.getValue(), x.getValue()); if(r == 0) return x.getKey().compareTo(y.getKey()); return r; }); ArrayList<String> result = new ArrayList<>(); for(int i = 0; i < k; i++) result.add(arr.get(i).getKey()); return result; }
- PriorityQueue
public List<String> topKFrequent(String[] words, int k) { HashMap<String, Integer> map = new HashMap<>(); for(String s : words) { map.put(s, map.getOrDefault(s, 0)+1); } PriorityQueue<Map.Entry<String, Integer>> q = new PriorityQueue<>((x,y)->{ int r = Integer.compare(x.getValue(), y.getValue()); if(r == 0) return y.getKey().compareTo(x.getKey()); return r; }); for(Map.Entry<String, Integer> e : map.entrySet()) { if(q.size() < k) q.offer(e); else if(q.peek().getValue() < e.getValue() || q.peek().getValue() == e.getValue() && q.peek().getKey().compareTo(e.getKey()) > 0) { q.poll(); q.offer(e); } } LinkedList<String> result = new LinkedList<>(); while(!q.isEmpty()) { result.addFirst(q.poll().getKey()); } return result; }
-
348. Design Tic-Tac-Toe
Design a Tic-tac-toe game that is played between two players on a n x n grid. You may assume the following rules: A move is guaranteed to be valid and is placed on an empty block. Once a winning condition is reached, no more moves is allowed. A player who succeeds in placing n of their marks in a horizontal, vertical, or diagonal row wins the game. Example: Given n = 3, assume that player 1 is "X" and player 2 is "O" in the board. TicTacToe toe = new TicTacToe(3); toe.move(0, 0, 1); -> Returns 0 (no one wins) |X| | | | | | | // Player 1 makes a move at (0, 0). | | | | toe.move(0, 2, 2); -> Returns 0 (no one wins) |X| |O| | | | | // Player 2 makes a move at (0, 2). | | | | toe.move(2, 2, 1); -> Returns 0 (no one wins) |X| |O| | | | | // Player 1 makes a move at (2, 2). | | |X| toe.move(1, 1, 2); -> Returns 0 (no one wins) |X| |O| | |O| | // Player 2 makes a move at (1, 1). | | |X| toe.move(2, 0, 1); -> Returns 0 (no one wins) |X| |O| | |O| | // Player 1 makes a move at (2, 0). |X| |X| toe.move(1, 0, 2); -> Returns 0 (no one wins) |X| |O| |O|O| | // Player 2 makes a move at (1, 0). |X| |X| toe.move(2, 1, 1); -> Returns 1 (player 1 wins) |X| |O| |O|O| | // Player 1 makes a move at (2, 1). |X|X|X|
class TicTacToe { int n; int[][] cnt = new int[2][n]; int d1, d2; public TicTacToe(int n) { this.n = n; cnt[0] = new int[n]; cnt[1] = new int[n]; } /** Player {player} makes a move at ({row}, {col}). @param row The row of the board. @param col The column of the board. @param player The player, can be either 1 or 2. @return The current winning condition, can be either: 0: No one wins. 1: Player 1 wins. 2: Player 2 wins. */ public int move(int row, int col, int player) { int step = player == 1 ? 1 : -1; cnt[0][row] += step; cnt[1][col] += step; if(row == col) d1 += step; if(row + col == n-1) d2 += step; if(Math.abs(cnt[0][row]) == n || Math.abs(cnt[1][col]) == n || Math.abs(d1) == n || Math.abs(d2) == n) { return player; } return 0; } }
-
207. Course Schedule
There are a total of n courses you have to take, labeled from 0 to n-1. Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? Example 1: Input: 2, [[1,0]] Output: true Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible. Example 2: Input: 2, [[1,0],[0,1]] Output: false Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.
public boolean canFinish(int n, int[][] ps) { int[] in = new int[n]; HashSet<Integer>[] out = new HashSet[n]; for(int i = 0; i < n; i++) out[i] = new HashSet<Integer>(); for(int[] p : ps) { in[p[0]]++; out[p[1]].add(p[0]); } ArrayDeque<Integer> q = new ArrayDeque<>(); for(int i = 0; i < n; i++) if(in[i] == 0) q.offer(i); int cnt = 0; while(!q.isEmpty()) { int i = q.poll(); System.out.print(i); cnt++; for(int j : out[i]) { in[j]--; if(in[j] == 0) q.offer(j); } } return cnt == n; }
-
45. Jump Game II
Given an array of non-negative integers, you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. Your goal is to reach the last index in the minimum number of jumps. Example: Input: [2,3,1,1,4] Output: 2 Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to the last index.
public int jump(int[] nums) { if(nums.length <= 1) return 0; int i = 0, frontier = 0, step = -1; while(i < nums.length) { int newFrontier = -1; while(i <= frontier && i < nums.length) { newFrontier = Math.max(newFrontier, i+nums[i]); i++; } frontier = newFrontier; step++; } return step; }
-
843. Guess the Word
This problem is an interactive problem new to the LeetCode platform. We are given a word list of unique words, each word is 6 letters long, and one word in this list is chosen as secret. You may call master.guess(word) to guess a word. The guessed word should have type string and must be from the original list with 6 lowercase letters. This function returns an integer type, representing the number of exact matches (value and position) of your guess to the secret word. Also, if your guess is not in the given wordlist, it will return -1 instead. For each test case, you have 10 guesses to guess the word. At the end of any number of calls, if you have made 10 or less calls to master.guess and at least one of these guesses was the secret, you pass the testcase. Besides the example test case below, there will be 5 additional test cases, each with 100 words in the word list. The letters of each word in those testcases were chosen independently at random from 'a' to 'z', such that every word in the given word lists is unique. Example 1: Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"] Explanation: master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist. master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 matches. master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches. master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches. master.guess("abcczz") returns 4, because "abcczz" has 4 matches. We made 5 calls to master.guess and one of them was the secret, so we pass the test case.
Random guess (Pass)
public void findSecretWord(String[] wordlist, Master master) { int n = Math.min(10, wordlist.length); int[] result = new int[n]; String[] candidates = new String[n]; int i = 0; int p = 0; boolean[] visited = new boolean[wordlist.length]; Random r = new Random(); while(true) { i = r.nextInt(wordlist.length); if(visited[i]) continue; visited[i] = true; if(!isCandidate(wordlist[i], result, candidates)) { continue; } int guess = master.guess(wordlist[i]); if(guess == 6) break; if(guess > 0) { candidates[p] = wordlist[i]; result[p++] = guess; } } } public boolean isCandidate(String s, int[] result, String[] candidates) { for(int i = 0; i < result.length; i++) { if(candidates[i] == null) break; if(countMatch(candidates[i], s) != result[i]) return false; } return true; } public int countMatch(String x, String y) { int cnt = 0; for(int i = 0; i < x.length(); i++) { if(x.charAt(i) == y.charAt(i)) cnt++; } return cnt; } }
A better minmax solution.
class Solution { int N = 6; public void findSecretWord(String[] words, Master master) { Set<Integer> options = new HashSet<>(); for (int i = 0; i < words.length; i++) options.add(i); while (options.size() > 0) { int min = Integer.MAX_VALUE; int minIdx = -1; for (int i : options) { int max = maxLoss(i, words, options); System.out.println(words[i] + " " + max); if (max < min) { min = max; minIdx = i; } } int match = master.guess(words[minIdx]); //made the correct guess, return if (match == N) return; Set<Integer> next = new HashSet<>(); for (int i : options) { if (similarity(words[minIdx], words[i]) == match) { next.add(i); } } options = next; } } private int maxLoss(int wordIdx, String[] words, Set<Integer> options) { int[] bucket = new int[N]; int maxLoss = 0; for (int i : options) { if (!words[wordIdx].equals(words[i])) { int sim = similarity(words[wordIdx], words[i]); bucket[sim]++; maxLoss = Math.max(maxLoss, bucket[sim]); } } return maxLoss; } private int similarity(String s1, String s2) { int match = 0; for (int i = 0; i < N; i++) { match += s1.charAt(i) == s2.charAt(i) ? 1 : 0; } return match; } }
-
126. Word Ladder II
Given two words (beginWord and endWord), and a dictionary's word list, find all shortest transformation sequence(s) from beginWord to endWord, such that: Only one letter can be changed at a time Each transformed word must exist in the word list. Note that beginWord is not a transformed word. Note: Return an empty list if there is no such transformation sequence. All words have the same length. All words contain only lowercase alphabetic characters. You may assume no duplicates in the word list. You may assume beginWord and endWord are non-empty and are not the same. Example 1: Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: [ ["hit","hot","dot","dog","cog"], ["hit","hot","lot","log","cog"] ]
HashMap<String, HashSet<String>> dict = new HashMap<>(); public List<List<String>> findLadders(String beginWord, String endWord, List<String> wordList) { wordList.add(beginWord); for(String w : wordList) { StringBuilder sb = new StringBuilder(w); for(int i = 0; i < w.length(); i++) { sb.setCharAt(i, '*'); String key = sb.toString(); if(!dict.containsKey(key)) dict.put(key, new HashSet<String>()); dict.get(key).add(w); sb.setCharAt(i, w.charAt(i)); } } ArrayList<HashSet<String>> arr = new ArrayList<>(); ArrayDeque<String> q = new ArrayDeque<>(); q.offer(beginWord); HashSet<String> visited = new HashSet<>(); visited.add(beginWord); boolean find = false; while(!q.isEmpty()) { HashSet<String> currLevel = new HashSet<String>(); int size = q.size(); for(int j = 0; j < size; j++) { String w = q.poll(); visited.add(w); StringBuilder sb = new StringBuilder(w); for(int i = 0; i < w.length(); i++) { sb.setCharAt(i, '*'); String key = sb.toString(); if(dict.containsKey(key)) { for(String next : dict.get(key)) { if(next.equals(endWord)) find = true; if(!visited.contains(next)) { q.offer(next); currLevel.add(next); } } } sb.setCharAt(i, w.charAt(i)); } } if(find) break; arr.add(currLevel); } List<List<String>> result = new ArrayList<List<String>>(); dfs(result, arr, new LinkedList<String>(), new HashSet<String>(), arr.size()-1, beginWord, endWord); return result; } public void dfs(List<List<String>> result, ArrayList<HashSet<String>> arr, LinkedList<String> temp, HashSet<String> visited, int level, String endWord, String w) { visited.add(w); temp.add(w); StringBuilder sb = new StringBuilder(w); for(int i = 0; i < w.length(); i++) { sb.setCharAt(i, '*'); String key = sb.toString(); if(dict.containsKey(key)) { for(String next : dict.get(key)) { if(next.equals(endWord)) { temp.add(endWord); ArrayList<String> toAdd = new ArrayList<String>(temp); Collections.reverse(toAdd); result.add(toAdd); temp.remove(temp.size()-1); } else if(!visited.contains(next) && level >= 0 && arr.get(level).contains(next)) { dfs(result, arr, temp, visited, level-1, endWord, next); } } } sb.setCharAt(i, w.charAt(i)); } temp.remove(temp.size()-1); visited.remove(w); }
-
460. LFU Cache
Design and implement a data structure for Least Frequently Used (LFU) cache. It should support the following operations: get and put. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. put(key, value) - Set or insert the value if the key is not already present. When the cache reaches its capacity, it should invalidate the least frequently used item before inserting a new item. For the purpose of this problem, when there is a tie (i.e., two or more keys that have the same frequency), the least recently used key would be evicted. Follow up: Could you do both operations in O(1) time complexity? Example: LFUCache cache = new LFUCache( 2 /* capacity */ ); cache.put(1, 1); cache.put(2, 2); cache.get(1); // returns 1 cache.put(3, 3); // evicts key 2 cache.get(2); // returns -1 (not found) cache.get(3); // returns 3. cache.put(4, 4); // evicts key 1. cache.get(1); // returns -1 (not found) cache.get(3); // returns 3 cache.get(4); // returns 4
class LFUCache { int capacity; int min; int size; HashMap<Integer, Integer> map; HashMap<Integer, Integer> k2f; HashMap<Integer, LinkedHashSet<Integer>> f2k; public LFUCache(int capacity){ this.capacity = capacity; map = new HashMap<>(); k2f = new HashMap<>(); f2k = new HashMap<>(); f2k.put(1, new LinkedHashSet<>()); } public int get(int key) { if(!map.containsKey(key)) return -1; int f = k2f.get(key); k2f.put(key, f+1); f2k.get(f).remove(key); f2k.put(f+1, f2k.getOrDefault(f+1, new LinkedHashSet<>())); f2k.get(f+1).add(key); if(f2k.get(min).isEmpty()) { min++; } return map.get(key); } public void put(int key, int val) { if(capacity == 0) return; if(map.containsKey(key)) { map.put(key, val); get(key); } else { map.put(key, val); k2f.put(key, 1); f2k.get(1).add(key); size++; if(capacity < size) { Iterator<Integer> it = f2k.get(min).iterator(); int removed = it.next(); map.remove(removed); k2f.remove(removed); it.remove(); size--; } min = 1; } } }
-
239. Sliding Window Maximum
Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window. Example: Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3 Output: [3,3,5,5,6,7] Explanation: Window position Max ------------------------- ----- [1 3 -1] -3 5 3 6 7 3 1 [3 -1 -3] 5 3 6 7 3 1 3 [-1 -3 5] 3 6 7 5 1 3 -1 [-3 5 3] 6 7 5 1 3 -1 -3 [5 3 6] 7 6 1 3 -1 -3 5 [3 6 7] 7
public int[] maxSlidingWindowDP(int[] nums, int k) { if(nums.length == 0 || k == 0) return new int[0]; int[] result = new int[nums.length - k + 1]; int[] leftMax = new int[nums.length], rightMax = new int[nums.length]; for(int i = 0; i < leftMax.length; i++) { int pre = i % k == 0 || i == 0 ? Integer.MIN_VALUE : leftMax[i-1]; leftMax[i] = Math.max(nums[i], pre); } for(int i = rightMax.length-1; i >= 0; i--) { int pre = i % k == k-1 || i == rightMax.length-1 ? Integer.MIN_VALUE : rightMax[i+1]; rightMax[i] = Math.max(nums[i], pre); } for(int i = 0; i < result.length; i++) result[i] = Math.max(rightMax[i], leftMax[i+k-1]); return result; }
public int[] maxSlidingWindowDeQueue(int[] nums, int k) { if(nums.length == 0) return new int[0]; ArrayDeque<int[]> q = new ArrayDeque<>(); int[] result = new int[nums.length - k + 1]; for(int i = 0; i < nums.length; i++) { if(!q.isEmpty() && q.peek()[0] <= i - k) q.poll(); while(!q.isEmpty() && q.peekLast()[1] <= nums[i]) q.removeLast(); q.offer(new int[]{i, nums[i]}); if(i-k+1 >= 0) result[i-k+1] = q.peek()[1]; } return result; }
-
283. Move Zeroes
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative order of the non-zero elements. Example: Input: [0,1,0,3,12] Output: [1,3,12,0,0] Note: You must do this in-place without making a copy of the array. Minimize the total number of operations.
public void moveZeroes(int[] nums) { int l = 0, r = 0; // l -> left most 0 // r -> the first none 0 starting from l while(r < nums.length) { while(l < nums.length && nums[l] != 0) l++; r = l; while(r < nums.length && nums[r] == 0) r++; if(r < nums.length) swap(nums, l, r); } }
public void moveZeroes(int[] nums) { if (nums == null || nums.length == 0) return; int insertPos = 0; for (int num: nums) { if (num != 0) nums[insertPos++] = num; } while (insertPos < nums.length) { nums[insertPos++] = 0; } }
-
124. Binary Tree Maximum Path Sum
124. Binary Tree Maximum Path Sum
Given a non-empty binary tree, find the maximum path sum. For this problem, a path is defined as any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The path must contain at least one node and does not need to go through the root. Example 1: Input: [1,2,3] 1 / \ 2 3 Output: 6
int result = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) { traverse(root); return result; } public int traverse(TreeNode root) { if(root == null) return 0; int curr = root.val; int left = traverse(root.left), right = traverse(root.right); int temp = Math.max(curr, Math.max(curr+left, curr+right)); result = Math.max(result, Math.max(temp, curr+left+right)); return temp; }
-
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] ]
public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> result = new ArrayList<>(); backtrack(0, target, candidates, 0, new ArrayList<Integer>(), result); return result; } public void backtrack(int curr, int target, int[] candidates, int index, ArrayList<Integer> temp, List<List<Integer>> result) { if(curr >= target) { if(curr == target) result.add(new ArrayList(temp)); return; } for(int i = index; i < candidates.length; i++) { temp.add(candidates[i]); backtrack(curr + candidates[i], target, candidates, i, temp, result); temp.remove(temp.size()-1); } }
-
6. ZigZag Conversion
The string "PAYPALISHIRING" is written in a zigzag pattern on a given number of rows like this: (you may want to display this pattern in a fixed font for better legibility) P A H N A P L S I I G Y I R And then read line by line: "PAHNAPLSIIGYIR" Write the code that will take a string and make this conversion given a number of rows: string convert(string s, int numRows); Example 1: Input: s = "PAYPALISHIRING", numRows = 3 Output: "PAHNAPLSIIGYIR"
public String convert(String s, int numRows) { if(numRows == 1) return s; int row = 0, step = -1; String[] result = new String[numRows]; Arrays.fill(result, ""); for(char c : s.toCharArray()) { if(row == 0 || row == numRows-1) step = -step; result[row] += c; row += step; } return String.join("", result); }
- Pure math
class Solution { public String convert(String s, int numRows) { //如果是有一行,可以直接返回 if (numRows == 1) { return s; } StringBuilder result = new StringBuilder(); //运用数学规律,逐行取值 for (int i = 0; i < numRows; i++) { int j = 0;//表示第i行的第j个数 while (j + i < s.length()) { result.append(s.charAt(j + i));//j每次循环时,首先取j+i坐标上的字母 //如果不是第一排或者最后一排,一般情况下,每两个竖排间会有两个字母,第二个字母的规律是j+numRows * 2 - 2 - i if (i != 0 && i != numRows - 1 && j + numRows * 2 - 2 - i < s.length()) { result.append(s.charAt(j + numRows * 2 - 2 - i)); } //第一竖排和第二竖排的坐标差值为numRows * 2 - 2 j += numRows * 2 - 2; } } return result.toString(); } }
-
829. Consecutive Numbers Sum
Given a positive integer N, how many ways can we write it as a sum of consecutive positive integers? Example 1: Input: 5 Output: 2 Explanation: 5 = 5 = 2 + 3
public int consecutiveNumbersSum(int N) { int result = 0; for (int k = 1; k * (k + 1) <= 2 * N; k++) { if ((2* N - k * (k + 1)) % (2*k) == 0) result++; } return result; }
-
364. Nested List Weight Sum II
364. Nested List Weight Sum II
Given a nested list of integers, return the sum of all integers in the list weighted by their depth. Each element is either an integer, or a list -- whose elements may also be integers or other lists. Different from the previous question where weight is increasing from root to leaf, now the weight is defined from bottom up. i.e., the leaf level integers have weight 1, and the root level integers have the largest weight. Example 1: Input: [[1,1],2,[1,1]] Output: 8 Explanation: Four 1's at depth 1, one 2 at depth 2.
See Also: nested-list-weight-sum
public int depthSumInverse(List<NestedInteger> nestedList) { int h = getHeight(nestedList); return count(nestedList, h); } public int count(List<NestedInteger> nestedList, int level) { int sum = 0; for(NestedInteger n : nestedList) sum += n.isInteger() ? n.getInteger() * level : count(n.getList(), level - 1); return sum; } public int getHeight(List<NestedInteger> nestedList) { int h = 0; for(NestedInteger n : nestedList) if(!n.isInteger()) h = Math.max(h, getHeight(n.getList())); return h + 1; }
-
149. Max Points on a Line
Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. Example 1: Input: [[1,1],[2,2],[3,3]] Output: 3 Explanation: ^ | | o | o | o +-------------> 0 1 2 3 4
public int maxPoints(int[][] points) { int max = 0; for(int i = 0; i < points.length; i++) { HashMap<String, Integer> map = new HashMap<>(); int temp = 0, overlap = 0; for(int j = 0; j < points.length; j++) { int[] a = points[i], b = points[j]; int dx = a[0] - b[0], dy = a[1] - b[1]; if(dx == 0 && dy == 0) overlap++; else { int gcd = generateGcd(dx, dy); dx /= gcd; dy /= gcd; String slope = dy + "/" + dx; map.put(slope, map.getOrDefault(slope, 0) + 1); } } for(Integer v : map.values()) temp = Math.max(v, temp); max = Math.max(max, temp + overlap); } return max; } public int generateGcd(int x, int y) { if (y == 0) return x; return generateGcd(y, x % y); }
Another version; Does not pass all test cases.
public int maxPointsFailed(int[][] points) { HashMap<String, HashSet<int[]>> map = new HashMap<>(); for(int i = 0; i < points.length; i++) { for(int j = i + 1; j < points.length; j++) { int[] a = points[i], b = points[j]; int dx = a[0] - b[0], dy = a[1] - b[1]; HashSet<int[]> set = null; String key = ""; if(dx == 0) { key += a[0]; } else { double slope = (double)dy / (double)dx; double yIntercept = a[1] - slope * a[0]; key += yIntercept + "," + slope; } set = map.getOrDefault(key, new HashSet<>()); set.add(a); set.add(b); map.put(key, set); } } int max = points.length > 0 ? 1 : 0; String m = ""; for(HashSet<int[]> set : map.values()) { max = Math.max(set.size(), max); } return max; }
-
819. Most Common Word
Given a paragraph and a list of banned words, return the most frequent word that is not in the list of banned words. It is guaranteed there is at least one word that isn't banned, and that the answer is unique. Words in the list of banned words are given in lowercase, and free of punctuation. Words in the paragraph are not case sensitive. The answer is in lowercase. Example: Input: paragraph = "Bob hit a ball, the hit BALL flew far after it was hit." banned = ["hit"] Output: "ball" Explanation: "hit" occurs 3 times, but it is a banned word. "ball" occurs twice (and no other word does), so it is the most frequent non-banned word in the paragraph. Note that words in the paragraph are not case sensitive, that punctuation is ignored (even if adjacent to words, such as "ball,"), and that "hit" isn't the answer even though it occurs more because it is banned.
public String mostCommonWord(String paragraph, String[] banned) { HashSet<String> set = new HashSet<>(Arrays.asList(banned)); HashMap<String, Integer> counter = new HashMap<>(); int i = 0, j = 0; while(i < paragraph.length()) { while(i < paragraph.length() && !Character.isLetter(paragraph.charAt(i))) i++; j = i; while(i < paragraph.length() && Character.isLetter(paragraph.charAt(i))) i++; String curr = paragraph.substring(j, i).toLowerCase(); if(i == j || set.contains(curr)) continue; counter.put(curr, counter.getOrDefault(curr, 0) + 1); } int max = 0; String result = ""; for(Map.Entry<String, Integer> e : counter.entrySet()) { if(e.getValue() > max) { max = e.getValue(); result = e.getKey(); } } return result; }
Or preprocess with regular expression
public String mostCommonWordOld(String paragraph, String[] banned) { HashSet<String> set = new HashSet<>(); for(String w : banned) set.add(w); paragraph = paragraph.replaceAll("[^a-zA-Z]", " ").toLowerCase(); String[] words = paragraph.split("\\s+"); HashMap<String, Integer> map = new HashMap<>(); for(String w : words) { if(set.contains(w)) continue; int v = map.getOrDefault(w, 0); map.put(w, v+1); } int max = 0; String r = null; for(Map.Entry<String,Integer> e : map.entrySet()) { int v = e.getValue(); if(max < v) { max = v; r = e.getKey(); } } return r; }
-
12. Integer to Roman
see also 12. Integer to Roman
Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 V 5 X 10 L 50 C 100 D 500 M 1000 For example, two is written as II in Roman numeral, just two one's added together. Twelve is written as, XII, which is simply X + II. The number twenty seven is written as XXVII, which is XX + V + II. Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: I can be placed before V (5) and X (10) to make 4 and 9. X can be placed before L (50) and C (100) to make 40 and 90. C can be placed before D (500) and M (1000) to make 400 and 900. Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 to 3999. Example 1: Input: "III" Output: 3 Example 2: Input: "IV" Output: 4
public String intToRoman(int n) { int[] arr = new int[] {1000,900,500,400,100,90,50,40,10,9,5,4,1}; String[] val = new String[] {"M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"}; int p = 0; String result = ""; while(n > 0) { while(arr[p] > n) p++; result += val[p]; n -= arr[p]; } return result; }
-
67. Add Binary
Given two binary strings, return their sum (also a binary string). The input strings are both non-empty and contains only characters 1 or 0. Example 1: Input: a = "11", b = "1" Output: "100" Example 2: Input: a = "1010", b = "1011" Output: "10101"
The same with adding two linkedlist: 2. Add Two Numbers
public String addBinary(String a, String b) { int carry = 0; int x = a.length()-1, y = b.length()-1; StringBuilder sb = new StringBuilder(); while(x >= 0 || y >= 0) { int i = (x >= 0 && a.charAt(x) == '1') ? 1 : 0; int j = (y >= 0 && b.charAt(y) == '1') ? 1 : 0; int sum = i + j + carry; sb.insert(0, sum % 2); carry = sum / 2; x--; y--; } if(carry == 1) sb.insert(0, 1); return sb.toString(); }
-
103. Binary Tree Zigzag Level Order Traversal
103. Binary Tree Zigzag Level Order Traversal
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). For example: Given binary tree [3,9,20,null,null,15,7], 3 / \ 9 20 / \ 15 7 return its zigzag level order traversal as: [ [3], [20,9], [15,7] ]
public List<List<Integer>> zigzagLevelOrder(TreeNode root) { List<List<Integer>> result = new ArrayList<>(); if(root == null) return result; ArrayDeque<TreeNode> q = new ArrayDeque<>(); q.offer(root); int level = 0; while(!q.isEmpty()) { int size = q.size(); level++; LinkedList<Integer> temp = new LinkedList<>(); boolean isReverse = level % 2 == 0; for(int i = 0; i < size; i++) { TreeNode curr = q.poll(); if(isReverse) temp.addFirst(curr.val); else temp.add(curr.val); if(curr.left != null) q.offer(curr.left); if(curr.right != null) q.offer(curr.right); } result.add(temp); } return result; }
-
986. Interval List Intersections
986. Interval List Intersections
Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order. Return the intersection of these two interval lists. (Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval. For example, the intersection of [1, 3] and [2, 4] is [2, 3].) Example 1: Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists.
Note: 0 <= A.length < 1000 0 <= B.length < 1000 0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9
public int[][] intervalIntersection(int[][] A, int[][] B) { ArrayList<int[]> arr = new ArrayList<>(); int pa = 0, pb = 0; while(pa < A.length && pb < B.length) { int[] a = A[pa], b = B[pb]; if(a[1] < b[0]) pa++; else if(b[1] < a[0]) pb++; else { int l = a[0] > b[0] ? a[0] : b[0]; int r = a[1] < b[1] ? a[1] : b[1]; arr.add(new int[] {l, r}); if(r == a[1]) { pa++; } else { pb++; } } } int[][] result = new int[arr.size()][]; int p = 0; for(int[] pair : arr) result[p++] = pair; return result; }
or
public int[][] intervalIntersection(int[][] A, int[][] B) { ArrayList<int[]> arr = new ArrayList<>(); if(A.length == 0 || B.length == 0) return new int[0][]; int[] curr = A[0], next = B[0]; int i = 0; for(int[] a : A) { while(i < B.length) { int[] b = B[i]; if((a[0] >= b[0] && a[0] <= b[1]) || (b[0] >= a[0] && b[0] <= a[1])) { arr.add(new int[] {Math.max(a[0],b[0]), Math.min(a[1],b[1])}); } if(b[1] > a[1]) break; i++; } } int[][] result = new int[arr.size()][]; for(int j = 0; j < result.length; j++) result[j] = arr.get(j); return result; }
-
528. Random Pick with Weight
Given an array w of positive integers, where w[i] describes the weight of index i, write a function pickIndex which randomly picks an index in proportion to its weight. Note: 1 <= w.length <= 10000 1 <= w[i] <= 10^5 pickIndex will be called at most 10000 times. Example 1: Input: ["Solution","pickIndex"] [[[1]],[]] Output: [null,0]
class Solution { int[] arr; Random random = new Random(); public Solution(int[] w) { arr = new int[w.length]; arr[0] = w[0]; for(int i = 1; i < arr.length; i++) arr[i] = arr[i-1] + w[i]; } public int pickIndex() { int target = 1 + random.nextInt(arr[arr.length-1]); int l = 0, r = arr.length; while(l < r) { int mid = l + (r-l) / 2; if(arr[mid] >= target) r = mid; else l = mid + 1; } return l; } }
-
974. Subarray Sums Divisible by K
974. Subarray Sums Divisible by K
Given an array A of integers, return the number of (contiguous, non-empty) subarrays that have a sum divisible by K. Example 1: Input: A = [4,5,0,-2,-3,1], K = 5 Output: 7 Explanation: There are 7 subarrays with a sum divisible by K = 5: [4, 5, 0, -2, -3, 1], [5], [5, 0], [5, 0, -2, -3], [0], [0, -2, -3], [-2, -3]
- HashMap
public int subarraysDivByK(int[] A, int K) { int result = 0, sum = 0; HashMap<Integer, Integer> map = new HashMap<>(); map.put(0, 1); for(int i : A) { sum += i; int residual = (sum % K + K) % K; int preCnt = map.getOrDefault(residual, 0); result += preCnt; map.put(residual, preCnt + 1); } return result; }
-
98. Validate Binary Search Tree
98. Validate Binary Search Tree
Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. Example 1: 2 / \ 1 3 Input: [2,1,3] Output: true
- Recursion
```java
public boolean isValidBST(TreeNode root) { return check(root, null, null); }
public boolean check(TreeNode root, Integer min, Integer max) { // if current node valid if(root == null) return true; if(min != null && root.val <= min) return false; if(max != null && root.val >= max) return false; // if children valid return check(root.left, min, root.val) && check(root.right, root.val, max); }```
-
981. Time Based Key-Value Store
981. Time Based Key-Value Store
Create a timebased key-value store class TimeMap, that supports two operations. 1. set(string key, string value, int timestamp) Stores the key and value, along with the given timestamp. 2. get(string key, int timestamp) Returns a value such that set(key, value, timestamp_prev) was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it returns the one with the largest timestamp_prev. If there are no values, it returns the empty string ("").
- BinarySearch
HashMap<String, ArrayList<Node>> map; /** Initialize your data structure here. */ public TimeMap() { map = new HashMap<>(); } public void set(String key, String value, int timestamp) { ArrayList<Node> arr = map.getOrDefault(key, new ArrayList<>()); arr.add(new Node(timestamp, value)); map.put(key, arr); } public String get(String key, int timestamp) { if(!map.containsKey(key)) return ""; return binarySearch(map.get(key), timestamp); } // find the first element less than or equal to target // convert to find the first element greater than the target, then minus one from the index public String binarySearch(ArrayList<Node> arr, int target) { int l = 0, r = arr.size(); while(l < r) { int mid = (l+r) / 2; if(arr.get(mid).k <= target) l = mid + 1; else r = mid; } r--; if(r == -1) return ""; return arr.get(r).v; } class Node { int k; String v; public Node(int k, String v) { this.k = k; this.v = v; } }
- TreeMap
class TimeMap { HashMap<String, TreeMap<Integer, String>> map; public TimeMap() { map = new HashMap<>(); } public String get(String key, int timestamp) { if(!map.containsKey(key)) return ""; else { TreeMap<Integer, String> tree = map.get(key); Map.Entry<Integer, String> e = tree.floorEntry(timestamp); return e == null ? "" : e.getValue(); } } public void set(String key, String value, int timestamp) { TreeMap<Integer, String> tree = map.getOrDefault(key, new TreeMap<>()); tree.put(timestamp, value); map.put(key, tree); } }
-
347. Top K Frequent Elements
Given a non-empty array of integers, return the k most frequent elements. Example 1: Input: nums = [1,1,1,2,2,3], k = 2 Output: [1,2] Example 2: Input: nums = [1], k = 1 Output: [1] Note: You may assume k is always valid, 1 ≤ k ≤ number of unique elements. Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
public List<Integer> topKFrequent(int[] nums, int k) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i : nums) { int v = map.getOrDefault(i, 0); map.put(i, v+1); } PriorityQueue<Integer> q = new PriorityQueue<>((x,y) -> Integer.compare(map.get(x), map.get(y))); for(int key : map.keySet()) { if(q.size() < k) q.offer(key); else if(map.get(q.peek()) < map.get(key)) { q.poll(); q.offer(key); } } return new ArrayList<>(q); }
-
621. Task Scheduler
Given a char array representing tasks CPU need to do. It contains capital letters A to Z where different letters represent different tasks. Tasks could be done without original order. Each task could be done in one interval. For each interval, CPU could finish one task or just be idle. However, there is a non-negative cooling interval n that means between two same tasks, there must be at least n intervals that CPU are doing different tasks or just be idle. You need to return the least number of intervals the CPU will take to finish all the given tasks. Example: Input: tasks = ["A","A","A","B","B","B"], n = 2 Output: 8 Explanation: A -> B -> idle -> A -> B -> idle -> A -> B.
The result only related to the frequency of each character.
- PriorityQueue with Node help class
The Node class can be removed in the next solution.
public int leastInterval(char[] tasks, int n) { PriorityQueue<Node> q = new PriorityQueue<>(new Comparator<Node>(){ public int compare(Node x, Node y) { return Integer.compare(y.cnt, x.cnt); } }); int[] cnts = new int[26]; for(char c : tasks) cnts[c - 'A']++; for(int i = 0; i < cnts.length; i++) if(cnts[i] > 0) q.offer(new Node((char)(i+'A'), cnts[i])); int result = 0; while(true) { int intervals = 0; int size = q.size(); LinkedList<Node> temp = new LinkedList<>(); while(intervals < size && intervals < n+1) { Node curr = q.poll(); intervals++; if(curr.cnt > 1) { curr.cnt--; temp.add(curr); } } for(Node node : temp) q.offer(node); result += q.isEmpty() ? size : n + 1; intervals = 0; if(q.isEmpty()) break; } return result; } class Node { char c; int cnt; public Node(char c, int cnt) {this.c = c; this.cnt = cnt;} }
- PriorityQueue
public int leastInterval(char[] tasks, int n) { PriorityQueue<Integer> q = new PriorityQueue<>(Collections.reverseOrder()); int[] cnts = new int[26]; for(char c : tasks) cnts[c - 'A']++; for(int i = 0; i < cnts.length; i++) if(cnts[i] > 0) q.offer(cnts[i]); int result = 0; while(true) { int intervals = 0; int size = q.size(); LinkedList<Integer> temp = new LinkedList<>(); while(intervals < size && intervals < n+1) { int curr = q.poll(); intervals++; if(curr > 1) temp.add(curr-1); } for(int num : temp) q.offer(num); result += q.isEmpty() ? size : n + 1; intervals = 0; if(q.isEmpty()) break; } return result; }
- Sort Array
public int leastInterval(char[] tasks, int n) { int[] cnts = new int[26]; for(char c : tasks) cnts[c - 'A']++; int intervals = 0, result = 0; Arrays.sort(cnts); while(true) { for(int i = 25; i >=0; i--) { if(cnts[i] > 0) { cnts[i]--; intervals++; } if(intervals == n+1) break; } Arrays.sort(cnts); if(cnts[25] == 0) { result += intervals; break; } else result += n+1; intervals = 0; } return result; }
- Hard to understand
public int leastInterval(char[] tasks, int n) { Integer[] arr = new Integer[26]; Arrays.fill(arr, 0); for(char c : tasks) arr[c -'A']++; Arrays.sort(arr); int longest = arr[25] - 1; int idle = longest * n; for(int i = 24; i >= 0 && arr[i] > 0; i--) { idle -= Math.min(longest, arr[i]); } // idle may be negative if there are many types of tasks with a short window return idle > 0 ? idle + tasks.length : tasks.length; }
-
155. Min Stack
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. push(x) -- Push element x onto stack. pop() -- Removes the element on top of the stack. top() -- Get the top element. getMin() -- Retrieve the minimum element in the stack.
class MinStack { Stack<Integer> mins = new Stack<>(); Stack<Integer> stack = new Stack<>(); public void push(int x) { if(mins.isEmpty()) mins.push(x); else mins.push(mins.peek() > x ? x : mins.peek()); stack.push(x); } public void pop() { mins.pop(); stack.pop(); } public int top() { return stack.peek(); } public int getMin() { return mins.peek(); } }
-
904. Fruit Into Baskets
Implement atoi which converts a string to an integer. The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. If no valid conversion could be performed, a zero value is returned. Note: Only the space character ' ' is considered as whitespace character. Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. If the numerical value is out of the range of representable values, INT_MAX (231 − 1) or INT_MIN (−231) is returned. Example 1: Input: "42" Output: 42 Example 2: Input: " -42" Output: -42 Explanation: The first non-whitespace character is '-', which is the minus sign. Then take as many numerical digits as possible, which gets 42.
public int myAtoi(String str) { double result = 0; int l = 0, r = 0, sign = 1; while(r < str.length() && str.charAt(r) == ' ') r++; if(r == str.length()) return 0; char first = str.charAt(r); if(first == '-' || first == '+') { if(first == '-') sign = -1; r++; } else if(!Character.isDigit(first)) { return 0; } l = r; // time to pick digits while(r < str.length() && Character.isDigit(str.charAt(r))) { r++; } // r stops at the first character that is not a digit String num = str.substring(l, r); if(num.length() != 0) result = Double.valueOf(num); result = result * sign; if(result > Integer.MAX_VALUE) result = Integer.MAX_VALUE; else if(result < Integer.MIN_VALUE) result = Integer.MIN_VALUE; return (int)result; }
-
340. Longest Substring with At Most K Distinct Characters
340. Longest Substring with At Most K Distinct Characters
Given a string, find the length of the longest substring T that contains at most k distinct characters. Example 1: Input: s = "eceba", k = 2 Output: 3 Explanation: T is "ece" which its length is 3.
public int lengthOfLongestSubstringKDistinct(String s, int k) { if(k == 0) return 0; HashMap<Character, Integer> map = new HashMap<>(); int l = 0, r = 0, result = 0; while(r < s.length()) { while(map.size() == k && !map.containsKey(s.charAt(r))) { int v = map.get(s.charAt(l)); if(v == 1) map.remove(s.charAt(l)); else map.put(s.charAt(l), v-1); l++; } map.put(s.charAt(r), map.getOrDefault(s.charAt(r), 0) + 1); result = Math.max(result, r - l + 1); r++; } return result; }
-
904. Fruit Into Baskets
In a row of trees, the i-th tree produces fruit with type tree[i]. You start at any tree of your choice, then repeatedly perform the following steps: Add one piece of fruit from this tree to your baskets. If you cannot, stop. Move to the next tree to the right of the current tree. If there is no tree to the right, stop. Note that you do not have any choice after the initial choice of starting tree: you must perform step 1, then step 2, then back to step 1, then step 2, and so on until you stop. You have two baskets, and each basket can carry any quantity of fruit, but you want each basket to only carry one type of fruit each. What is the total amount of fruit you can collect with this procedure? Example 1: Input: [1,2,1] Output: 3 Explanation: We can collect [1,2,1].
public int totalFruit(int[] arr) { int l = 0, r = 0, result = 0; HashMap<Integer, Integer> map = new HashMap<>(); while(r < arr.length) { while(map.size() == 2 && !map.containsKey(arr[r])) { int v = map.get(arr[l]); if(v == 1) map.remove(arr[l]); else map.put(arr[l], v-1); l++; } map.put(arr[r], map.getOrDefault(arr[r], 0)+1); result = Math.max(result, r - l + 1); r++; } return result; }
-
25. Reverse Nodes in k-Group
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list. k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is. Example: Given this linked list: 1->2->3->4->5 For k = 2, you should return: 2->1->4->3->5 For k = 3, you should return: 3->2->1->4->5 Note: Only constant extra memory is allowed. You may not alter the values in the list's nodes, only nodes itself may be changed.
- Iterative
public ListNode reverseKGroup(ListNode head, int k) { int len = 0; ListNode curr = head; while(curr != null) { len++; curr = curr.next; } ListNode sentinel = new ListNode(0), pre = sentinel; curr = head; pre.next = curr; for(int i = 0; i < len / k; i++) { for(int j = 0; j < k - 1; j++) { ListNode moved = curr.next; curr.next = moved.next; moved.next = pre.next; pre.next = moved; } pre = curr; curr = curr.next; } return sentinel.next; }
-
84. Largest Rectangle in Histogram
84. Largest Rectangle in Histogram
Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.
Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
The largest rectangle is shown in the shaded area, which has area = 10 unit.
- MonotonicStack N
public int largestRectangleArea(int[] h) { ArrayDeque<Integer> stack = new ArrayDeque<>(); stack.push(-1); int result = 0; for(int i = 0; i < h.length; i++) { while(stack.peek() != -1 && h[stack.peek()] > h[i]) { int height = h[stack.pop()]; result = Math.max(result, height * (i - 1 - stack.peek())); } stack.push(i); } while(stack.peek() != -1) { int height = h[stack.pop()]; result = Math.max(result, height * (h.length - 1 - stack.peek())); } return result; }
- divede and conquer NlogN
find the lowest bar, divide the histograms into left and right parts. result = max(loestHeight * widthOfTheHistogram, maxOfLeftPart, maxOfRightPart)
-
85. Maximal Rectangle
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area. Example: Input: [ ["1","0","1","0","0"], ["1","0","1","1","1"], ["1","1","1","1","1"], ["1","0","0","1","0"] ] Output: 6
- MonotonicStack
public int maximalRectangle(char[][] matrix) { if(matrix == null || matrix.length == 0 || matrix[0].length == 0) return 0; int[] h = new int[matrix[0].length+1]; int result = 0; for(int i = 0; i < matrix.length; i++) { if(i == 0) { for(int j = 0; j < matrix[0].length; j++) h[j] = matrix[i][j] == '1' ? 1 : 0; } else { for(int j = 0; j < matrix[0].length; j++) h[j] = matrix[i][j] == '1' ? h[j] + 1 : 0; } result = Math.max(result, largestRectangleArea(h)); } return result; } public int largestRectangleArea(int[] h) { ArrayDeque<Integer> stack = new ArrayDeque<>(); stack.push(-1); int result = 0; for(int i = 0; i < h.length; i++) { while(stack.peek() != -1 && h[stack.peek()] > h[i]) { int height = h[stack.pop()]; result = Math.max(result, height * (i - 1 - stack.peek())); } stack.push(i); } return result; }
- DP row by row, N^2 * M
public int maximalRectangleAccumulateRows(char[][] matrix) { if(matrix.length == 0 || matrix[0].length == 0) return 0; int result = Integer.MIN_VALUE; int[] temp = new int[matrix[0].length]; for(int i = 0; i < matrix.length; i++) { for(int j = i; j < matrix.length; j++) { if(j == i) { for(int k = 0; k < temp.length; k++) temp[k] = matrix[j][k] - '0'; } else { for(int k = 0; k < matrix[0].length; k++) { if(matrix[j][k] == '1' && temp[k] > 0) temp[k] += 1; else temp[k] = 0; } } int sum = 0; for(int k = 0; k < temp.length; k++) { if(k == 0 || temp[k] != temp[k-1]) sum = temp[k]; else sum += temp[k]; result = Math.max(sum, result); } } } return result; }
or
public int maximalRectangleReduceDimension(char[][] matrix) { if(matrix==null || matrix.length == 0 || matrix[0] == null || matrix[0].length==0) return 0; int n = matrix.length, m = matrix[0].length; int ans = 0; char[] buffer = new char[m]; Arrays.fill(buffer, '1'); for(int i = 0; i < n; i++) { for(int j = i; j < n; j++) { int cols = 0; for(int k = 0; k < m; k++) { buffer[k] = buffer[k] == '1' && matrix[j][k] == '1' ? '1' : '0'; } for(int k = 0; k < m; k++) { if(buffer[k] == '0') { cols = 0; } else { cols++; } ans = Math.max(ans, (j-i+1)*cols); } } Arrays.fill(buffer,'1'); } return ans; }
-
9. Palindrome Number
Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward. Example 1: Input: 121 Output: true Example 2: Input: -121 Output: false Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.
- Compare Half
public boolean isPalindrome(int x) { if(x < 0 || x > 0 && x % 10 == 0) return false; int re = 0; while(x > re) { re = re * 10 + x % 10; x /= 10; } return re == x || re / 10 == x; }
-
393. UTF-8 Validation
A character in UTF8 can be from 1 to 4 bytes long, subjected to the following rules:
For 1-byte character, the first bit is a 0, followed by its unicode code. For n-bytes character, the first n-bits are all one's, the n+1 bit is 0, followed by n-1 bytes with most significant 2 bits being 10.
This is how the UTF-8 encoding would work:
Char. number range | UTF-8 octet sequence (hexadecimal) | (binary) --------------------+--------------------------------------------- 0000 0000-0000 007F | 0xxxxxxx 0000 0080-0000 07FF | 110xxxxx 10xxxxxx 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
Given an array of integers representing the data, return whether it is a valid utf-8 encoding.
Note: The input is an array of integers. Only the least significant 8 bits of each integer is used to store the data. This means each integer represents only 1 byte of data.
Example 1:
data = [197, 130, 1], which represents the octet sequence: 11000101 10000010 00000001.
Return true. It is a valid utf-8 encoding for a 2-bytes character followed by a 1-byte character.
Example 2:
data = [235, 140, 4], which represented the octet sequence: 11101011 10001100 00000100.
Return false. The first 3 bits are all one’s and the 4th bit is 0 means it is a 3-bytes character. The next byte is a continuation byte which starts with 10 and that’s correct. But the second continuation byte does not start with 10, so it is invalid.
public boolean validUtf8(int[] data) { for(int j = 0; j < data.length; j++) { int first = data[j]; int trailing = data.length; if(first >> 7 == 0) { trailing = 0; } else if(first >> 3 == 30) { trailing = 3; } else if(first >> 4 == 14) { trailing = 2; } else if(first >> 5 == 6) { trailing = 1; } if(j + trailing >= data.length) return false; for(int i = j+1; i <= j + trailing; i++) { if(data[i] >> 6 != 2) return false; } j += trailing; } return true; }
-
68. Text Justification
Given an array of words and a width maxWidth, format the text such that each line has exactly maxWidth characters and is fully (left and right) justified. You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly maxWidth characters. Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. For the last line of text, it should be left justified and no extra space is inserted between words. Note: A word is defined as a character sequence consisting of non-space characters only. Each word's length is guaranteed to be greater than 0 and not exceed maxWidth. The input array words contains at least one word. Example 1: Input: words = ["This", "is", "an", "example", "of", "text", "justification."] maxWidth = 16 Output: [ "This is an", "example of text", "justification. " ] Example 2: Input: words = ["What","must","be","acknowledgment","shall","be"] maxWidth = 16 Output: [ "What must be", "acknowledgment ", "shall be " ] Explanation: Note that the last line is "shall be " instead of "shall be", because the last line must be left-justified instead of fully-justified. Note that the second line is also left-justified becase it contains only one word.
public List<String> fullJustify(String[] words, int max) { List<String> result = new ArrayList<>(); int len = 0; int j = 0; for(int i = 0; i < words.length; i++) { String w = words[i]; if(len + w.length() > max) { // give up current word, process words between j and i-1 (inclusive) // words j -> i-1 int wCnt = i - j; int spaceSlot = wCnt - 1; int remain = max - (len - 1) + spaceSlot; // number of empty spaces to be added String temp = ""; if(spaceSlot > 0) { // if multiple words in a line int spaceCnt = 0; while(spaceCnt < remain) { for(int k = j; k <= i-2; k++) { words[k] += " "; spaceCnt++; if(spaceCnt == remain) break; } } for(int k = j; k <= i-2; k++) temp += words[k]; } temp += words[i-1]; if(spaceSlot == 0) { // if only one word in this line, add trailing spaces for(int space = 0; space < remain; space++) temp += " "; } result.add(temp); j = i; i--; // give up current word len = 0; continue; } len += w.length() + 1; } String temp = ""; // process last line while(j < words.length - 1) temp += words[j++] + " "; temp += words[j]; int trailingSpace = max - len + 1; for(int i = 0; i < trailingSpace; i++) temp += " "; result.add(temp); return result; }
Anoter version
public List<String> fullJustifyOld(String[] words, int maxWidth) { List<String> result = new ArrayList<>(); StringBuilder sb = new StringBuilder(); int length = 0; for(int i = 0; i < words.length; i++) { String curr = words[i]; if(sb.length() == 0) sb.append(curr); else if(sb.length() + 1 + curr.length() <= maxWidth) { sb.append(" ").append(curr); } else { i--; String[] ws = sb.toString().split(" "); int slot = ws.length - 1; if(slot == 0) { int space = maxWidth - sb.length(); for(int j = 0; j < space; j++) sb.append(" "); result.add(sb.toString()); sb.setLength(0); continue; } int trail = maxWidth - sb.length(); int total = slot + trail; int between = total / slot; int residual = total % slot; sb.setLength(0); for(int j = 0; j < ws.length; j++) { sb.append(ws[j]); if(j == ws.length - 1) break; for(int k = 0; k < between; k++) sb.append(" "); if(residual-- > 0) sb.append(" "); } result.add(sb.toString()); sb.setLength(0); } } if(sb.length() > 0) { int space = maxWidth - sb.length(); for(int i = 0; i < space; i++) sb.append(" "); result.add(sb.toString()); } return result; }
-
642. Design Search Autocomplete System
642. Design Search Autocomplete System
Design a search autocomplete system for a search engine. Users may input a sentence (at least one word and end with a special character '#'). For each character they type except '#', you need to return the top 3 historical hot sentences that have prefix the same as the part of sentence already typed. Here are the specific rules: The hot degree for a sentence is defined as the number of times a user typed the exactly same sentence before. The returned top 3 hot sentences should be sorted by hot degree (The first is the hottest one). If several sentences have the same degree of hot, you need to use ASCII-code order (smaller one appears first). If less than 3 hot sentences exist, then just return as many as you can. When the input is a special character, it means the sentence ends, and in this case, you need to return an empty list. Your job is to implement the following functions: The constructor function: AutocompleteSystem(String[] sentences, int[] times): This is the constructor. The input is historical data. Sentences is a string array consists of previously typed sentences. Times is the corresponding times a sentence has been typed. Your system should record these historical data. Now, the user wants to input a new sentence. The following function will provide the next character the user types: List<String> input(char c): The input c is the next character typed by the user. The character will only be lower-case letters ('a' to 'z'), blank space (' ') or a special character ('#'). Also, the previously typed sentence should be recorded in your system. The output will be the top 3 historical hot sentences that have prefix the same as the part of sentence already typed. Example: Operation: AutocompleteSystem(["i love you", "island","ironman", "i love leetcode"], [5,3,2,2]) The system have already tracked down the following sentences and their corresponding times: "i love you" : 5 times "island" : 3 times "ironman" : 2 times "i love leetcode" : 2 times Now, the user begins another search: Operation: input('i') Output: ["i love you", "island","i love leetcode"] Explanation: There are four sentences that have prefix "i". Among them, "ironman" and "i love leetcode" have same hot degree. Since ' ' has ASCII code 32 and 'r' has ASCII code 114, "i love leetcode" should be in front of "ironman". Also we only need to output top 3 hot sentences, so "ironman" will be ignored. Operation: input(' ') Output: ["i love you","i love leetcode"] Explanation: There are only two sentences that have prefix "i ". Operation: input('a') Output: [] Explanation: There are no sentences that have prefix "i a". Operation: input('#') Output: [] Explanation: The user finished the input, the sentence "i a" should be saved as a historical sentence in system. And the following input will be counted as a new search. Note: The input sentence will always start with a letter and end with '#', and only one blank space will exist between two words. The number of complete sentences that to be searched won't exceed 100. The length of each sentence including those in the historical data won't exceed 100. Please use double-quote instead of single-quote when you write test cases even for a character input. Please remember to RESET your class variables declared in class AutocompleteSystem, as static/class variables are persisted across multiple test cases. Please see here for more details.
class AutocompleteSystem { Node root, curr; StringBuilder sb; public AutocompleteSystem(String[] sentences, int[] times) { root = new Node(); sb = new StringBuilder(); curr = root; for(int i = 0; i < sentences.length; i++) { for(char c : sentences[i].toCharArray()) { int index = c == ' ' ? 26 : c - 'a'; if(curr.arr[index] == null) curr.arr[index] = new Node(); curr = curr.arr[index]; } curr.cnt = times[i]; curr.s = sentences[i]; curr = root; } } public List<String> input(char ch) { LinkedList<String> result = new LinkedList<>(); if(ch == '#') { curr.cnt++; curr.s = sb.toString(); curr = root; sb.setLength(0); } else { sb.append(ch); int index = ch == ' ' ? 26 : ch - 'a'; if(curr.arr[index] == null) curr.arr[index] = new Node(); curr = curr.arr[index]; PriorityQueue<Node> pq = new PriorityQueue<>(new Comparator<Node>() { public int compare(Node x, Node y) { if(x.cnt != y.cnt) return Integer.compare(x.cnt, y.cnt); else return y.s.compareTo(x.s); } }); Queue<Node> q = new LinkedList<>(); q.offer(curr); while(!q.isEmpty()) { Node n = q.poll(); if(n.cnt > 0) { if(pq.size() < 3) pq.offer(n); else { pq.offer(n); pq.poll(); } } for(int i = 0; i <= 26; i++) if(n.arr[i] != null) q.offer(n.arr[i]); } while(!pq.isEmpty()) result.addFirst(pq.poll().s); } return result; } class Node { int cnt; Node[] arr = new Node[27]; String s; } }
-
332. Reconstruct Itinerary
Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK. Note: If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"]. All airports are represented by three capital letters (IATA code). You may assume all tickets form at least one valid itinerary. Example 1: Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] Output: ["JFK", "MUC", "LHR", "SFO", "SJC"]
Euler path. Another option to run faster but more space: HashMap<from, PriorityQueue<to1, to2…»
public List<String> findItinerary(List<List<String>> tickets) { Collections.sort(tickets, new Comparator<List<String>>() { public int compare(List<String> x, List<String> y) { return x.get(1).compareTo(y.get(1)); } }); LinkedList<String> result = new LinkedList<>(); dfs(result, "JFK", tickets, new boolean[tickets.size()]); return result; } public void dfs(LinkedList<String> result, String curr, List<List<String>> tickets, boolean[] used) { for(int i = 0; i < tickets.size(); i++) { if(!used[i] && tickets.get(i).get(0).equals(curr)) { used[i] = true; dfs(result, tickets.get(i).get(1), tickets, used); } } result.addFirst(curr); }
-
41. First Missing Positive
Given an unsorted integer array, find the smallest missing positive integer. Example 1: Input: [1,2,0] Output: 3 Example 2: Input: [3,4,-1,1] Output: 2 Example 3: Input: [7,8,9,11,12] Output: 1
Use indexs and nagetive value to store information.
public int firstMissingPositive(int[] nums) { boolean hasOne = false; for(int i = 0; i < nums.length; i++) { if(nums[i] == 1) hasOne = true; if(nums[i] <= 0) nums[i] = 1; } if(!hasOne) return 1; for(int i = 0; i < nums.length; i++) { if(nums[i] <= nums.length) { int index = Math.abs(nums[i]) - 1; // negative value represents a existed number in the array if(nums[index] > 0) nums[index] = -nums[index]; } } for(int i = 0; i < nums.length; i++) { if(nums[i] > 0) return i+1; } return nums.length+1; }
-
347. Top K Frequent Elements
Given a non-empty array of integers, return the k most frequent elements. Example 1: Input: nums = [1,1,1,2,2,3], k = 2 Output: [1,2] Example 2: Input: nums = [1], k = 1 Output: [1] Note: You may assume k is always valid, 1 ≤ k ≤ number of unique elements. Your algorithm's time complexity must be better than O(n log n), where n is the array's size.
public List<Integer> topKFrequent(int[] nums, int k) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i : nums) { int v = map.getOrDefault(i, 0); map.put(i, v+1); } PriorityQueue<Map.Entry<Integer, Integer>> q = new PriorityQueue<>(new Comparator<Map.Entry<Integer, Integer>>() { public int compare(Map.Entry<Integer, Integer> x, Map.Entry<Integer, Integer> y) { return Integer.compare(x.getValue(), y.getValue()); } }); for(Map.Entry<Integer, Integer> e : map.entrySet()) { if(q.size() < k) q.offer(e); else if(e.getValue() > q.peek().getValue()) { q.poll(); q.offer(e); } } List<Integer> result = new ArrayList<Integer>(); for(Map.Entry<Integer, Integer> e : q) result.add(e.getKey()); return result; }
-
139. Word Break
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words. Note: The same word in the dictionary may be reused multiple times in the segmentation. You may assume the dictionary does not contain duplicate words. Example 1: Input: s = "leetcode", wordDict = ["leet", "code"] Output: true Explanation: Return true because "leetcode" can be segmented as "leet code". Example 2: Input: s = "applepenapple", wordDict = ["apple", "pen"] Output: true Explanation: Return true because "applepenapple" can be segmented as "apple pen apple". Note that you are allowed to reuse a dictionary word.
- Bootom Up
public boolean wordBreak(String s, List<String> wordDict) { boolean[] dp = new boolean[s.length()+1]; HashSet<String> dict = new HashSet<String>(wordDict); dp[0] = true; for(int i = 1; i < dp.length; i++) { for(int j = 0; j < i; j++) { if(dp[j] && dict.contains(s.substring(j, i))) { dp[i] = true; break; } } } return dp[s.length()]; }
- Memorisation
public boolean wordBreak(String s, List<String> wordDict) { HashSet<String> dict = new HashSet<String>(wordDict); HashMap<String, Boolean> dp = new HashMap<>(); dp.put("", true); boolean result = recursive(s, dict, dp); return result; } public boolean recursive(String s, HashSet<String> dict, HashMap<String, Boolean> dp) { if(dp.containsKey(s)) return dp.get(s); for(int i = 0; i <= s.length(); i++) { if(dict.contains(s.substring(0,i))) { if(recursive(s.substring(i, s.length()), dict, dp)) { dp.put(s, true); return true; } } } dp.put(s, false); return false; }
-
127. Word Ladder
See also 126. Word Ladder 2
Given two words (beginWord and endWord), and a dictionary's word list, find the length of shortest transformation sequence from beginWord to endWord, such that: Only one letter can be changed at a time. Each transformed word must exist in the word list. Note that beginWord is not a transformed word. Note: Return 0 if there is no such transformation sequence. All words have the same length. All words contain only lowercase alphabetic characters. You may assume no duplicates in the word list. You may assume beginWord and endWord are non-empty and are not the same. Example 1: Input: beginWord = "hit", endWord = "cog", wordList = ["hot","dot","dog","lot","log","cog"] Output: 5 Explanation: As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", return its length 5.
“abc” -> “*ab”, “a*c”, “ab*”
public int ladderLength(String s, String e, List<String> ws) { HashMap<String, ArrayList<String>> map = new HashMap<>(); for(String w : ws) { for(int i = 0; i < w.length(); i++) { char[] arr = w.toCharArray(); char c = arr[i]; arr[i] = '*'; String starWord = new String(arr); ArrayList<String> nexts = map.getOrDefault(starWord, new ArrayList<String>()); nexts.add(w); map.put(starWord, nexts); arr[i] = c; } } Queue<String> q = new ArrayDeque<String>(); q.offer(s); int level = 0; int result = Integer.MAX_VALUE; HashSet<String> visited = new HashSet<>(); while(!q.isEmpty()) { int size = q.size(); level++; for(int k = 0; k < size; k++) { String w = q.poll(); for(int i = 0; i < w.length(); i++) { char[] arr = w.toCharArray(); char c = arr[i]; arr[i] = '*'; String starWord = new String(arr); if(!map.containsKey(starWord)) continue; ArrayList<String> nexts = map.get(starWord); for(String next : nexts) { if(next.equals(e)) return level + 1; if(!visited.contains(next)) { visited.add(next); q.offer(next); } } arr[i] = c; } } } return 0; }
-
609. Find Duplicate File in System
609. Find Duplicate File in System
Given a list of directory info including directory path, and all the files with contents in this directory, you need to find out all the groups of duplicate files in the file system in terms of their paths. A group of duplicate files consists of at least two files that have exactly the same content. A single directory info string in the input list has the following format: "root/d1/d2/.../dm f1.txt(f1_content) f2.txt(f2_content) ... fn.txt(fn_content)" It means there are n files (f1.txt, f2.txt ... fn.txt with content f1_content, f2_content ... fn_content, respectively) in directory root/d1/d2/.../dm. Note that n >= 1 and m >= 0. If m = 0, it means the directory is just the root directory. The output is a list of group of duplicate file paths. For each group, it contains all the file paths of the files that have the same content. A file path is a string that has the following format: "directory_path/file_name.txt" Example 1: Input: ["root/a 1.txt(abcd) 2.txt(efgh)", "root/c 3.txt(abcd)", "root/c/d 4.txt(efgh)", "root 4.txt(efgh)"] Output: [["root/a/2.txt","root/c/d/4.txt","root/4.txt"],["root/a/1.txt","root/c/3.txt"]]
public List<String> letterCombinations(String digits) { if(digits.length() == 0) return new ArrayList<>(); String[] dict = new String[10]; dict[2] = "abc"; dict[3] = "def"; dict[4] = "ghi"; dict[5] = "jkl"; dict[6] = "mno"; dict[7] = "pqrs"; dict[8] = "tuv"; dict[9] = "wxyz"; List<String> result = new ArrayList<>(); dfs(digits, result, dict, new StringBuilder(), 0); return result; } public void dfs(String digits, List<String> result, String[] dict, StringBuilder sb, int i) { if(i == digits.length()) { result.add(sb.toString()); } else { for(char c : dict[digits.charAt(i)-'0'].toCharArray()) { sb.append(c); dfs(digits, result, dict, sb, i+1); sb.setLength(sb.length()-1); } } }
-
17. Letter Combinations of a Phone Number
17. Letter Combinations of a Phone Number
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
Example:
Input: “23” Output: [“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
public List<String> letterCombinations(String digits) { if(digits.length() == 0) return new ArrayList<>(); String[] dict = new String[10]; dict[2] = "abc"; dict[3] = "def"; dict[4] = "ghi"; dict[5] = "jkl"; dict[6] = "mno"; dict[7] = "pqrs"; dict[8] = "tuv"; dict[9] = "wxyz"; List<String> result = new ArrayList<>(); dfs(digits, result, dict, new StringBuilder(), 0); return result; } public void dfs(String digits, List<String> result, String[] dict, StringBuilder sb, int i) { if(i == digits.length()) { result.add(sb.toString()); } else { for(char c : dict[digits.charAt(i)-'0'].toCharArray()) { sb.append(c); dfs(digits, result, dict, sb, i+1); sb.setLength(sb.length()-1); } } }
-
928. Minimize Malware Spread II
928. Minimize Malware Spread II
(This problem is the same as Minimize Malware Spread, with the differences bolded.) In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j] = 1. Some nodes initial are initially infected by malware. Whenever two nodes are directly connected and at least one of those two nodes is infected by malware, both nodes will be infected by malware. This spread of malware will continue until no more nodes can be infected in this manner. Suppose M(initial) is the final number of nodes infected with malware in the entire network, after the spread of malware stops. We will remove one node from the initial list, **completely removing it and any connections from this node to any other node.** Return the node that if removed, would minimize M(initial). If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index. In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j] = 1. Some nodes initial are initially infected by malware. Whenever two nodes are directly connected and at least one of those two nodes is infected by malware, both nodes will be infected by malware. This spread of malware will continue until no more nodes can be infected in this manner. Suppose M(initial) is the final number of nodes infected with malware in the entire network, after the spread of malware stops. Suppose M(initial) is the final number of nodes infected with malware in the entire network, after the spread of malware stops. We will remove one node from the initial list, **completely removing it and any connections from this node to any other node.** Return the node that if removed, would minimize M(initial). If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index. Example 1: Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] Output: 0 Example 2: Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2] Output: 0 Example 3: Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1] Output: 1
The key is to search on a graph with all bad nodes removed. Then count the bad neighbors of each group, and the group with a unique bad neighbors is a valid group. Add all valid groups of a bad node together. The bad node with the largest elements count is the result.
- Search BFS
public int minMalwareSpreadBFS(int[][] graph, int[] initial) { int[] eCnt = new int[graph.length]; HashSet<Integer> vSet = new HashSet<>(); for(int v : initial) vSet.add(v); boolean[] visited = new boolean[graph.length]; for(int i = 0; i < graph.length; i++) { if(visited[i] || vSet.contains(i)) continue; ArrayList<Integer> vs = new ArrayList<>(); Queue<Integer> q = new ArrayDeque<>(); int cnt = 0; q.offer(i); visited[i] = true; while(!q.isEmpty()) { int curr = q.poll(); if(vSet.contains(curr)) { vs.add(curr); continue; // don't add virus's neighbors } cnt++; for(int j = 0; j < graph.length; j++) { if(!visited[j] && graph[curr][j] == 1) { visited[j] = true; // both good and virus nodes are visited q.offer(j); } } } if(vs.size() == 1) eCnt[vs.get(0)] += cnt; // find a valid group for(int v : vs) // recover removed virus node (even with only one virus, // example: [[1,1,0,0,0],[1,1,1,1,1],[0,1,1,0,0],[0,1,0,1,0],[0,1,0,0,1]], [0,1]) visited[v] = false; } System.out.println(Arrays.toString(eCnt)); int result = graph.length; for(int i : initial) result = Math.min(result, i); for(int i = result; i < graph.length; i++) { if(eCnt[i] > eCnt[result]) result = i; } return result; }
- UnionFind
public int minMalwareSpread(int[][] graph, int[] initial) { UnionFind uf = new UnionFind(graph.length, initial); for(int i = 0; i < graph.length; i++) { for(int j = i + 1; j < graph.length; j++) { if(graph[i][j] == 1) uf.union(i, j); } } return uf.calculate(); } class UnionFind { int[] arr, eCnt, vIndex; HashSet<Integer>[] vs; HashSet<Integer> vSet = new HashSet<>(); public UnionFind(int n, int[] initial) { arr = new int[n]; eCnt = new int[n]; vs = new HashSet[n]; for(int v : initial) vSet.add(v); for(int i = 0; i < n; i++) { arr[i] = i; eCnt[i] = 1; vs[i] = new HashSet(); } } public int find(int x) { if(x != arr[x]) arr[x] = find(arr[x]); return arr[x]; } public void union(int x, int y) { int i = find(x), j = find(y); boolean vi = vSet.contains(i), vj = vSet.contains(j); if(vi && vj) return; // two virus else if(!vi && !vj && i != j) { arr[j] = i; eCnt[i] += eCnt[j]; for(int v : vs[j]) vs[i].add(v); } else if(vi) { // i is virus, j is not vs[j].add(i); } else if(vj) { vs[i].add(j); } } public int calculate() { HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < arr.length; i++) { if(arr[i] == i && vs[i] != null && vs[i].size() == 1) { Iterator it = vs[i].iterator(); int v = (int)it.next(); int cnt = map.getOrDefault(v, 0); map.put(v, cnt+eCnt[i]); } } int index = arr.length, cnt = 0; for(Map.Entry<Integer, Integer> e : map.entrySet()) { if(e.getValue() > cnt || (e.getValue() == cnt && e.getKey() < index)) { index = e.getKey(); cnt = e.getValue(); } } if(index < arr.length) return index; for(int v : vSet) index = Math.min(index, v); return index; }
-
924. Minimize Malware Spread
In a network of nodes, each node i is directly connected to another node j if and only if graph[i][j] = 1. Some nodes initial are initially infected by malware. Whenever two nodes are directly connected and at least one of those two nodes is infected by malware, both nodes will be infected by malware. This spread of malware will continue until no more nodes can be infected in this manner. Suppose M(initial) is the final number of nodes infected with malware in the entire network, after the spread of malware stops. We will remove one node from the initial list. Return the node that if removed, would minimize M(initial). If multiple nodes could be removed to minimize M(initial), return such a node with the smallest index. Note that if a node was removed from the initial list of infected nodes, it may still be infected later as a result of the malware spread. Example 1: Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1] Output: 0 Example 2: Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2] Output: 0 Example 3: Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1] Output: 1
- Search BFS
public int minMalwareSpread(int[][] graph, int[] initial) { HashSet<Integer> vs = new HashSet<>(); for(int v : initial) vs.add(v); int[] result = new int[graph.length]; boolean[] visited = new boolean[graph.length]; Arrays.fill(result, -1); for(int v : initial) { if(visited[v]) continue; Queue<Integer> q = new ArrayDeque<>(); q.offer(v); visited[v] = true; int cnt = 0; ArrayList<Integer> virus = new ArrayList<>(); while(!q.isEmpty()) { int curr = q.poll(); if(vs.contains(curr)) { virus.add(curr); } cnt++; for(int j = 0; j < graph.length; j++) { if(graph[curr][j] == 1 && !visited[j]) { q.offer(j); visited[j] = true; } } } if(virus.size() == 1) result[v] = cnt; } int i = graph.length; for(int index : initial) i = Math.min(index, i); int maxCnt = result[i]; for(int index : initial) { if(result[index] > maxCnt) { maxCnt = result[index]; i = index; } } return i; }
- UnionFind
public int minMalwareSpread(int[][] graph, int[] initial) { UnionFind uf = new UnionFind(graph.length, initial); for(int i = 0; i < graph.length; i++) { for(int j = i + 1; j < graph.length; j++) { if(graph[i][j] == 1) uf.union(i, j); } } return uf.calculate(); } class UnionFind { int[] arr; // the parent index of an element int[] vIndex; // -1 if no virus in this group, -2 if multiple virus, others are // virus index of the only virus in this group int[] eCnt; // element count of a group HashSet<Integer> virus = new HashSet<>(); public UnionFind(int n, int[] initial) { arr = new int[n]; vIndex = new int[n]; eCnt = new int[n]; Arrays.fill(vIndex, -1); for(int i : initial) { virus.add(i); vIndex[i] = i; // virus element itself is a group with a single virus } for(int i = 0; i < n; i++) { arr[i] = i; eCnt[i] = 1; } } public int find(int x) { if(x != arr[x]) arr[x] = find(arr[x]); return arr[x]; } public void union(int x, int y) { int i = find(x), j = find(y); if(i != j) { if(vIndex[j] == -2 || vIndex[i] == -2 || (vIndex[i] >= 0 && vIndex[j] >= 0)) vIndex[i] = -2; // -2 represents multiple virus in the combined group // this is caused by either of the two groups has multiple virus or each of them has only one virus else if(vIndex[j] >= 0) vIndex[i] = vIndex[j]; // only one virus in the two groups eCnt[i] += eCnt[j]; arr[j] = i; } } public int calculate() { int index = -1; for(int i = 0; i < arr.length; i++) { if(arr[i] == i && vIndex[i] >= 0) { if(index == -1) index = i; else index = eCnt[i] > eCnt[index] ? i : index; } } if(index != -1) return vIndex[index]; // find a group with only one virus int min = arr.length; for(int i : virus) min = Math.min(min, i); // return the virus with min index return min; } }
-
772. Basic Calculator III
Implement a basic calculator to evaluate a simple expression string. The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces . The expression string contains only non-negative integers, +, -, *, / operators , open ( and closing parentheses ) and empty spaces . The integer division should truncate toward zero. You may assume that the given expression is always valid. All intermediate results will be in the range of [-2147483648, 2147483647].
- General template Recursion
public int calculate(String s) { s = s.trim(); int result = 0, num = 0, temp = 0; char sign = '+'; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == ' ') continue; if(Character.isDigit(c)) num = 10 * num + c - '0'; if(c == '(') { int l = 0, r = 0; int start = i; while(i < s.length()) { if(s.charAt(i) == '(') l++; if(s.charAt(i) == ')') r++; if(l == r) break; i++; } num = calculate(s.substring(start + 1, i)); } if(c == '+' || c == '-' || c == '*' || c == '/' || i == s.length()-1) { switch(sign) { case '+' : temp += num; break; case '-' : temp -= num; break; case '*' : temp *= num; break; case '/' : temp /= num; break; } if(c == '+' || c == '-' || i == s.length() - 1) { result += temp; temp = 0; } sign = c; num = 0; } } return result; }
-
227. Basic Calculator II
Implement a basic calculator to evaluate a simple expression string. The expression string contains only non-negative integers, +, -, *, / operators and empty spaces . The integer division should truncate toward zero. Example 1: Input: "3+2*2" Output: 7
- Good Version
public int calculate(String s) { s = s.trim(); int result = 0, num = 0, temp = 0; char sign = '+'; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == ' ') continue; if(Character.isDigit(c)) num = 10 * num + c - '0'; if(c == '+' || c == '-' || c == '*' || c == '/' || i == s.length()-1) { switch(sign) { case '+' : temp += num; break; case '-' : temp -= num; break; case '*' : temp *= num; break; case '/' : temp /= num; break; } if(c == '+' || c == '-' || i == s.length() - 1) { result += temp; temp = 0; } sign = c; num = 0; } } return result; } }
- Verbose Version
public int calculateMyBadVersion(String s) { int result = 0, num = 0, temp = 1; char sign = '+'; Stack<Integer> stack = new Stack<>(); for(char c : s.toCharArray()) { if(c == ' ') continue; if(Character.isDigit(c)) num = num * 10 + (c - '0'); else { if(c == '+' || c == '-') { if(sign == '+' || sign == '-') { result += num * (sign == '+' ? 1 : -1); } else { if(sign == '*') temp *= num; if(sign == '/') temp /= num; result += temp; temp = 0; } } else if(c == '*' || c == '/') { if(sign == '+' || sign == '-') { temp = sign == '+' ? num : -num; } else { if(sign == '*') temp *= num; if(sign == '/') temp /= num; } } sign = c; num = 0; } } if(sign == '+' || sign == '-') { result += sign == '+' ? num : -num; } else { if(sign == '*') temp *= num; if(sign == '/') temp /= num; result += temp; } return result; }
-
224. Basic Calculator
Implement a basic calculator to evaluate a simple expression string. The expression string may contain open ( and closing parentheses ), the plus + or minus sign -, non-negative integers and empty spaces . Example 1: Input: "1 + 1" Output: 2
- Stack Version
public int calculate(String s) { Stack<Integer> stack = new Stack<>(); int num = 0, sign = 1, result = 0; int result = 0; for(char c : s.toCharArray()) { if(c == ' ') continue; if(Character.isDigit(c)) { num = num * 10 + (c - '0'); } else if(c == '(') { stack.push(sign); stack.push(result); result = 0; num = 0; sign = 1; } else if(c == ')') { result += sign * num; System.out.println(stack); result = stack.pop() + stack.pop() * result; // previous result + current result * sign num = 0; } else { result += sign * num; sign = c == '+' ? 1 : -1; num = 0; } } if(num != 0) result += sign * num; return result; }
- General template Recursion
public int calculate(String s) { s = s.trim(); int result = 0, num = 0, temp = 0; char sign = '+'; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(c == ' ') continue; if(Character.isDigit(c)) num = 10 * num + c - '0'; if(c == '(') { int l = 0, r = 0; int start = i; while(i < s.length()) { if(s.charAt(i) == '(') l++; if(s.charAt(i) == ')') r++; if(l == r) break; i++; } num = calculate(s.substring(start + 1, i)); } if(c == '+' || c == '-' || c == '*' || c == '/' || i == s.length()-1) { switch(sign) { case '+' : temp += num; break; case '-' : temp -= num; break; case '*' : temp *= num; break; case '/' : temp /= num; break; } if(c == '+' || c == '-' || i == s.length() - 1) { result += temp; temp = 0; } sign = c; num = 0; } } return result; }
-
380. Insert Delete GetRandom O(1)
380. Insert Delete GetRandom O(1)
Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note: The number of elements initialized in nums1 and nums2 are m and n respectively. You may assume that nums1 has enough space (size that is greater or equal to m + n) to hold additional elements from nums2. Example: Input: nums1 = [1,2,3,0,0,0], m = 3 nums2 = [2,5,6], n = 3 Output: [1,2,2,3,5,6]
- In place
public void merge(int[] nums1, int m, int[] nums2, int n) { System.arraycopy(nums1, 0, nums1, n, m); int i = 0, p = n, q = 0; while(p < nums1.length && q < nums2.length) nums1[i++] = nums1[p] < nums2[q] ? nums1[p++] : nums2[q++]; while(p < nums1.length) nums1[i++] = nums1[p++]; while(q < nums2.length) nums1[i++] = nums2[q++]; }
-
380. Insert Delete GetRandom O(1)
380. Insert Delete GetRandom O(1)
Given an array of integers nums, sort the array in ascending order. Design a data structure that supports all following operations in average O(1) time. insert(val): Inserts an item val to the set if not already present. remove(val): Removes an item val from the set if present. getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned.
class RandomizedSet { // RANDOM ACCESS --> ARRAY ARRAYLIST // O(1) INSERT REMOVE --> HASHMAP // MAP<VAL, INDEX>->ARRAYLIST // ARRAYLIST<INDEX->VALUE> -> MAP HashMap<Integer, Integer> v2i = new HashMap<>(); ArrayList<Integer> vs = new ArrayList<>(); Random r = new Random(); /** Initialize your data structure here. */ public RandomizedSet() { } /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ public boolean insert(int val) { if(v2i.containsKey(val)) return false; v2i.put(val, vs.size()); vs.add(val); return true; } /** Removes a value from the set. Returns true if the set contained the specified element. */ public boolean remove(int val) { if(!v2i.containsKey(val)) return false; int i = v2i.remove(val); int lastV = vs.get(vs.size()-1); if(i != vs.size()-1) v2i.put(lastV, i); // rmove the only element in the array, don't update the map vs.set(i, lastV); vs.remove(vs.size()-1); return true; } /** Get a random element from the set. */ public int getRandom() { int i = vs.size() > 0 ? r.nextInt(vs.size()) : -1; return vs.get(i); } }
-
912. Sort an Array
Given an array of integers nums, sort the array in ascending order.
- BubbleSort
public int[] bubbleSort(int[] nums) { for(int i = 0; i < nums.length; i++) { for(int j = 0; j < nums.length - 1 - i; j++) { if(nums[j] > nums[j+1]) swap(nums, j, j+1); } } return nums; }
- SelectSort
public int[] selectSort(int[] nums) { for(int i = 0; i < nums.length; i++) { int maxIndex = 0, max = Integer.MIN_VALUE; for(int j = 0; j < nums.length - i; j++) { if(max < nums[j]) { max = nums[j]; maxIndex = j; } } swap(nums, nums.length - 1 - i, maxIndex); } return nums; }
- InsertSort
public int[] insertSort(int[] nums) { for(int i = 1; i < nums.length; i++) { int j = i; while(j-1 >= 0 && nums[j-1] > nums[j]) { swap(nums, j, j-1); j--; } } return nums; }
- MergeSort
public int[] mergeSort(int[] nums) { mergeSort(nums, 0, nums.length-1); return nums; } public void mergeSort(int[] nums, int start, int end) { if(start >= end) return; int mid = start + (end - start) / 2; mergeSort(nums, start, mid); mergeSort(nums, mid+1, end); int[] temp = new int[end - start + 1]; int p = start, q = mid+1, i = 0; while(p <= mid && q <= end) temp[i++] = nums[p] < nums[q] ? nums[p++] : nums[q++]; while(p <= mid) temp[i++] = nums[p++]; while(q <= end) temp[i++] = nums[q++]; System.arraycopy(temp, 0, nums, start, end - start + 1); }
- QuickSort
public void quickSort(int[] nums, int start, int end) { if(start >= end) return; int l = start + 1, r = end; while(l <= r) { if(nums[l] > nums[start]) swap(nums, l--, r--); l++; } l--; swap(nums, l, start); quickSort(nums, start, l-1); quickSort(nums, l+1, end); }
- HeapSort
public int[] heapSort(int[] nums) { for(int i = nums.length-1; i >= 0; i--) { maxHeapify(nums, i, nums.length-1); } System.out.println(Arrays.toString(nums)); int end = nums.length-1; while(end >= 0) { swap(nums, 0, end--); for(int i = 0; i <= end; i++) { maxHeapify(nums, i, end); } } return nums; } public void maxHeapify(int[] nums, int i, int limit) { int l = 2 * i + 1, r = 2 * i + 2; int maxIndex = i; if(l <= limit) maxIndex = nums[l] > nums[maxIndex] ? l : maxIndex; if(r <= limit) maxIndex = nums[r] > nums[maxIndex] ? r : maxIndex; if(maxIndex != i) { swap(nums, i, maxIndex); maxHeapify(nums, maxIndex, limit); } }
-
54. Spiral Matrix
Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. Example 1: Input: [ [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8, 9 ] ] Output: [1,2,3,6,9,8,7,4,5]
public List<Integer> spiralOrder(int[][] matrix) { if(matrix.length == 0) return new ArrayList<>(); int[] dirs = {1, 2, 3, 0}; int d = 0; int[] is = {0, 1, 0, -1}; int[] js = {1, 0, -1, 0}; int top = 0, bottom = matrix.length - 1, left = 0, right = matrix[0].length - 1; int i = 0, j = 0; List<Integer> result = new ArrayList<>(); while(top <= i && i <= bottom && left <= j && j <= right) { result.add(matrix[i][j]); int ni = i + is[d], nj = j + js[d]; if(top <= ni && ni <= bottom && left <= nj && nj <= right) { i = ni; j = nj; } else { d = dirs[d]; ni = i + is[d]; nj = j + js[d]; if(d == 1) top++; if(d == 2) right--; if(d == 3) bottom--; if(d == 0) left++; if(top <= ni && ni <= bottom && left <= nj && nj <= right) { i = ni; j = nj; } else { break; } } } return result; }
- Beat 100% speed and memory ```java
public List
spiralOrder(int[][] m) { if(m.length == 0 || m[0].length == 0) return new ArrayList<>(); int h = m.length, k = m[0].length; List result = new ArrayList<>(); for(int i = 0; i < h/2 && i < k/2; i++) { for(int col = i; col < k - i - 1; col++) result.add(m[i][col]); for(int row = i; row < h - i - 1; row++) result.add(m[row][k-i-1]); for(int col = k-i-1; col > i; col--) result.add(m[h-i-1][col]); for(int row = h-i-1; row > i; row--) result.add(m[row][i]); } if(k >= h && h % 2 == 1) for(int col = h / 2; col <= k - h/2 - 1; col++) result.add(m[h/2][col]); if(k < h && k % 2 == 1) for(int row = k/2; row <= h - k/2 - 1; row++) result.add(m[row][k/2]); return result; } ``` -
215. Kth Largest Element in an Array
215. Kth Largest Element in an Array
Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element. Example 1: Input: [3,2,1,5,6,4] and k = 2 Output: 5 Example 2: Input: [3,2,3,1,2,4,5,5,6] and k = 4 Output: 4
- PriorityQueue
public int findKthLargestPriorityQueue(int[] nums, int k) { PriorityQueue<Integer> q = new PriorityQueue<>(); for(int i : nums) { if(q.size() < k) q.offer(i); else { if(q.peek() < i) { q.poll(); q.offer(i); } } } return q.peek(); }
- Quick Select Iterative
public int findKthLargestIterative(int[] nums, int k) { k = nums.length - k; // now k is the index of target element in a sorted array int l = 0, r = nums.length - 1; while(l <= r) { int p = l + 1, q = r; while(p <= q) { if(nums[p] > nums[l]) swap(nums, p--, q--); p++; } p--; // p is first element larger than pivot, it is not the index in a sorted array becasue there may be other elements on the right side less than it (it is not the least largest element of pivot). swap(nums, p, l); // <=pivot <-- p(==pivot) --> >pivot // now nums[p] is the pivot and all elemets on the left side of p are <= nums[p], so p is the index of value nums[p] in a sorted array // In a word, nums[p] is the least largest element in terms of elements from 0 to p-1 if(p < k) { l = p + 1; } else if(p > k) { r = p - 1; } else { return nums[k]; } } return -1; }
- QuickSelect recursive
public int quickSelect(int[] nums, int k, int l, int r) { if(l == r) return nums[l]; int p = l + 1, q = r; while(p <= q) { if(nums[p] > nums[l]) swap(nums, p--, q--); p++; } p--; swap(nums, l, p); if(p < k) return quickSelect(nums, k, p+1, r); else if(p > k) return quickSelect(nums, k, l, p-1); else return nums[k]; } public void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
-
336. Palindrome Pairs
Given a list of unique words, find all pairs of distinct indices (i, j) in the given list, so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome. Example 1: Input: ["abcd","dcba","lls","s","sssll"] Output: [[0,1],[1,0],[3,2],[2,4]] Explanation: The palindromes are ["dcbaabcd","abcddcba","slls","llssssll"] Example 2: Input: ["bat","tab","cat"] Output: [[0,1],[1,0]] Explanation: The palindromes are ["battab","tabbat"]
- Tier Tree
public List<List<Integer>> palindromePairs(String[] words) { List<List<Integer>> result = new ArrayList<>(); Tier dict = new Tier(words); for(int i = 0; i < words.length; i++) { String w = words[i]; Node curr = dict.head; int j = w.length() - 1; while(j >= 0) { if(curr.i != -1 && curr.i != i && isPalindrome(w, 0, j)) { // we find a word is a palindrome with substring(j, w.length()), // so we only need to check whether the substring in the mid is also a palindrome result.add(new ArrayList(Arrays.asList(curr.i, i))); } char c = w.charAt(j--); curr = curr.next[c-'a']; if(curr == null) break; } if(j == -1 && curr != null) { // Word j run out, add all indexs at current node for(int k : curr.indexs) { if(k != i) result.add(new ArrayList(Arrays.asList(k, i))); } } } return result; } class Tier { Node head = new Node(); public Tier(String[] words) { for(int i = 0; i < words.length; i++) { Node curr = head; for(int j = 0; j < words[i].length(); j++) { if(isPalindrome(words[i], j, words[i].length()-1)) curr.indexs.add(i); char c = words[i].charAt(j); if(curr.next[c-'a'] == null) curr.next[c-'a'] = new Node(); curr = curr.next[c-'a']; } curr.indexs.add(i); // empty string is a valid P curr.i = i; // index of word ends at this node } } } class Node { int i = -1; // -1 means no words end at this node (letter) ArrayList<Integer> indexs = new ArrayList<>(); // a word with an index in this arraylist has a substring Palindrome from the char represented by this node to the end of this word Node[] next = new Node[26]; } public boolean isPalindrome(String w, int i, int j) { while(i < j) { if(w.charAt(i++) != w.charAt(j--)) return false; } return true; }
-
13. Roman to Integer
see also 13. Roman to Integer
Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 V 5 X 10 L 50 C 100 D 500 M 1000 For example, two is written as II in Roman numeral, just two one's added together. Twelve is written as, XII, which is simply X + II. The number twenty seven is written as XXVII, which is XX + V + II. Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: I can be placed before V (5) and X (10) to make 4 and 9. X can be placed before L (50) and C (100) to make 40 and 90. C can be placed before D (500) and M (1000) to make 400 and 900. Given a roman numeral, convert it to an integer. Input is guaranteed to be within the range from 1 to 3999. Example 1: Input: "III" Output: 3 Example 2: Input: "IV" Output: 4
public int romanToInt(String s) { HashMap<String, Integer> map = new HashMap<>(); map.put("I", 1); map.put("V", 5); map.put("X", 10); map.put("L", 50); map.put("C", 100); map.put("D", 500); map.put("M", 1000); map.put("IV", 4); map.put("IX", 9); map.put("XL", 40); map.put("XC", 90); map.put("CD", 400); map.put("CM", 900); int result = 0, n = 0; for(int i = 0; i < s.length(); i++) { if(i+1 < s.length() && map.containsKey(s.substring(i, i+2))) n = map.get(s.substring(i, i++ + 2)); else n = map.get("" + s.charAt(i)); result += n; } return result; }
-
46. Permutations
Given a collection of distinct integers, return all possible permutations. Example: Input: [1,2,3] Output: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
- Recursive (Swap)(Modify the original array)
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> result = new ArrayList<>(); backtrack(nums, 0, result, new ArrayList<Integer>()); return result; } public void backtrack(int[] nums, int start, List<List<Integer>> result, ArrayList<Integer> temp) { if(start == nums.length) { result.add(new ArrayList<Integer>(temp)); } for(int i = start; i < nums.length; i++) { swap(nums, i, start); temp.add(nums[start]); backtrack(nums, start + 1, result, temp); temp.remove(temp.size()-1); swap(nums, i, start); } } public void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
- Recursive (Visited Memorisation)
public List<List<Integer>> permute(int[] nums) { List<List<Integer>> result = new ArrayList<>(); backtrack(nums, 0, result, new ArrayList<Integer>(), new boolean[nums.length]); return result; } public void backtrack(int[] nums, int start, List<List<Integer>> result, ArrayList<Integer> temp, boolean[] visited) { if(temp.size() == nums.length) { result.add(new ArrayList<Integer>(temp)); } for(int i = 0; i < nums.length; i++) { if(visited[i]) continue; visited[i] = true; temp.add(nums[i]); backtrack(nums, 0, result, temp, visited); temp.remove(temp.size()-1); visited[i] = false; } }
-
72. Edit Distance
Given two words word1 and word2, find the minimum number of operations required to convert word1 to word2. You have the following 3 operations permitted on a word: Insert a character Delete a character Replace a character Example 1: Input: word1 = "horse", word2 = "ros" Output: 3 Explanation: horse -> rorse (replace 'h' with 'r') rorse -> rose (remove 'r') rose -> ros (remove 'e')
- Recursive (TLE)
public int minDistance(String s, String p) { return minDistanceRecursive(s, p, 0); } public int minDistanceRecursive(String s, String p, int cnt) { if(s.length() == 0) return p.length() + cnt; if(p.length() == 0) return s.length() + cnt; if(s.equals(p)) return cnt; int result = Integer.MAX_VALUE; if(s.charAt(0) == p.charAt(0)) { int addBoth = minDistanceRecursive(s.substring(1, s.length()), p.substring(1, p.length()), cnt); result = Math.min(result, addBoth); } else { int addS = minDistanceRecursive(s.substring(1, s.length()), p, cnt + 1); int addP = minDistanceRecursive(s, p.substring(1, p.length()), cnt + 1); int addBoth = minDistanceRecursive(s.substring(1, s.length()), p.substring(1, p.length()), cnt + 1); result = Math.min(Math.min(addS, addBoth), Math.min(addP, result)); } return result; }
- DynamicProgramming
public int minDistanceDP(String s, String p) { int[][] dp = new int[p.length()+1][s.length()+1]; for(int j = 1; j <= s.length(); j++) dp[0][j] = dp[0][j-1] + 1; for(int i = 1; i <= p.length(); i++) { dp[i][0] = dp[i-1][0] + 1; for(int j = 1; j <= s.length(); j++) { int addP = dp[i-1][j] + 1, addS = dp[i][j-1] + 1, addBoth = s.charAt(j-1) == p.charAt(i-1) ? dp[i-1][j-1] : dp[i-1][j-1] + 1; dp[i][j] = Math.min(addP, Math.min(addS, addBoth)); } } return dp[p.length()][s.length()]; }
-
44. Wildcard Matching
Given an input string (s) and a pattern (p), implement wildcard pattern matching with support for '?' and '*'. '?' Matches any single character. '*' Matches any sequence of characters (including the empty sequence). The matching should cover the entire input string (not partial). Note: s could be empty and contains only lowercase letters a-z. p could be empty and contains only lowercase letters a-z, and characters like ? or *. Example 1: Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa".
- Recursive
public boolean isMatch(String s, String p) { if(p.length() == 0) return s.length() == 0; if(s.length() > 0 && (p.charAt(0) == '?' || p.charAt(0) == s.charAt(0))) { return isMatch(s.substring(1, s.length()), p.substring(1, p.length())); } else if(p.charAt(0) == '*') { return s.length() > 0 && isMatch(s.substring(1, s.length()), p) || isMatch(s, p.substring(1, p.length())); } return false; }
- Recursive With Memorisation
public boolean isMatch(String s, String p) { int[][] dp = new int[p.length()+1][s.length()+1]; dp[0][0] = 1; return isMatchRecursive(s, p, dp); } public boolean isMatchRecursive(String s, String p, int[][] dp) { if(dp[p.length()][s.length()] != 0) return dp[p.length()][s.length()] == 1; if(p.length() == 0) return s.length() == 0; boolean result = false; if(s.length() > 0 && (p.charAt(0) == '?' || p.charAt(0) == s.charAt(0))) { result = isMatchRecursive(s.substring(1, s.length()), p.substring(1, p.length()), dp); } else if(p.charAt(0) == '*') { result = s.length() > 0 && isMatchRecursive(s.substring(1, s.length()), p, dp) || isMatchRecursive(s, p.substring(1, p.length()), dp); } dp[p.length()][s.length()] = result ? 1 : 2; return result; }
- DynamicProgramming
The same with Shortest Edit Distance
public boolean isMatch(String s, String p) { boolean[][] dp = new boolean[p.length()+1][s.length()+1]; dp[0][0] = true; for(int i = 1; i <= p.length(); i++) { dp[i][0] = dp[i-1][0] && p.charAt(i-1) == '*'; for(int j = 1; j <= s.length(); j++) { boolean addP = dp[i-1][j] && p.charAt(i-1) == '*'; boolean addS = dp[i][j-1] && p.charAt(i-1) == '*'; boolean addBoth = dp[i-1][j-1] && (p.charAt(i-1) == '*' || p.charAt(i-1) == '?' || p.charAt(i-1) == s.charAt(j-1)); dp[i][j] = addP || addS || addBoth; } } return dp[p.length()][s.length()]; }
-
811. Subdomain Visit Count
A website domain like "discuss.leetcode.com" consists of various subdomains. At the top level, we have "com", at the next level, we have "leetcode.com", and at the lowest level, "discuss.leetcode.com". When we visit a domain like "discuss.leetcode.com", we will also visit the parent domains "leetcode.com" and "com" implicitly. Now, call a "count-paired domain" to be a count (representing the number of visits this domain received), followed by a space, followed by the address. An example of a count-paired domain might be "9001 discuss.leetcode.com". We are given a list cpdomains of count-paired domains. We would like a list of count-paired domains, (in the same format as the input, and in any order), that explicitly counts the number of visits to each subdomain. Example 1: Input: ["9001 discuss.leetcode.com"] Output: ["9001 discuss.leetcode.com", "9001 leetcode.com", "9001 com"] Explanation: We only have one website domain: "discuss.leetcode.com". As discussed above, the subdomain "leetcode.com" and "com" will also be visited. So they will all be visited 9001 times.
- Recursive
public List<String> subdomainVisits(String[] cpdomains) { HashMap<String, Integer> map = new HashMap<>(); for(String p : cpdomains) { String[] cp = p.split(" "); String[] ds = cp[1].split("\\."); String temp = ""; for(int i = ds.length - 1; i >= 0; i--) { temp = ds[i] + (i == ds.length - 1 ? "" : ".") + temp; int v = map.getOrDefault(temp, 0); map.put(temp, v + Integer.valueOf(cp[0])); } } List<String> result = new ArrayList<>(); map.forEach((k,v)-> result.add(v + " " + k)); return result; }
-
295. Find Median from Data Stream
295. Find Median from Data Stream Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. For example,
[2,3,4], the median is 3 [2,3], the median is (2 + 3) / 2 = 2.5 Design a data structure that supports the following two operations: void addNum(int num) - Add a integer number from the data stream to the data structure. double findMedian() - Return the median of all elements so far. Example: addNum(1) addNum(2) findMedian() -> 1.5 addNum(3) findMedian() -> 2
- min heap and max heap
The same with Shortest Edit Distance
class MedianFinder { PriorityQueue<Integer> small, large; public MedianFinder() { large = new PriorityQueue<>(); //max heap store small values small = new PriorityQueue<Integer>(new Comparator<Integer>() { // min head store large values public int compare(Integer x, Integer y) { return Integer.compare(y, x); } }); } public void addNum(int num) { if(small.size() < large.size()) small.offer(large.poll()); small.offer(num); large.offer(small.poll()); } public double findMedian() { if(small.size() < large.size()) return large.peek(); else return (large.peek() + small.peek()) / 2.0; } }
Follow up: If all integer numbers from the stream are between 0 and 100, how would you optimize it? If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it? Array from 0 to 100; Count and sum;
-
10. Regular Expression Matching
10. Regular Expression Matching
Given an input string (s) and a pattern (p), implement regular expression matching with support for '.' and '*'. '.' Matches any single character. '*' Matches zero or more of the preceding element. The matching should cover the entire input string (not partial). Note: s could be empty and contains only lowercase letters a-z. p could be empty and contains only lowercase letters a-z, and characters like . or *. Example 1: Input: s = "aa" p = "a" Output: false Explanation: "a" does not match the entire string "aa". Example 2: Input: s = "aa" p = "a*" Output: true Explanation: '*' means zero or more of the precedeng element, 'a'. Therefore, by repeating 'a' once, it becomes "aa". Example 3: Input: s = "ab" p = ".*" Output: true Explanation: ".*" means "zero or more (*) of any character (.)".
- Recursive
public boolean isMatch(String s, String p) { if(p.length() == 0) return s.length() == 0; boolean firstMatch = s.length() > 0 && (s.charAt(0) == p.charAt(0) || p.charAt(0) == '.'); if(p.length() > 1 && p.charAt(1) == '*') { boolean shrinkP = isMatch(s, p.substring(2, p.length())); if(shrinkP) return shrinkP; // s is empty, and p is a*b*c* boolean shrinkS = firstMatch && isMatch(s.substring(1, s.length()), p); return shrinkS; } return firstMatch && isMatch(s.substring(1, s.length()), p.substring(1, p.length())); }
- DynamicProgramming
The same with Shortest Edit Distance
public boolean isMatch(String s, String p) { boolean[][] dp = new boolean[p.length()+1][s.length()+1]; dp[0][0] = true; // "" s0 s1 s2 s3 // "" T F F F F // p1 // p2 // p3 // p4 for(int i = 1; i < p.length()+1; i++) { // true when previous is true and current or then next is a "*" dp[i][0] = dp[i-1][0] && (p.charAt(i-1) == '*' || (i < p.length() && p.charAt(i) == '*')); for(int j = 1; j < s.length()+1; j++) { // add a p only when currenty p[i] is a "*" ,two situation: use p[i-1] or not use p[i-1] boolean addP = p.charAt(i-1) == '*' && (dp[i-1][j] || i-2 >= 0 && dp[i-2][j]); // add a s when previous p is "*", and this s is the same with the char before the "*" boolean addS = dp[i][j-1] && p.charAt(i-1) == '*' && (p.charAt(i-2) == '.' || p.charAt(i-2) == s.charAt(j-1)); // add both when the left up diagonal is true and current s and p match boolean addBoth = dp[i-1][j-1] && (p.charAt(i-1) == '.' || p.charAt(i-1) == s.charAt(j-1) || i-2 >= 0 && p.charAt(i-1) == '*' && s.charAt(j-1) == p.charAt(i-2)); dp[i][j] = addP || addS || addBoth; } } return dp[p.length()][s.length()]; }
-
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,3 → 1,3,2 3,2,1 → 1,2,3 1,1,5 → 1,5,1
public void nextPermutation(int[] nums) { for(int i = nums.length - 2; i >= 0 && i+1 < nums.length; i--) { if(nums[i+1] > nums[i]) { int p = i+1; while(p+1 < nums.length && nums[p+1] > nums[i]) p++; swap(nums, p, i); Arrays.sort(nums, i+1, nums.length); return; } } Arrays.sort(nums); return; } public void swap(int[] nums, int i, int j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; }
-
49. Group Anagrams
Given an array of strings, group anagrams together. Example: Input: ["eat", "tea", "tan", "ate", "nat", "bat"], Output: [ ["ate","eat","tea"], ["nat","tan"], ["bat"] ]
- Comparator
public List<List<String>> groupAnagrams(String[] strs) { HashMap<String, ArrayList<String>> map = new HashMap<>(); for(String s : strs) { char[] sc = s.toCharArray(); Arrays.sort(sc); String sorted = new String(sc); ArrayList<String> arr = map.getOrDefault(sorted, new ArrayList<String>()); arr.add(s); map.put(sorted, arr); } return new ArrayList(map.values()); }
-
Django Meeting Room Summary
https://github.com/CMU-Web-Application-Development/team53
-
560. Subarray Sum Equals K
You are given an integer array A. From some starting index, you can make a series of jumps. The (1st, 3rd, 5th, ...) jumps in the series are called odd numbered jumps, and the (2nd, 4th, 6th, ...) jumps in the series are called even numbered jumps. You may from index i jump forward to index j (with i < j) in the following way: During odd numbered jumps (ie. jumps 1, 3, 5, ...), you jump to the index j such that A[i] <= A[j] and A[j] is the smallest possible value. If there are multiple such indexes j, you can only jump to the smallest such index j. During even numbered jumps (ie. jumps 2, 4, 6, ...), you jump to the index j such that A[i] >= A[j] and A[j] is the largest possible value. If there are multiple such indexes j, you can only jump to the smallest such index j. (It may be the case that for some index i, there are no legal jumps.) A starting index is good if, starting from that index, you can reach the end of the array (index A.length - 1) by jumping some number of times (possibly 0 or more than once.) Return the number of good starting indexes. Example 1: Input: [10,13,12,14,15] Output: 2 Explanation: From starting index i = 0, we can jump to i = 2 (since A[2] is the smallest among A[1], A[2], A[3], A[4] that is greater or equal to A[0]), then we can't jump any more. From starting index i = 1 and i = 2, we can jump to i = 3, then we can't jump any more. From starting index i = 3, we can jump to i = 4, so we've reached the end. From starting index i = 4, we've reached the end already. In total, there are 2 different starting indexes (i = 3, i = 4) where we can reach the end with some number of jumps.
- Dynamic TreeMap
The key is that at each i, all elements currently in the TreeMap have indexes greater than i.
public int oddEvenJumps(int[] A) { TreeMap<Integer, Integer> map = new TreeMap<>(); boolean[][] dp = new boolean[A.length][2]; Arrays.fill(dp[A.length - 1], true); for(int i = A.length - 1; i >= 0; i--) { Map.Entry<Integer, Integer> greater = map.ceilingEntry(A[i]), less = map.floorEntry(A[i]); if(greater != null) dp[i][0] = dp[greater.getValue()][1]; if(less != null) dp[i][1] = dp[less.getValue()][0]; map.put(A[i], i); } int result = 0; for(boolean[] b : dp) result += b[0] ? 1 : 0; return result; }
- MonotonicStack
public int oddEvenJumps(int[] A) { ArrayList<Node> arr = new ArrayList<>(); for(int i = 0; i < A.length; i++) arr.add(new Node(A[i], i)); Collections.sort(arr, new Comparator<Node>() { public int compare(Node x, Node y) { int r = Integer.compare(x.k, y.k); return r == 0 ? Integer.compare(x.v, y.v) : r; } }); // small to large by value Stack<Node> stack = new Stack<>(); int[] firstLarger = new int[A.length]; // the smallest greater elements with greater index Arrays.fill(firstLarger, -1); for(Node n : arr) { while(!stack.isEmpty() && n.v > stack.peek().v) firstLarger[stack.pop().v] = n.v; stack.push(n); } Collections.sort(arr, new Comparator<Node>() { public int compare(Node x, Node y) { int r = Integer.compare(y.k, x.k); return r == 0 ? Integer.compare(x.v, y.v) : r; } }); // large to small by value stack = new Stack<>(); int[] firstSmaller = new int[A.length]; // the largest smaller elements with greater index Arrays.fill(firstSmaller, -1); for(Node n : arr) { while(!stack.isEmpty() && n.v > stack.peek().v) firstSmaller[stack.pop().v] = n.v; stack.push(n); } // dynamic programming in reverse order boolean[][] dp = new boolean[A.length][2]; Arrays.fill(dp[A.length - 1], true); for(int i = A.length - 2; i >= 0; i--) { dp[i][0] = firstLarger[i] != -1 && dp[firstLarger[i]][1]; dp[i][1] = firstSmaller[i] != -1 && dp[firstSmaller[i]][0]; } int result = 0; for(boolean[] b : dp) result += b[0] ? 1 : 0; return result; } class Node { int v; int k; public Node(int k, int v) { this.v = v; this.k = k;} }
-
560. Subarray Sum Equals K
Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k. Example 1: Input:nums = [1,1,1], k = 2 Output: 2
- HashMap O(N)
No duplicates: HashMap is filled dynamicly
public int subarraySum(int[] nums, int k) { int count = 0, sum = 0; HashMap <Integer, Integer> map = new HashMap<>(); map.put(0, 1); for (int i = 0; i < nums.length; i++) { sum += nums[i]; if (map.containsKey(sum - k)) count += map.get(sum - k); map.put(sum, map.getOrDefault(sum, 0) + 1); } return count; }
- Cumulative sum O(N^2)
public int subarraySum3(int[] nums, int k) { int result = 0; for(int i = 0; i < nums.length; i++) { int sum = 0; for(int j = i; j < nums.length; j++) { sum += nums[j]; if(sum == k) result++; } } return result; }
-
79. Word Search
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. Example: board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] Given word = "ABCCED", return true. Given word = "SEE", return true. Given word = "ABCB", return false.
- BackTraking
public boolean exist(char[][] board, String word) { for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { if(dfs(board, word, 0, i, j)) return true; } } return false; } public boolean dfs(char[][] board, String word, int index, int i, int j) { if(index == word.length()) return true; if(!inBound(board, i, j) || word.charAt(index) != board[i][j]) return false; board[i][j] = '*'; int[] xs = {1,-1,0,0}, ys = {0,0,1,-1}; for(int k = 0; k < 4; k++) { int ni = i + xs[k], nj = j + ys[k]; if(dfs(board, word, index + 1, ni, nj)) return true; } board[i][j] = word.charAt(index); return false; } public boolean inBound(char[][] board, int i, int j) { return i >= 0 && j >= 0 && i < board.length && j < board[0].length; }
-
560. Subarray Sum Equals K
Given an array of integers and an integer k, you need to find the total number of continuous subarrays whose sum equals to k. Example 1: Input:nums = [1,1,1], k = 2 Output: 2 Note: The length of the array is in range [1, 20,000]. The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7].
- Acumulative Sum
public int subarraySum3(int[] nums, int k) { int result = 0; for(int i = 0; i < nums.length; i++) { int sum = 0; for(int j = i; j < nums.length; j++) { sum += nums[j]; if(sum == k) result++; } } return result; }
- HashMap
public int subarraySum(int[] nums, int k) { int count = 0, sum = 0; HashMap <Integer, Integer> map = new HashMap<>(); map.put(0, 1); for (int i = 0; i < nums.length; i++) { sum += nums[i]; if (map.containsKey(sum - k)) count += map.get(sum - k); map.put(sum, map.getOrDefault(sum, 0) + 1); } return count; }
-
518. Coin Change II
You are given coins of different denominations and a total amount of money. Write a function to compute the number of combinations that make up that amount. You may assume that you have infinite number of each kind of coin. Example 1: Input: amount = 5, coins = [1, 2, 5] Output: 4 Explanation: there are four ways to make up the amount: 5=5 5=2+2+1 5=2+1+1+1 5=1+1+1+1+1
- Bottom Up
https://leetcode.com/problems/coin-change-2/discuss/99212/
i:coin j:amount dp[i][j] = dp[i-1][j] + dp[i][j-coins[i]] –> reduce a dimension to dp[j] = dp[j] + dp[j-coins[i]]
public int change(int amount, int[] coins) { int[] dp = new int[amount + 1]; dp[0] = 1; for(int c : coins) { for(int i = 1; i <= amount; i++) { if(c <= i) dp[i] += dp[i - c]; } } return dp[amount]; }
- Memory Pad Top Down
public int change2(int amount, int[] coins) { if(amount == 0) return 1; if(coins.length == 0) return 0; Integer[][] memo = new Integer[coins.length][amount+1]; return dfs(0, coins, 0, amount, memo); } public int dfs(int index, int[] coins, int sum, int amount, Integer[][] memo) { if(sum > amount) return 0; if(sum == amount) return 1; if(memo[index][amount-sum] != null) { return memo[index][amount-sum]; } int ans = 0; for(int i = index; i < coins.length; i++) { ans += dfs(i, coins, sum + coins[i], amount, memo); } memo[index][amount-sum] = ans; return ans; }
-
322. Coin Change
You are given coins of different denominations and a total amount of money amount. Write a function to compute the fewest number of coins that you need to make up that amount. If that amount of money cannot be made up by any combination of the coins, return -1.
- Bottom Up
i:coin j:amount dp[i][j] = Math.min(dp[i-1][j], dp[i][j-coins[i]]+1) –> reduce a dimension to dp[j] = Math.min(dp[j] + dp[j-coins[i]]+1)
public int coinChange(int[] coins, int amount) { int[] dp = new int[amount+1]; Arrays.fill(dp, amount + 1); dp[0] = 0; for(int i = 0; i < amount + 1; i++) { for(int c : coins) { if(c <= i) { dp[i] = Math.min(dp[i], dp[i-c] + 1); } } } return dp[amount] == amount + 1 ? -1 : dp[amount]; }
- Memory Pad Top Down
public int coinChange(int[] coins, int amount) { int result = recursive(coins, new HashMap<Integer, Integer>(), amount); return result; } public int recursive(int[] coins, HashMap<Integer, Integer> dp, int remain) { if(remain < 0) return -1; if(remain == 0) return 0; if(dp.containsKey(remain)) return dp.get(remain); int result = Integer.MAX_VALUE; for(int coin : coins) { int min = recursive(coins, dp, remain - coin); if(min >= 0) result = Math.min(result, min + 1); // valid change } if(result == Integer.MAX_VALUE) result = -1; // no valid change dp.put(remain, result); return result; }
-
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: [ "((()))", "(()())", "(())()", "()(())", "()()()" ]
- HashSet Brutal Force
public List<String> generateParenthesis(int n) { HashSet<String> set = new HashSet<>(); backtrack(n, 0, new StringBuilder(), set); return new ArrayList<>(set); } public void backtrack(int n, int m, StringBuilder sb, HashSet<String> set) { if(n == m) { set.add(sb.toString()); return; } for(int i = 0; i <= sb.length() / 2; i++) { backtrack(n, m + 1, new StringBuilder(sb).insert(i, "()"), set); } }
- Count left and right brackets
public List<String> generateParenthesis(int n) { ArrayList<String> result = new ArrayList<>(); backtrack(n, 0, 0, "", result); return result; } public void backtrack(int n, int left, int right, String temp, List<String> result) { if(left == n && right == n) { result.add(temp); } else { if(left < n) { backtrack(n, left + 1, right, temp + "(", result); } if(right < left) { backtrack(n, left, right + 1, temp + ")", result); } } }
-
289. Game of Life
According to the Wikipedia's article: "The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970." Given a board with m by n cells, each cell has an initial state live (1) or dead (0). Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules (taken from the above Wikipedia article): Any live cell with fewer than two live neighbors dies, as if caused by under-population. Any live cell with two or three live neighbors lives on to the next generation. Any live cell with more than three live neighbors dies, as if by over-population.. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. Write a function to compute the next state (after one update) of the board given its current state. The next state is created by applying the above rules simultaneously to every cell in the current state, where births and deaths occur simultaneously. Example: Input: [ [0,1,0], [0,0,1], [1,1,1], [0,0,0] ] Output: [ [0,0,0], [1,0,1], [0,1,1], [0,1,0] ]
public void gameOfLife(int[][] board) { int[] xs = {0,0,1,1,1,-1,-1,-1}; int[] ys = {1,-1,0,1,-1,0,1,-1}; // 0. d-d // 1. l-l // 2. l-d // 3. d-l for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { int live = 0; for(int k = 0; k < 8; k++) { int nx = i + xs[k], ny = j + ys[k]; if(nx >= 0 && ny >= 0 && nx < board.length && ny < board[0].length) { if(board[nx][ny] == 1 || board[nx][ny] == 2) live++; } } if(board[i][j] == 1 && (live < 2 || live > 3)) board[i][j] = 2; if(board[i][j] == 0 && live == 3) board[i][j] = 3; } } for(int i = 0; i < board.length; i++) { for(int j = 0; j < board[0].length; j++) { if(board[i][j] == 3) board[i][j] = 1; if(board[i][j] == 2) board[i][j] = 0; } } }
-
269. Alien Dictionary
There is a new alien language which uses the latin alphabet. However, the order among letters are unknown to you. You receive a list of non-empty words from the dictionary, where words are sorted lexicographically by the rules of this new language. Derive the order of letters in this language.
Example 1: Input: [ "wrt", "wrf", "er", "ett", "rftt" ] Output: "wertf" Input: [ "z", "x", "z" ] Output: "" Explanation: The order is invalid, so return "".
public String alienOrder(String[] words) { HashMap<Character, HashSet<Character>> map = new HashMap<>(); // chars with neighbors HashSet<Character> allChars = new HashSet<>(); for(Character ch : words[0].toCharArray()) allChars.add(ch); for(int i = 1; i < words.length; i++) { String p = words[i-1], c = words[i]; for(Character ch : c.toCharArray()) allChars.add(ch); for(int j = 0; j < Math.min(p.length(), c.length()); j++) { if(p.charAt(j) != c.charAt(j)) { HashSet<Character> set = map.getOrDefault(p.charAt(j), new HashSet<Character>()); set.add(c.charAt(j)); map.put(p.charAt(j), set); break; } } } HashMap<Character, Integer> inDegree = new HashMap<>(); for(Character c : allChars) inDegree.put(c, 0); for(HashSet<Character> set : map.values()) { for(Character c : set) inDegree.put(c, inDegree.get(c) + 1); } Queue<Character> q = new LinkedList<>(); for(Character c : allChars) { if(inDegree.get(c) == 0) q.offer(c); } StringBuilder sb = new StringBuilder(); while(!q.isEmpty()) { char c = q.poll(); sb.append(c); if(map.containsKey(c)) { for(Character next : map.get(c)) { int v = inDegree.get(next); inDegree.put(next, v - 1); if(v - 1 == 0) q.offer(next); } } } return sb.length() == allChars.size() ? sb.toString() : ""; }
-
11. Container With Most Water
Given n non-negative integers a1, a2, ..., an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water. Note: You may not slant the container and n is at least 2.
Example: Input: [1,8,6,2,5,4,8,3,7] Output: 49
- TwoPointer
public int maxArea(int[] height) { int l = 0, r = height.length - 1; int max = 0; while(l < r) { max = Math.max(max, Math.min(height[l], height[r]) * (r - l)); if(height[l] < height[r]) l++; else r--; } return max; }
-
138. Copy List with Random Pointer
138. Copy List with Random Pointer
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list.
- Recursive
public Node copyRandomList(Node head) { return recursive(head, new HashMap<Node, Node>()); } public Node recursive(Node head, HashMap<Node, Node> map) { if(head == null) return null; if(map.containsKey(head)) return map.get(head); Node newHead = new Node(); map.put(head, newHead); newHead.val = head.val; newHead.next = recursive(head.next, map); newHead.random = recursive(head.random, map); return newHead; }
-
76. Minimum Window Substring
Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).
Example:
Input: S = “ADOBECODEBANC”, T = “AABC” Output: “ADOBECODEBA”
public String minWindow(String s, String t) { HashMap<Character, Integer> targets = new HashMap<>(); // chars we want for(char c : t.toCharArray()) { int v = targets.getOrDefault(c, 0); targets.put(c, v + 1); } HashMap<Character, Integer> map = new HashMap<>(); // chars in hand int left = 0, start = 0, end = Integer.MAX_VALUE, satisfied = 0; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(!targets.containsKey(c)) continue; int v = map.getOrDefault(c, 0); map.put(c, v + 1); if(v + 1 == targets.get(c)) { // new satisfied satisfied++; } while(satisfied == targets.size()) { // shrink left if(i - left < end - start) { end = i; start = left; } char pre = s.charAt(left++); if(!targets.containsKey(pre)) continue; map.put(pre, map.get(pre) - 1); if(map.get(pre) < targets.get(pre)) { satisfied--; while(left < s.length() && !targets.containsKey(s.charAt(left))) { left++; } } } } return end == Integer.MAX_VALUE ? "" : s.substring(start, end + 1); }
-
297. Serialize and Deserialize Binary Tree
297. Serialize and Deserialize Binary Tree
Serialization is the process of converting a data structure or object into a sequence of bits so that it can be stored in a file or memory buffer, or transmitted across a network connection link to be reconstructed later in the same or another computer environment. Design an algorithm to serialize and deserialize a binary tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary tree can be serialized to a string and this string can be deserialized to the original tree structure.
public String serialize(TreeNode root) { if(root == null) return "null"; return "" + root.val + "#" + serialize(root.left) + "#" + serialize(root.right); } public TreeNode deserialize(String data) { LinkedList<String> list = new LinkedList<>(Arrays.asList(data.split("#"))); return ddfs(list); } public TreeNode ddfs(LinkedList<String> list) { if(list.isEmpty()) return null; TreeNode root = null; String curr = list.removeFirst(); if(!curr.equals("null")) { root = new TreeNode(Integer.valueOf(curr)); root.left = ddfs(list); root.right = ddfs(list); } return root; }
-
238. Product of Array Except Self
238. Product of Array Except Self
Given an array nums of n integers where n > 1, return an array output such that output[i] is equal to the product of all the elements of nums except nums[i]. Example: Input: [1,2,3,4] Output: [24,12,8,6]
- Two Pass
public int[] productExceptSelf(int[] nums) { int[] result = new int[nums.length]; int product = 1; for(int i = 0; i < nums.length; i++) { result[i] = product; product *= nums[i]; } product = 1; for(int i = nums.length-1; i >= 0; i--) { result[i] *= product; product *= nums[i]; } return result; }
- Two Pass More Space
public int[] productExceptSelfBad(int[] nums) { if(nums.length == 0) return new int[0]; int[] left = new int[nums.length], right = new int[nums.length]; left[0] = 1; right[nums.length-1] = 1; for(int i = 1; i < nums.length; i++) left[i] = left[i-1] * nums[i-1]; for(int i = nums.length-2; i >= 0; i--) right[i] = right[i+1] * nums[i+1]; int[] result = new int[nums.length]; for(int i = 0; i < result.length; i++) result[i] = left[i] * right[i]; return result; }
-
7. Reverse Integer
Given a 32-bit signed integer, reverse digits of an integer.
Note: Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−2^31, 2^31 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
public int reverse(int n) { int negative = n < 0 ? -1 : 1; long x = Math.abs(n), r = 0; while(x > 0) { r *= 10; r += x % 10; x /= 10; } return r > Integer.MAX_VALUE ? 0 : negative * (int)r; }
-
121. Best Time to Buy and Sell Stock
121. Best Time to Buy and Sell Stock
Say you have an array for which the ith element is the price of a given stock on day i. If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit. Note that you cannot sell a stock before you buy one.
public int maxProfit(int[] prices) { if(prices.length == 0) return 0; int[] profits = new int[prices.length-1]; for(int i = 0; i < profits.length; i++) profits[i] = prices[i+1] - prices[i]; int sum = 0, max = Integer.MIN_VALUE; for(int profit : profits) { // sum = sum + profit < 0 ? 0 : sum + profit; // max = profit < 0 ? Math.max(profit, max) : Math.max(sum, max); if(sum < 0) { sum = profit; } else { sum += profit; } max = Math.max(max, sum); } return max < 0 ? 0 : max; }
-
301. Remove Invalid Parentheses
301. Remove Invalid Parentheses
Remove the minimum number of invalid parentheses in order to make the input string valid. Return all possible results. Note: The input string may contain letters other than the parentheses ( and ). Example 1: Input: "()())()" Output: ["()()()", "(())()"] Example 2: Input: "(a)())()" Output: ["(a)()()", "(a())()"]
public List<String> removeInvalidParentheses(String s) { int lb = 0, rb = 0; for(char c : s.toCharArray()) { if(c == '(') lb++; if(c == ')') { if(lb > 0) lb--; else rb++; } } HashSet<String> set = new HashSet<>(); backtraking(lb, rb, 0, 0, set, s, 0, ""); return new ArrayList<>(set); } public void backtraking(int lb, int rb, int left, int right, HashSet<String> set, String s, int i, String temp) { if(i == s.length()) { if(lb == 0 && rb == 0 && left == right) set.add(temp); return; } if(s.charAt(i) == '(') { if(lb > 0) { backtraking(lb-1, rb, left, right, set, s, i+1, temp); } backtraking(lb, rb, left+1, right, set, s, i+1, temp + "("); } else if(s.charAt(i) == ')') { if(rb > 0) { backtraking(lb, rb-1, left, right, set, s, i+1, temp); } if(left > right) { backtraking(lb, rb, left, right+1, set, s, i+1, temp + ")"); } } else { backtraking(lb, rb, left, right, set, s, i+1, temp + s.charAt(i)); } }
-
253. Meeting Rooms II
Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), find the minimum number of conference rooms required. Example 1: Input: [[0, 30],[5, 10],[15, 20]] Output: 2
Consider a ended room first when trying to start a new meeting.
- PriorityQueue
public int minMeetingRoomsPriorityQueue(int[][] intervals) { PriorityQueue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() { public int compare(int[] x, int[] y) { return Integer.compare(x[1], y[1]); } }); Arrays.sort(intervals, new Comparator<int[]>(){ public int compare(int[] x, int[] y) { return Integer.compare(x[0], y[0]); } }); for(int[] i : intervals) { if(q.isEmpty()) q.offer(i); else { if(q.peek()[1] <= i[0]) q.poll(); q.offer(i); } } return q.size(); }
- Two sorted Arrays
public int minMeetingRoomsArraySort(int[][] intervals) { int[] starts = new int[intervals.length], ends = new int[intervals.length]; for(int i = 0; i < intervals.length; i++) { starts[i] = intervals[i][0]; ends[i] = intervals[i][1]; } Arrays.sort(starts); Arrays.sort(ends); int s = 0, e = 0, cnt = 0; while(s < starts.length) { if(starts[s] >= ends[e]) { cnt--; e++; } s++; cnt++; } return cnt; }
-
206. Reverse Linked List
Reverse a singly linked list. Example: Input: 1->2->3->4->5->NULL Output: 5->4->3->2->1->NULL
- Recursive
public ListNode reverseList(ListNode head) { if(head == null || head.next == null) return head; ListNode newHead = reverseList(head.next); head.next.next = head; head.next = null; return newHead; }
- Iterative
public ListNode reverseListIterative(ListNode head) { ListNode sentinel = new ListNode(0), curr = head; sentinel.next = curr; while(curr != null && curr.next != null) { ListNode next = curr.next; curr.next = next.next; next.next = sentinel.next; sentinel.next = next; } return sentinel.next; }
-
56. Merge Intervals
Given a collection of intervals, merge all overlapping intervals. Example: Input: [[1,3],[2,6],[8,10],[15,18]] Output: [[1,6],[8,10],[15,18]] Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].
public int[][] merge(int[][] intervals) { List<int[]> result = new ArrayList<>(); Arrays.sort(intervals, new Comparator<int[]>() { public int compare(int[] x, int[] y) { return Integer.compare(x[0], y[0]); } }); for(int[] i : intervals) { if(result.isEmpty()) result.add(i); else { int[] pre = result.get(result.size()-1); if(i[0] <= pre[1]) pre[1] = Math.max(pre[1], i[1]); else result.add(i); } } int[][] arr = new int[result.size()][]; for(int i = 0; i < result.size(); i++) arr[i] = result.get(i); return arr; }
-
20. Valid Parentheses
Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. An input string is valid if: Open brackets must be closed by the same type of brackets. Open brackets must be closed in the correct order. Note that an empty string is also considered valid.
public boolean isValid(String s) { Stack<Character> stack = new Stack<>(); HashMap<Character, Character> map = new HashMap<>(); map.put(')', '('); map.put(']', '['); map.put('}', '{'); for(char c : s.toCharArray()) { if(!map.containsKey(c)) { stack.push(c); } else { if(!stack.isEmpty() && stack.peek() == map.get(c)) stack.pop(); else return false; } } return stack.isEmpty(); }
-
273. Integer to English Words
Convert a non-negative integer to its english words representation. Given input is guaranteed to be less than 231 - 1. Case: Input: 1234567 Output: "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven"
String[] dict = {"Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven" , "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen", "Twenty"}; String[] tens = {"Zero", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; String[] suffixs = {"", "Thousand", "Million", "Billion"}; public String numberToWords(int num) { if(num <= 20) return dict[num]; StringBuilder sb = new StringBuilder(); int cnt = 0; while(num > 0) { String prefix = three(num - num / 1000 * 1000).trim(); if(prefix.length() != 0) { String suffix = ((suffixs[cnt].length() == 0) ? "" : " " + suffixs[cnt] + (sb.length() == 0 ? "" : " ")); prefix += suffix; } cnt++; num /= 1000; sb.insert(0, prefix); } return sb.toString(); } public String three(int num) { StringBuilder sb = new StringBuilder(); if(num > 99) { // hundred sb.append(dict[num / 100]).append(" ").append("Hundred "); num -= num / 100 * 100; } if(num > 20) { // tens sb.append(tens[num / 10] + " "); num -= num / 10 * 10; if(num != 0) { sb.append(dict[num] + " "); } } else if(num != 0) { // ones sb.append(dict[num] + " "); } return sb.toString(); }
-
23. Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
- PriorityQueue
public ListNode mergeKLists(ListNode[] lists) { PriorityQueue<ListNode> q = new PriorityQueue<>(new Comparator<ListNode>() { public int compare(ListNode x, ListNode y) { return Integer.compare(x.val, y.val); } }); ListNode sentinel = new ListNode(0), curr = sentinel; for(ListNode root : lists) { if(root != null) q.offer(root); } while(!q.isEmpty()) { ListNode poped = q.poll(); curr.next = poped; curr = curr.next; if(poped.next != null) q.offer(poped.next); } return sentinel.next; }
- One Pass
public int maxSubArray(int[] nums) { int result = Integer.MIN_VALUE, sum = 0; for(int n : nums) { sum = sum + n < 0 ? 0 : sum + n; // reset sum when it becoming negative result = n < 0 ? Math.max(result, n) : Math.max(result, sum); // imaging an array with only negative elements } return result; }
-
53. Maximum Subarray
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
- DP brutal
public int maxSubArrayBrutal(int[] nums) { int result = Integer.MIN_VALUE; for(int i = 0; i < nums.length; i++) { int sum = 0; for(int j = i; j < nums.length; j++) { sum += nums[j]; result = Math.max(sum, result); } } return result; }
- One Pass
Key: Is i the index to start a new sum?
public int maxSubArray(int[] nums) { int result = nums[0], sum = result; for(int i = 1; i < nums.length; i++) { sum += nums[i]; if(sum < nums[i]) { sum = nums[i]; } result = Math.max(result, sum); } return result; }
public int maxSubArray(int[] nums) { int result = Integer.MIN_VALUE, sum = 0; for(int n : nums) { sum = sum + n < 0 ? 0 : sum + n; // reset sum when it becoming negative result = n < 0 ? Math.max(result, n) : Math.max(result, sum); // imaging an array with only negative elements } return result; }
-
21. Merge Two Sorted Lists
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
- Recursive
public ListNode mergeTwoListsRecursive(ListNode l1, ListNode l2) { if(l1 == null) return l2; if(l2 == null) return l1; ListNode root, next; if(l1.val < l2.val) { root = l1; next = mergeTwoLists(l1.next, l2); } else { root = l2; next = mergeTwoLists(l1, l2.next); } root.next = next; return root; }
- Iterative
public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode sentinel = new ListNode(0), curr = sentinel; while(l1 != null && l2 != null) { if(l1.val < l2.val) { curr.next = l1; l1 = l1.next; } else { curr.next = l2; l2 = l2.next; } curr = curr.next; } curr.next = l1 == null ? l2 : l1; // l1 and l2 are of different length return sentinel.next; }
-
3. Longest Substring Without Repeating Characters
3. Longest Substring Without Repeating Characters
Given a string, find the length of the longest substring without repeating characters.
- TwoPointer with HashSet
public int lengthOfLongestSubstring(String s) { HashSet<Character> set = new HashSet<>(); int result = 0, left = 0; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); while(set.contains(c)) { set.remove(s.charAt(left++)); } set.add(c); result = Math.max(set.size(), result); } return result; }
- TwoPointer with HashMap
public int lengthOfLongestSubstring(String s) { HashMap<Character,Integer> map = new HashMap<>(); int result = 0, left = 0; for(int i = 0; i < s.length(); i++) { char c = s.charAt(i); if(map.containsKey(c)) { left = Math.max(left, map.get(c)+1); } result = Math.max(result, i-left+1); map.put(c, i); } return result; }
-
42. Trapping Rain Water
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
- DynamicProgramming: Two Pass(left max and right max)
public List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); for(int i = 0; i < nums.length - 2; i++) { if(i > 0 && nums[i] == nums[i-1]) continue; // remove duplicate int l = i + 1, r = nums.length-1; while(l < r) { if(l > i+1 && nums[l] == nums[l-1]) { // remove duplicate l++; continue; } if(nums[l] + nums[r] + nums[i] == 0) { result.add(new ArrayList<Integer>(Arrays.asList(nums[i], nums[l], nums[r]))); l++; r--; } else if(nums[l] + nums[r] + nums[i] < 0) { l++; } else { r--; } } } return result; }
- DP: One Pass
public int trap(int[] height) { int max = Integer.MIN_VALUE; int l = 0, r = height.length-1; int result = 0; while(l < r) { if(height[l] < height[r]) { max = Math.max(max, height[l]); result += Math.min(max, height[r]) - height[l]; l++; } else { max = Math.max(max, height[r]); result += Math.min(max, height[l]) - height[r]; r--; } } return result; }
- MonotonicStack
public int trap(int[] height) { Deque<Integer> stack = new ArrayDeque<>(); int result = 0; for(int i = 0; i < height.length; i++) { while(!stack.isEmpty() && height[i] >= height[stack.peek()]) { int base = height[stack.pop()]; if(!stack.isEmpty()) { int preHeight = height[stack.peek()]; result += (Math.min(preHeight, height[i]) - base) * (i-stack.peek() - 1); } } stack.push(i); } return result; }
-
15. 3Sum
Given an array nums of n integers, are there elements a, b, c in nums such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. Note: The solution set must not contain duplicate triplets.
public List<List<Integer>> threeSum(int[] nums) { Arrays.sort(nums); List<List<Integer>> result = new ArrayList<>(); for(int i = 0; i < nums.length - 2; i++) { if(i > 0 && nums[i] == nums[i-1]) continue; // remove duplicate int l = i + 1, r = nums.length-1; while(l < r) { if(l > i+1 && nums[l] == nums[l-1]) { // remove duplicate l++; continue; } if(nums[l] + nums[r] + nums[i] == 0) { result.add(new ArrayList<Integer>(Arrays.asList(nums[i], nums[l], nums[r]))); l++; r--; } else if(nums[l] + nums[r] + nums[i] < 0) { l++; } else { r--; } } } return result; }
-
973. K Closest Points to Origin
973. K Closest Points to Origin
We have a list of points on the plane. Find the K closest points to the origin (0, 0). (Here, the distance between two points on a plane is the Euclidean distance.) You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.)
- QuickSelect
- O(N)
public int[][] kClosest(int[][] points, int K) { quickSelect(points, K, 0, points.length-1); int[][] result = new int[K][2]; for(int i = 0; i < K; i++) result[i] = points[i]; return result; } public void quickSelect(int[][] points, int K, int start, int end) { if(start > end) return; int pivot = getDS(points[start]); int i = start + 1, j = i; while(i <= end) { int d = getDS(points[i]); if(d <= pivot) { swap(points, i, j); j++; } i++; } j--; swap(points, start, j); if(K == j+1) return; else if(K > j+1) { quickSelect(points, K, j+1, end); } else { quickSelect(points, K, start, j); } } public void swap(int[][] points, int i, int j) { int[] temp = points[i]; points[i] = points[j]; points[j] = temp; } public int getDS(int[] p) { return p[0] * p[0] + p[1] * p[1]; }
- PriorityQueue
- NlogK ```java public int[][] kClosest(int[][] points, int K) { PriorityQueue<int[]> q = new PriorityQueue<>(new Comparator<int[]>() { public int compare(int[] p1, int[] p2) { return getDS(p2) - getDS(p1); } }); for(int[] p : points) { if(q.size() < K) q.offer(p); else { int[] top = q.peek(); if(getDS(top) > getDS(p)) { q.poll(); q.offer(p); } } } int[][] r = new int[K][]; for(int i = 0; i < K; i++) r[i] = q.poll(); return r; }
public int getDS(int[] p) { return p[0] * p[0] + p[1] * p[1]; }
```
- QuickSelect
-
200. Number of Islands
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
- DFS
int[] xs = {0,0,1,-1}; int[] ys = {1,-1,0,0}; public int numIslands(char[][] grid) { int result = 0; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { if(grid[i][j] == '1') { result++; dfs(grid, i, j); } } } return result; } public void dfs(char[][] grid, int i, int j) { grid[i][j] = '#'; for(int k = 0; k < 4; k++) { int nx = i + xs[k], ny = j + ys[k]; if(nx >= 0 && nx < grid.length && ny >=0 && ny < grid[0].length && grid[nx][ny] == '1') { dfs(grid, nx, ny); } } }
- Union Find with rank
class UnionFind { int[] arr; int[] rank; int count; public UnionFind(char[][] grid) { int n = grid.length, m = grid[0].length; arr = new int[n * m]; rank = new int[n * m]; for(int i = 0; i < n; i++) { for(int j = 0; j < m; j++) { if(grid[i][j] == '1') { arr[i*m+j] = i*m+j; rank[i*m+j] = 1; count++; } } } } public int find(int x) { if(arr[x] != x) { return find(arr[x]); } return x; } public void union(int x1, int x2) { int r1 = find(x1), r2 = find(x2); if(r1 != r2) { count--; if(rank[r1] >= rank[r2]) { arr[r2] = r1; rank[r1] += rank[r2]; } else { arr[r1] = r2; rank[r2] += rank[r1]; } } } } int[] xs = {0,0,1,-1}; int[] ys = {1,-1,0,0}; public int numIslands(char[][] grid) { if(grid.length == 0 || grid[0].length == 0) return 0; UnionFind uf = new UnionFind(grid); int numPerRow = grid[0].length; for(int i = 0; i < grid.length; i++) { for(int j = 0; j < grid[0].length; j++) { if(grid[i][j] == '1') { for(int k = 0; k < 4; k++) { int nx = i + xs[k], ny = j + ys[k]; if(nx >= 0 && nx < grid.length && ny >=0 && ny < grid[0].length && grid[nx][ny] == '1') { uf.union(i*numPerRow+j, nx*numPerRow+ny); } } } } } return uf.count; }
-
4. Median of Two Sorted Arrays
4. Median of Two Sorted Arrays
There are two sorted arrays nums1 and nums2 of size m and n respectively. Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). You may assume nums1 and nums2 cannot be both empty.
public double findMedianSortedArrays(int[] nums1, int[] nums2) { int[] la = nums1.length >= nums2.length ? nums1 : nums2; int[] sa = la == nums2 ? nums1 : nums2; if(la.length == sa.length) { // check if the answer only includes all elements from the shorter arr if(sa[la.length-1] <= la[0]) return (double)(sa[la.length-1] + la[0])/2; } int half = (la.length + sa.length + 1) / 2; // 6->3 ; 5->3 int left = 0, right = la.length-1; while(left <= right) { int i1 = (right + left) / 2; // i1 means the smaller half includes the elements from index i1 to 0 in the longer arr int len1 = i1 + 1; int len2 = half - len1; int i2 = len2 - 1; // the same as i2 int ll = getArrValue(i1, la); // array index bound checking int lr = getArrValue(i1+1, la); int sl = getArrValue(i2, sa); int sr = getArrValue(i2+1, sa); if(ll <= sr && lr >= sl) { // binary search if(total % 2 == 0) return (Math.max(ll, sl) + Math.min(lr, sr)) / (double)2; else return (double)Math.max(ll, sl); } else if(ll > sr) { right = i1 - 1; } else if(lr < sl) { left = i1 + 1; } } return 0; } public int getArrValue(int index, int[] arr) { return index >= arr.length ? Integer.MAX_VALUE : index >= 0 ? arr[index] : Integer.MIN_VALUE; }
- Conscise
public double findMedianSortedArrays(int[] nums1, int[] nums2) { if(nums1.length > nums2.length) { int[] temp = nums1; nums1 = nums2; nums2 = temp; } int n = nums1.length, m = nums2.length; int l = 0, r = n; boolean odd = ((n + m) & 1) == 1; double result = 0.0; int half = (m + n +1) / 2; while(l <= r) { int mid = l + (r-l) / 2; int inN = mid; int inM = half - inN; int i = inN-1, j = inM - 1; int nl = i < 0 ? Integer.MIN_VALUE : nums1[i]; int nr = i == n-1 ? Integer.MAX_VALUE : nums1[i+1]; int ml = j < 0 ? Integer.MIN_VALUE : nums2[j]; int mr = j == m-1 ? Integer.MAX_VALUE : nums2[j+1]; if(nl <= mr && ml <= nr) { if(odd) { return (double) Math.max(nl, ml); } else { return (Math.max(nl, ml) + Math.min(nr, mr)) / 2.0; } } else if(nl > mr) { r = mid - 1; } else { l = mid + 1; } } return 0.0; }
-
5. Longest Palindromic Substring
5. Longest Palindromic Substring
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
- Two Pointer Version
expand from center
public String longestPalindrome(String s) { if(s.length()==0) return s; int left = 0, right = 0; for(int i = 0; i < s.length(); i++) { int[] single = check(i, i, s); int[] pair = {0, 0}; if(i+1 < s.length() && s.charAt(i) == s.charAt(i+1)) { pair = check(i, i+1, s); } if(single[1]-single[0] > right - left) { right = single[1]; left = single[0]; } if(pair[1]-pair[0] > right - left) { right = pair[1]; left = pair[0]; } } return s.substring(left+1, right); } public int[] check(int left, int right, String s) { while(right >= left && right < s.length() && left >= 0) { if(s.charAt(left) == s.charAt(right)) { left--; right++; } else { break; } } return new int[]{left, right}; }
- Manachers’s Algorithm
public String longestPalindrome(String s) { StringBuilder sb = new StringBuilder(); for(char c : s.toCharArray()) { sb.append('#').append(c); } sb.append('#'); s = sb.toString(); int max = 0, axis = 0; int left = 0, right = 0; int[] dp = new int[s.length()]; for(int i = 0; i < dp.length; i++) { int len = 0; if(i < max) { int mirrorIndex = 2 * axis - i; int mirrorLen = dp[mirrorIndex]; len = Math.min(max-i, mirrorLen); } // starting from i + len, we expand the palindrome // becasue we know the substring between i and i + len is a palindrome int r = i + len, l = i - len; while(r + 1 < s.length() && l - 1 >= 0 && s.charAt(r+1) == s.charAt(l-1)) { r++; l--; } if(r - l > right - left) { // update result right = r; left = l; } if(r > max) { // expand right most max = r; axis = i; } dp[i] = r - i; // update dp array } sb.setLength(0); for(char c : s.substring(left, right+1).toCharArray()) { if(c != '#') sb.append(c); } return sb.toString(); }
- Dynamic Programming
Define boolean dp[i][j] (if string(i,j) is a palindrome) Increase window size from 1 to n
-
146. LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put. get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. The cache is initialized with a positive capacity.
- Ordered Map Version
class LRUCache extends LinkedHashMap<Integer, Integer> { int capacity; public LRUCache(int capacity) { super(capacity, 0.75f, true); this.capacity = capacity; } public boolean removeEldestEntry(Map.Entry<Integer,Integer> en) { return this.size() > capacity; } public int get(int key) { return super.getOrDefault(key, -1); } public void put(int key, int value) { super.put(key, value); } }
- HashMap + Doubly LinkedList Version
class LRUCache { int capacity; int size; Node sentinel, tail; HashMap<Integer, Node> map = new HashMap<>(); // key->Node public LRUCache(int capacity) { this.capacity = capacity; this.sentinel = new Node(0, 0); } public int get(int key) { if(!map.containsKey(key)) return -1; Node curr = map.get(key); if(curr != this.tail) { Node prev = curr.prev, next = curr.next; prev.next = next; next.prev = prev; this.tail.next = curr; curr.prev = this.tail; this.tail = curr; } return curr.val; } public void put(int key, int val) { if(this.capacity == 0) return; Node n; if(map.containsKey(key)) { n = map.get(key); n.val = val; this.get(key); } else { // add to list and map n = new Node(key, val); map.put(key, n); if(this.tail == null) { sentinel.next = n; n.prev = sentinel; } else { this.tail.next = n; n.prev = tail; } tail = n; this.size++; if(this.size > this.capacity) { // remove from list and map Node removed = sentinel.next; sentinel.next = removed.next; removed.next.prev = sentinel; map.remove(removed.key); this.size--; } } } class Node { Node next, prev; int key, val; public Node(int key, int val) { this.key = key; this.val = val; } } }
-
2. Add Two Numbers
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list. You may assume the two numbers do not contain any leading zero, except the number 0 itself.
public ListNode addTwoNumbers(ListNode l1, ListNode l2) { int carry = 0; ListNode sentinel = new ListNode(0); ListNode curr = sentinel; while(l1 != null || l2 != null) { int x = l1 == null ? 0 : l1.val; int y = l2 == null ? 0 : l2.val; int sum = x + y + carry; carry = sum / 10; curr.next = new ListNode(sum % 10); curr = curr.next; l1 = l1 == null ? null : l1.next; l2 = l2 == null ? null : l2.next; } if(carry != 0) curr.next = new ListNode(1); return sentinel.next; }
-
1. Two Sum
public int[] twoSumHashMap(int[] nums, int target) { HashMap<Integer, Integer> map = new HashMap<>(); for(int i = 0; i < nums.length; i++) { if(map.containsKey(target-nums[i])) { return new int[] {map.get(target-nums[i]), i}; } map.put(nums[i], i); } return null; }
public int[] twoSumTwoPointer(int[] nums, int target) { N[] ns = new N[nums.length]; for(int i = 0; i < nums.length; i++) ns[i] = new N(nums[i], i); Arrays.sort(ns, new Comparator<N>() { public int compare(N n1, N n2) { return Integer.compare(n1.v, n2.v); } }); int l = 0, r = nums.length - 1; while(l < r) { if(ns[l].v + ns[r].v == target) return new int[] { ns[l].i, ns[r].i }; else if(ns[l].v + ns[r].v < target) l++; else r--; } return null; } class N { int v; int i; public N(int v, int i) { this.v = v; this.i = i; } }
-
Financial Accounting Summary
Review Session 1
- What is supply? in balance sheet current assets
- No single line for total of Stockholders’ equity. There is only a “Total liability and Stockholders’ equity.
- No indentation for asset
-
Map Reduce On Wikipedia Pageview Data
The purpose is to get the top viewed pages.
-
Introduction to Algorithms
Start to read Introduction to Algorithms and watch open source videos.
-
Java Fianl Review
Inner class
An anonymous inner class must implement all the abstract methods in the superclass or in the interface. An anonymous inner class is compiled into a class named OuterClassName$n.class An anonymous inner class always uses the no-arg constructor from its superclass
-
Face Recognition with JavaFX and OpenCV
Developed a biometric user identification system using a web camera to identify the student’s faces and display a dashboard of information about the student to the receptionist. The system can also alert the reception to strangers.
-
Java OOP Midterm Review
Time is so fast and now it is the week 5, and the mid-term exam for the Mini 1 semester is coming. I have used a couple of hours to look though the slides before visiting the Kangaroo island. And the day before Java exam, I practiced hundreds of sample questions. Now I put summary here.
-
Start to learn Jekyll
It’s the second week of this semester and a logic time to start building my personal blog. Now I’m begining with github pages and jekyll. This post is about tech issues of learning jekyll.