Tuesday, August 2, 2022

327. Count of Range Sum

 一刷 08/2022

Version #2 Merge Sort

看某一个prefix index后面有多少个prefix满足lower <= prefix[j]-prefix[i] <= upper

Time O(NlogN)

Space O(N)

Runtime: 71 ms, faster than 99.21% of Java online submissions for Count of Range Sum.
Memory Usage: 60.9 MB, less than 88.91% of Java online submissions for Count of Range Sum.

class Solution {

    public int countRangeSum(int[] nums, int lower, int upper) {

        long[] prefixSum = new long[nums.length + 1];

        for (int i = 0; i < nums.length; i++) {

            prefixSum[i + 1] = prefixSum[i] + nums[i];

        }

        

        // find count of prefixSum on the right that lower <= prefixSum[j] - prefixSum[i] <= uppder

        // [-2,5,-1,-4]

        // [0, -2, 3, 2, -2]

        // [-2, 0]  [-2, 2, 3]

        int[] count = new int[1];

        mergeSort(prefixSum, new long[prefixSum.length], 0, prefixSum.length - 1, count, lower, upper);

        return count[0];

        

    }

    

    private void mergeSort(long[] nums, long[] aux, int start, int end, int[] count, int lower, int upper) {

        if (start >= end) {

            return;

        }

        int mid = start + (end - start) / 2;

        mergeSort(nums, aux, start, mid, count, lower, upper);

        mergeSort(nums, aux, mid + 1, end, count, lower, upper);

        // left is the first index that nums[left] - num[i] >= lower

        // right is the first index that nums[right] - nums[i] > upper

        int left = mid + 1, right = mid + 1;

        

        

//         int i = mid + 1, j = mid + 1;

// for (int k = low; k <= mid; k++) {

// while (i <= high && pfxSum[i] - pfxSum[k] < lower) i++;  

// while (j <= high && pfxSum[j] - pfxSum[k] <= upper) j++;

            

// count += j - i;

// }

        

        // [0, -2, 3, 2]

        // [-2, 0, 2, 3]

        for (int i = start; i <= mid; i++) {

            while (left <= end && nums[left] - nums[i] < lower) {

                left++;

            }

            while (right <= end && nums[right] - nums[i] <= upper) {

                right++;

            }

            count[0] += right - left;

        }

        

        for (int i = start; i <= end; i++) {

            aux[i] = nums[i];

        }

        

        int pleft = start, pright = mid + 1;

        int p = start;

        while (pleft <= mid && pright <= end) {

            if (aux[pleft] <= aux[pright]) {

                nums[p++] = aux[pleft++];

            } else {

                nums[p++] = aux[pright++];

            }

        }

        while (pleft <= mid) {

            nums[p++] = aux[pleft++];

        }

        while (pright <= end) {

            nums[p++] = aux[pright++];

        }

    }

}



Version #1 Segment Tree[TLE]

Time O(NlogN)

Space O(Range of all prefix sums)


class Solution {

    class Node {

        Node left, right;

        int count;

        int start, end;

        public Node(int start, int end) {

            this.start = start;

            this.end = end;

        }

    }

    public int countRangeSum(int[] nums, int lower, int upper) {

        int[] prefixSum = new int[nums.length + 1];

        int min = 0, max = 0;

        for (int i = 0; i < nums.length; i++) {

            prefixSum[i + 1] = prefixSum[i] + nums[i];

            min = Math.min(min, prefixSum[i + 1]);

            max = Math.max(max, prefixSum[i + 1]);

        }

        Node root = buildTree(min, max);

        int count = 0;

        for (int i = nums.length; i >= 0; i--) {

            // prefixSum[i] - try to find the count of j that lower <= prefixSum[j] - prefixSum[i] <= upper

            // lower + prefixSum[i] <= prefixSum[j] <= upper + prefixSum[j]

            count += query(root, (long)lower + prefixSum[i], (long)upper + prefixSum[i]);

            update(root, prefixSum[i]);

        }

        return count;

    }

    

    private void update(Node node, int num) {

        if (node.start == num && node.end == num) {

            node.count++;

            return;

        }

        int mid = node.start + (node.end - node.start) / 2;

        if (num <= mid) {

            update(node.left, num);

        } else {

            update(node.right, num);

        }

        node.count = node.left.count + node.right.count;

    }

    

    private int query(Node node, long left, long right) {

        // [start, end]

        // [left, right]

        if (left > node.end || right < node.start) {

            return 0;

        }

        if (left <= node.start && right >= node.end) {

            return node.count;

        }

        return query(node.left, left, right) + query(node.right, left, right);

    }

    

    private Node buildTree(int start, int end) {

        Node n = new Node(start, end);

        if (start == end) {

            return n;

        }

        int mid = start + (end - start) / 2;

        n.left = buildTree(start, mid);

        n.right = buildTree(mid + 1, end);

        return n;

    }

}


Monday, August 1, 2022

460. LFU Cache

 一刷 08/2022

Version #1 Map of DoublyLinkedList

Time O(1) for all

