• 382. Linked List Random Node

    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

    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

    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;
        }
    }
    

  • 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

    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

    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;
        }
    }
    

  • 878. Nth Magical Number

    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);
    }
    

  • 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

    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

    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

    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();
    }
    

  • 1244. Design A Leaderboard

    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);
        }
    }
    

  • 1246. Palindrome Removal

    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

    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

    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

    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;
    }
    

  • 1230. Toss Strange Coins

    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];
    }
    

  • meeting-scheduler

    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);
    }
    

  • 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

    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;
        }
    }
    

  • 1223. Dice Roll Simulation

    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
    
    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];
        }
    }
    

  • 1206. Design Skiplist

    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);
     */
    

  • 1216. Valid Palindrome III

    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

    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;
        }
    }
    

  • 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;
    }
    

  • 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

    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

    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

    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;
    }
    

  • 1032. Stream of Characters

    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;
        }
    }
    

  • 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;
        }
    }
    

  • 895. Maximum Frequency Stack

    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();
     */
    

  • 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);
       }
    

  • 229. Majority Element II

    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;
    }
    

  • 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;
    }
    

  • 419. Battleships in a Board

    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;
    }
    

  • 228. Summary Ranges

    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;
    }
    

  • 316. Remove Duplicate Letters

    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();
    }
    

  • 780. Reaching Points

    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.

    Algorithm

    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

    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;
        }
      }
    

  • 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;
    }
    

  • 877. Stone Game

    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];
    }   } ```
    

  • 877. Stone Game

    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;
    }
    

  • 1049. Last Stone Weight II

    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];
    }
    

  • 1201. Ugly Number III

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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
    

    img

    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

    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

    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

    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:
    

    img

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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.
    

    img

    • 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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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
    

    img

    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).
    

    img1

    • 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

    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;
    }
    

  • 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

    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;
    }
    

  • 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

    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

    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

    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

    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

    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:
    

    img1

    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.
    

    img1

    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

    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

    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

    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

    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

    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.
    

    img1

    After the rain, water is trapped between the blocks. The total volume of water trapped is 4.
    

    img2

    • 地方包围中央,不断垒高墙

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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()

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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:
    

    img1

    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

    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:
    

    img1

    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)

    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

    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

    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)

    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

    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

    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?

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    935. Knight Dialer

    A chess knight can move as indicated in the chess diagram below:
    

    img1

    img2

    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

    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

    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:
    

    img1

    The corresponding quad tree should be as following, where each node is represented as a (isLeaf, val) pair.
    

    img2

    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

    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

    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

    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...
    

    img1

    ...and its solution numbers marked in red.
    

    img2

    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

    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);
        }
      }
      

  • 50. Pow(x, n)

    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;
      }
      

  • 312. Burst Balloons

    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

    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;
      }
      

  • 92. Reverse Linked List II

    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

    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:
    

    p1

    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.
    

    p2

    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.
    

    p3

    • 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

    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

    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

    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

    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

    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).
    

    p1 p2

    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

    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

    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

    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

    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

    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.
    

    p1

    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

    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

    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
        }
      }
      

  • 692. Top K Frequent Words

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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.
    

    example1

    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

    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

    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

    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

    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

    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

    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

    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.
    

    p1

    Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3].
    

    p2

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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”].

    kdf

    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

    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

    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

    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

    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

    912. Sort an Array

    Given an array of integers nums, sort the array in ascending order.
    

    Example1

    • 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

    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

    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

    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

    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

    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

    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

    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

    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;
    }
    

    Example1


  • 49. Group Anagrams

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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

    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.
    

    Example1

    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.
    

    Example1

    • 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

    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

    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

    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

    206. Reverse Linked List

    Reverse a singly linked list.
    
    Example:
    
    Input: 1->2->3->4->5->NULL
    Output: 5->4->3->2->1->NULL
    
    1. 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;
    }
    
    1. 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

    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

    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

    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

    23. Merge k Sorted Lists

    Merge k sorted linked lists and return it as one sorted list.
    Analyze and describe its complexity.
    
    1. 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;
    }
    
    1. 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

    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.
    
    1. 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;
    }
    
    1. 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

    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.
    
    1. 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;
    }
    
    1. 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.
    
    1. 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;
    }
    
    1. 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

    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.
    
    1. 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;
    }
    
    1. 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;
    }
    
    1. 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

    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]; }

    ```


  • 200. Number of Islands

    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.
    
    1. 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);
            }
        }
    }
    
    1. 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.
    
    1. 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};
    }
    
    1. 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();
    }
    
    1. Dynamic Programming

    Define boolean dp[i][j] (if string(i,j) is a palindrome) Increase window size from 1 to n


  • 146. LRU Cache

    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.
    
    1. 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);
        }
    }
    
    1. 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

    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

    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

    1. What is supply? in balance sheet current assets
    2. No single line for total of Stockholders’ equity. There is only a “Total liability and Stockholders’ equity.
    3. 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.