Space O(N) - N is number of keys

Runtime: 67 ms, faster than 95.05% of Java online submissions for LFU Cache.
Memory Usage: 134.1 MB, less than 77.48% of Java online submissions for LFU Cache.

class LFUCache {

    // key-the counter, value-the most recent used node with this counter

    Map<Integer, DList> counterToList;

    Map<Integer, Node> keyToNode;

    int cap;

    int minCounter;

    class Node {

        Node prev, next;

        int key;

        int val;

        int counter;

        public Node(int key, int val) {

            this.key = key;

            this.val = val;

            this.counter = 1;

        }

    }

    

    class DList {

        Node head, tail;

        int size;

        public DList() {

            this.head = new Node(0, 0);

            this.tail = new Node(0, 0);

            head.next = tail;

            tail.prev = head;

            this.size = 0;

        }

        

        // add after head

        public void add(Node node) {

            Node next = head.next;

            head.next = node;

            node.next = next;

            node.prev = head;

            next.prev = node;

            size++;

        }

        

        public void remove(Node node) {

            Node prev = node.prev;

            Node next = node.next;

            prev.next = next;

            next.prev = prev;

            size--;

        }

        

        // remove the last element and returns its key

        public int removeLast() {

            if (size == 0) {

                return 0;

            }

            size--;

            Node last = tail.prev;

            Node prev = last.prev;

            prev.next = tail;

            tail.prev = prev;

            return last.key;

        }

    }

    


    public LFUCache(int capacity) {

        this.cap = capacity;

        this.minCounter = 0;

        counterToList = new HashMap<>();

        keyToNode = new HashMap<>();

    }

    

    public int get(int key) {

        if (!keyToNode.containsKey(key)) {

            return -1;

        }

        Node curr = keyToNode.get(key);

        DList list = counterToList.get(curr.counter);

        list.remove(curr);

        if (list.size == 0 && curr.counter == minCounter) {

            minCounter++;

        }

        

        curr.counter++;

        counterToList.putIfAbsent(curr.counter, new DList());

        counterToList.get(curr.counter).add(curr);

        return curr.val;

    }

    

    public void put(int key, int value) {

        if (keyToNode.containsKey(key)) {

            keyToNode.get(key).val = value;

            get(key);

            return;

        }

        if (cap == 0) {

            if (minCounter == 0) {

                return;

            }

            cap++;

            DList list = counterToList.get(minCounter);

            int removedKey = list.removeLast();

            keyToNode.remove(removedKey);

        }

        minCounter = 1;

        cap--;

        Node curr = new Node(key, value);

        counterToList.putIfAbsent(1, new DList());

        counterToList.get(1).add(curr);

        keyToNode.put(key, curr);

    }

}

715. Range Module

 一刷 07/2022

Version #1 TreeMap

思路比较简单就是把interval用key-value pair的形式存在treemap里面,然后维持所有的invervals没有overlap

需要特别处理的case

exists [8, 9)

add [1,8)

这时候首先需要取floowKey(8)获得[8, 9)然后合并获得[1,9)

另外一个case就是

exists [1, 9)

add [9, 10)

取完floorKey获得[1, 9)这时候需要判断的是get(floorKey) >= start

Time Amortized O(logN) - N is number of intervals

Space O(N)

Runtime: 53 ms, faster than 87.80% of Java online submissions for Range Module.
Memory Usage: 70.8 MB, less than 48.91% of Java online submissions for Range Module.

class RangeModule {

    TreeMap<Integer, Integer> map;

    public RangeModule() {

        map = new TreeMap<>();

    }

    

    public void addRange(int left, int right) {

        int start = left, end = right;

        // System.out.printf("left=%d, right=%d\n", left, right);

        Integer lk = map.floorKey(end);

        while (lk != null && map.get(lk) >= left) {

            start = Math.min(start, lk);

            end = Math.max(end, map.get(lk));

            map.remove(lk);

            lk = map.floorKey(end);

            // System.out.printf("start=%d, end=%d\n", start, end);

        }

        map.put(start, end);

    }

    

    public boolean queryRange(int left, int right) {

        Integer lk = map.lowerKey(right);

        if (lk == null) {

            return false;

        }

        if (lk <= left && map.get(lk) >= right) {

            return true;

        }

        return false;

    }

    

    public void removeRange(int left, int right) {

        //    [    ]

        //  [  ]  [  ]

        Integer lk = map.lowerKey(right);

        while (lk != null && map.get(lk) > left) {

            int prevLeft = lk;

            int prevRight = map.get(lk);

            //left right

            // [    ]

            //  pl    pr

            //   [     ]

            map.remove(lk);

            if (prevRight > right) {

                map.put(right, prevRight);

            }

            if (prevLeft < left) {

                map.put(prevLeft, left);

            }

            lk = map.lowerKey(right);

        }

    }

}





1396. Design Underground System

 一刷 07/2022

Version #1 HashMap

Time O(1)

Space O(N^2 + P) - N is number of stations, P is number of passengers

Runtime: 235 ms, faster than 19.99% of Java online submissions for Design Underground System.
Memory Usage: 105 MB, less than 24.78% of Java online submissions for Design Underground System.

class UndergroundSystem {

    // key-startStation,endStation value-sum of all travel times, count of customers

    Map<List<String>, Pair<Long, Integer>> timeMap;

    // key-customer id, value-startStation,checkin time

    Map<Integer, Pair<String, Integer>> checkinMap;

    public UndergroundSystem() {

        this.timeMap = new HashMap<>();

        this.checkinMap = new HashMap<>();

    }

    

    public void checkIn(int id, String stationName, int t) {

        checkinMap.put(id, new Pair(stationName, t));

    }

    

    public void checkOut(int id, String stationName, int t) {

        if (!checkinMap.containsKey(id)) {

            return;

        }

        Pair<String, Integer> checkinInfo = checkinMap.get(id);

        List<String> stations = new ArrayList<>();

        stations.add(checkinInfo.getKey());

        stations.add(stationName);

        int duration = t - checkinInfo.getValue();

        Pair<Long, Integer> stationInfo = timeMap.getOrDefault(stations, new Pair(0l, 0));

        Pair<Long, Integer> nInfo = new Pair(stationInfo.getKey() + duration, stationInfo.getValue() + 1);

        timeMap.put(stations, nInfo);

    }

    

    public double getAverageTime(String startStation, String endStation) {

        Pair<Long, Integer> stationInfo = timeMap.get(new ArrayList<>(Arrays.asList(new String[]{startStation, endStation})));

        return (1.0 * stationInfo.getKey()) / stationInfo.getValue();

    }

}

995. Minimum Number of K Consecutive Bit Flips

 一刷 07/2022

Version #1 Sliding Window

感觉很难,照着答案写还是一知半解的感觉

Time O(N)

Space O(N)

Runtime: 11 ms, faster than 50.79% of Java online submissions for Minimum Number of K Consecutive Bit Flips.
Memory Usage: 92.1 MB, less than 74.80% of Java online submissions for Minimum Number of K Consecutive Bit Flips.

class Solution {

    public int minKBitFlips(int[] nums, int k) {

        // number of flips in the sliding window

        int flipCounter = 0;

        int flips = 0;

        boolean[] isFlipped = new boolean[nums.length];

        for (int i = 0; i < nums.length; i++) {

            if (i >= k && isFlipped[i - k]) {

                flipCounter--;

            }

            // check if we need to flip current bit

            if (flipCounter % 2 == nums[i]) {

                if (i + k > nums.length) {

                    return -1;

                }

                isFlipped[i] = true;

                flipCounter++;

                flips++;

            }

        }

        return flips;

    }

}

Saturday, July 30, 2022

1136. Parallel Courses

 一刷 07/2022

Version #1 Topological Sort

Time O(N + E)

Space O(N + E)

Runtime: 6 ms, faster than 97.21% of Java online submissions for Parallel Courses.
Memory Usage: 43.1 MB, less than 97.94% of Java online submissions for Parallel Courses.

class Solution {

    public int minimumSemesters(int n, int[][] relations) {

        int[] prevCount = new int[n];

        List<Integer>[] nextCourses = new ArrayList[n];

        for (int i = 0; i < n; i++) {

            nextCourses[i] = new ArrayList<>();

        }

        for (int[] relation : relations) {

            int prev = relation[0] - 1;

            int next = relation[1] - 1;

            nextCourses[prev].add(next);

            prevCount[next]++;

        }

        int taken = 0;

        int semester = 0;

        Queue<Integer> que = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {

            if (prevCount[i] == 0) {

                que.offer(i);

            }

        }

        while (!que.isEmpty()) {

            int size = que.size();

            semester++;

            // 1 - 3 - 2

            while (size-- > 0) {

                int curr = que.poll();

                taken++;

                for (int next : nextCourses[curr]) {

                    prevCount[next]--;

                    if (prevCount[next] == 0) {

                        que.offer(next);

                    }

                }

            }

        }

        return taken == n ? semester : -1;

    }

}

Friday, July 29, 2022

486. Predict the Winner

 一刷 07/2022

Version #1 DP game theory

Time O(N^2)

Space O(N^2) - space can be optimized to O(N)

Runtime: 0 ms, faster than 100.00% of Java online submissions for Predict the Winner.
Memory Usage: 39.9 MB, less than 87.08% of Java online submissions for Predict the Winner.

class Solution {

    public boolean PredictTheWinner(int[] nums) {

        int len = nums.length;

        int[][] dp = new int[len][len];

        // dp[i][j] - max score diff that we can get between nums [j, i]

        for (int i = 0; i < nums.length; i++) {

            for (int j = i; j >= 0; j--) {

                if (i == j) {

                    dp[i][j] = nums[i];

                } else {

                    // [j, i]

                    dp[i][j] = Math.max(nums[i] - dp[i - 1][j], nums[j] - dp[i][j + 1]);

                }

            }

        }

        return dp[len - 1][0] >= 0;

    }

}