Thursday, August 10, 2017

315. Count of Smaller Numbers After Self

三刷 07/2022

Version #4 Merge Sort
这个思路不看答案是完全想不到的,但是速度很快

For each element i, the function records the number of elements jumping from i's right to i's left during the merge sort.

当merge的时候如果left element小于right element,此时要放置left,那么所有在这个left之前放下去的right都是比它小且在它右边的
Time O(NlogN)
Space O(N)

Runtime: 58 ms, faster than 93.73% of Java online submissions for Count of Smaller Numbers After Self.
Memory Usage: 61.1 MB, less than 88.66% of Java online submissions for Count of Smaller Numbers After Self.

class Solution {
    public List<Integer> countSmaller(int[] nums) {
        // merge sort the indices
        int[] indices = new int[nums.length];
        for (int i = 0; i < nums.length; i++) {
            indices[i] = i;
        }
        int[] count = new int[nums.length];
        sort(nums, indices, count, 0, nums.length - 1, new int[nums.length]);
        List<Integer> result = new ArrayList<>();
        for (int cnt : count) {
            result.add(cnt);
        }
        return result;
    }
    
    private void sort(int[] nums, int[] indices, int[] count, int start, int end, int[] aux) {
        if (start >= end) {
            return;
        }
        int mid = start + (end - start) / 2;
        sort(nums, indices, count, start, mid, aux);
        sort(nums, indices, count, mid + 1, end, aux);
        merge(nums, indices, count, start, end, aux);
    }
    
    private void merge(int[] nums, int[] indices, int[] count, int start, int end, int[] aux) {
        for (int i = start; i <= end; i++) {
            aux[i] = indices[i];
        }
        int mid = start + (end - start) / 2;
        int left = start, right = mid + 1;
        int p = start;
        //              m+1 r
        //   1 4 5       2  6 8
        //   1 2 4
        while (left <= mid && right <= end) {
            if (nums[aux[left]] <= nums[aux[right]]) {
                indices[p] = aux[left];
                count[indices[p]] += right - (mid + 1);
                left++;
            } else {
                indices[p] = aux[right];
                right++;
            }
            p++;
        }
        while (left <= mid) {
            indices[p] = aux[left];
            count[indices[p]] += right - (mid + 1);
            left++;
            p++;
        }
        while (right <= end) {
            indices[p] = aux[right];
            right++;
            p++;
        }
    }
}




Version #3 Segment Tree
Time O(Nlog(max - min))
Space O(2(max - min))

Runtime: 248 ms, faster than 39.95% of Java online submissions for Count of Smaller Numbers After Self.
Memory Usage: 131 MB, less than 55.08% of Java online submissions for Count of Smaller Numbers After Self.

class Solution {
    class Node {
        int start, end;
        int count;
        Node left, right;
        public Node(int start, int end) {
            this.start = start;
            this.end = end;
        }
    }
    
    public List<Integer> countSmaller(int[] nums) {
        int min = (int)(1e4);
        int max = -(int)(1e4);
        for (int num : nums) {
            min = Math.min(min, num);
            max = Math.max(max, num);
        }
        Node root = buildTree(min, max);
        List<Integer> result = new LinkedList<>();
        for (int i = nums.length - 1; i >= 0; i--) {
            result.add(0, query(nums[i], root));
            update(nums[i], root);
        }
        return result;
    }
    
    private int query(int num, Node root) {
        if (root.start == root.end) {
            if (root.start < num) {
                return root.count;
            }
            return 0;
        }
        // [start, mid] [mid + 1, end]
        // count of numbers smaller than num
        int mid = root.start + (root.end - root.start) / 2;
        if (num <= mid) {
            return query(num, root.left);
        }
        return root.left.count + query(num, root.right);
    }
    
    private void update(int num, Node root) {
        if (root.start == num && root.end == num) {
            root.count++;
            return;
        }
        int mid = root.start + (root.end - root.start) / 2;
        if (num <= mid) {
            update(num, root.left);
        } else {
            update(num, root.right);
        }
        root.count = root.left.count + root.right.count;
    }
    
    private Node buildTree(int start, int end) {
        if (start == end) {
            return new Node(start, start);
        }
        Node root = new Node(start, end);
        int mid = start + (end - start) / 2;
        root.left = buildTree(start, mid);
        root.right = buildTree(mid + 1, end);
        return root;
    }
}


二刷
Version #2 Prefered BIT

有两个非常非常重要的点
第一个index 必须start from 1,否则在 i += (i & (-i))上就会进入死循环
第二个就是因为有负数,所以不能直接用nums[i]来表示index
discuss里面的做法是给所有的数都加一个offset,看了一个更好的做法就是sort以后给所有的数一个rank,然后用这个rank作为index,这样也避免了
[1, 1000]这种数据浪费空间的问题,非常厉害了

时间复杂度
initial sort O(nlogn)
iterate throught nums(n) and update(logn) and query(logn) -> O(nlogn)
所以total Time O(nlogn)
Space O(# nums)

50.74 %
class Solution {
    private int[] tree;
    private int[] count;
    public List<Integer> countSmaller(int[] nums) {
        // use num as index, count as value
        TreeSet<Integer> treeSet = new TreeSet<>();
        for (int num : nums) {
            treeSet.add(num);
        }
        int rank = 1;
        // map num value to its rank
        Map<Integer, Integer> map = new HashMap<>();
        Iterator<Integer> iter = treeSet.iterator();
        while (iter.hasNext()) {
            int num = iter.next();
            map.put(num, rank);
            rank++;
        }
     
        // rank equals to last rank + 1
        List<Integer> result = new ArrayList<>();
        tree = new int[rank];
        count = new int[rank];
        for (int i = nums.length - 1; i >= 0; i--) {
            int rankIndex = map.get(nums[i]);
            count[rankIndex]++;
            updateTree(rankIndex, 1);
            result.add(query(rankIndex - 1));
        }
        Collections.reverse(result);
        return result;
    }
 
    private void updateTree(int i, int val) {
        while (i < tree.length) {
            tree[i] += val;
            i += (i & (-i));
        }
    }
 
    private int query(int i) {
        int sum = 0;
        while (i > 0) {
            sum += tree[i];
            i -= (i & (-i));
        }
        return sum;
    }
}


Version #1 Self-defined BST
写了 insert 函数,哈哈
Every insertion O(height) -> O(logn) ~ O(n)
n times insertion
Total O(nlogn) ~ O(n^2)

86.26 %
class Solution {
    private class TreeNode {
        int leftCount;
        int currCount;
        int val;
        TreeNode left;
        TreeNode right;
        TreeNode(int val) {
            this.val = val;
            currCount = 1;
        }
    }
    public List<Integer> countSmaller(int[] nums) {
        // Insert a node
        List<Integer> result = new ArrayList<>();
        if (nums == null || nums.length == 0) {
            return result;
        }
        TreeNode root = new TreeNode(nums[nums.length - 1]);
        result.add(0);
        for (int i = nums.length - 2; i >= 0; i--) {
            int count = insertNode(root, nums[i]);
            result.add(count);
        }
        Collections.reverse(result);
        return result;
    }
    private int insertNode(TreeNode root, int val) {
        TreeNode curr = root;
        int count = 0;
        while (curr != null) {
            if (val == curr.val) {
                curr.currCount++;
                count += curr.leftCount;
                break;
            } else if (val < curr.val) {
                curr.leftCount++;
                if (curr.left != null) {
                    curr = curr.left;
                } else {
                    curr.left = new TreeNode(val);
                    break;
                }
            } else { // val > curr.val
                count += curr.leftCount + curr.currCount;
                if (curr.right != null) {
                    curr = curr.right;
                } else {
                    curr.right = new TreeNode(val);
                    break;
                }
            }
        }
        return count;
    }
}


一刷
应该改进一下写一个Insert函数

66.11 % 


/* TreeNode int val, selfCount, leftCount
      /    \
   left   right
*/
class TreeNode {
    public int selfCount;
    public int leftCount;
    public TreeNode left, right;
    public int val;
    public TreeNode(int val) {
        this.val = val;
        selfCount = 1;
        leftCount = 0;
    }
}
public class Solution {
    public List<Integer> countSmaller(int[] nums) {
        List<Integer> result = new ArrayList<>();
        if (nums == null || nums.length == 0) return result;
        TreeNode root = new TreeNode(nums[nums.length - 1]);
        result.add(0);
        TreeNode curr = null;
        int pathCount = 0;
        for (int i = nums.length - 2; i >= 0; i--) {
            curr = root;
            pathCount = 0;
            int value = nums[i];
            while (curr != null) {
                if (curr.val == value) {
                    pathCount += curr.leftCount;
                    curr.selfCount++;
                    break;
                // go right
                } else if (value > curr.val) {
                    pathCount += curr.selfCount + curr.leftCount;
                    if (curr.right == null) {
                        curr.right = new TreeNode(value);
                        curr = curr.right;
                    }
                    curr = curr.right;
                // go left
                } else {
                    curr.leftCount++;
                    if (curr.left == null) {
                        curr.left = new TreeNode(value);
                        curr = curr.left;
                    }
                    curr = curr.left;
                }
            }
            result.add(pathCount);
        }
        Collections.reverse(result);
        return result;
    }
}


Wednesday, August 9, 2017

260. Single Number III


一刷 07/2022
Version #1 Bitmask
i & (-i) 可以找到i从右向左看的第一个'1' bit

Time O(N)
Space O(1)
Runtime: 1 ms, faster than 100.00% of Java online submissions for Single Number III.
Memory Usage: 45.6 MB, less than 26.86% of Java online submissions for Single Number III.

class Solution {
    public int[] singleNumber(int[] nums) {
        int bitmask = 0;
        for (int num : nums) {
            bitmask ^= num;
        }
        // The right most bit of one single number
        int diff = bitmask & (-bitmask);
        int x = 0;
        for (int num : nums) {
            if ((num & diff) != 0) {
                x ^= num;
            }
        }
        
        return new int[]{x, bitmask ^ x};
    }
}

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 absolutedifference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

二刷
Version #2 Bucket
Time O(n)
Space O(n / t)
65.99 %
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (t < 0) {
            return false;
        }
        // window [index - k, index]
        // Put all elements into buckets
        // position = nums[index] / (t + 1)
        // e.g. t=2  position   0      1      2
        //           bucket   0,1,2  3,4,5  6,7,8
        // If element is negative, we'll have negative index -> we need to normalize -> subtract minimum num from all elements
        int min = Integer.MAX_VALUE;
        for (int num : nums) {
            min = Math.min(min, num);
        }
        long lt = Long.valueOf(t);
        Map<Long, Long> map = new HashMap<>();
        for (int index = 0; index < nums.length; index++) {
            long curr = Long.valueOf(nums[index]) - min;
            long position = curr / (lt + 1);
            if (map.containsKey(position)
               || (map.containsKey(position - 1) && curr - map.get(position - 1) <= t)
               || (map.containsKey(position + 1) && map.get(position + 1) - curr <= t)) {
                return true;
            }
            map.put(position, curr);
            if (index >= k) {
                map.remove((Long.valueOf(nums[index - k]) - min) / (lt + 1));
            }
        }
        return false;   
    }
}

Version #1 TreeSet
Time O(nlogk) Space O(k)
一直纠结用TreeSet的话,是怎么处理duplicate的问题
譬如 [6, 0, 6, 8, 17]  k = 2, t = 1 -> 当window [0, 6, 8] 的时候remove 第一个6, 就会把第二个6 也从set里面remove出去
后来想了一下,我是不是傻子? 如果window 有duplicate, 那么 6 - 6 = 0 符合一切 diff < t, 在之前一步就已经return true了

20.87 %
class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        // window [index - k, index]
        TreeSet<Integer> set = new TreeSet<>();
        for (int index = 0; index < nums.length; index++) {
            Integer ceil = set.ceiling(nums[index]);
            Integer floor = set.floor(nums[index]);
            if ((ceil != null && Long.valueOf(ceil) - Long.valueOf(nums[index]) <= t)
               || (floor != null && Long.valueOf(nums[index]) - Long.valueOf(floor) <= t)) {
                return true;
            }
            set.add(nums[index]);
            if (index >= k) {
                set.remove(nums[index - k]);
            }
        }
        return false;
    }
}

一刷
给一个unsorted array nums[i]
一个sliding window size k
把window里面的element 都放进TreeSet 里面, 用logk的时间找到离nums[i]最近的比nums[i] 大或等于的数/小或等于的数
如果diff小于k,就返回true

concern #1 duplicates
window 是在index i 以前, 因为num[i]还没有加入到TreeSet里面 所以不会被吃掉
即使TreeSet里面有和nums[i] 一样的数也是可以找到的

concern #2 window size = k
向后找index = i,那它和window最左边的元素的坐标差是?
e.g.
k = 3
 index  0   [1    2    3]   4    5    6
index 4 和index 1之差是3 , 3 == k


写了一个bug
Input:[-1,-1] 1 0
Output:false
Expected:true

问题是写了k <= 1就返回false
其实 k 是可以等于1的,这样是两数相邻的情况



还有一个溢出的问题
Input:[-1,2147483647] 1 2147483647
Output:true
Expected:false


Version #1
TreeSet
ceiling(E e)
Returns the least element in this set greater than or equal to the given element, or null if there is no such element.
floor(E e)
Returns the greatest element in this set less than or equal to the given element, or null if there is no such element.


22.27 %
Time O(nlogk)
Space O(k)

public class Solution {
    public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) {
        // k is window size
        // t is max diff
        if (nums == null || nums.length <= 1 || k < 1) return false;
        // set of numbers
        TreeSet<Integer> set = new TreeSet<>();
        for (int i = 0; i < nums.length; i++) {
            // candidate is the closest number smaller than nums[i]
            Long diff = 0L;
            Integer smaller = set.floor(nums[i]);
            if (smaller != null) {
                diff = nums[i] - (long)smaller;
                if (diff <= t) return true;
            }
         
            Integer larger = set.ceiling(nums[i]);
            if (larger != null) {
                diff = (long)larger - nums[i];
                if (diff <= t) return true;
            }
     
            // k = 3
            // index 0 1 2 3
            if (set.size() == k) {
                set.remove(nums[i - k]);
            }
            set.add(nums[i]);
        }
        return false;
    }
}

Version #2
算法哥写了一个lower bound和upper bound的方法
可以少做一次treeset search




Tuesday, August 8, 2017

8. String to Integer (atoi)

Version #2 不用Long [TODO]
懒得想了


Version #1 纯自己发明的粗糙的方法 用Long放结果然后check溢出
遇到非digit位直接返回之前的计算结果
e.g. 123a123 return 123

大于Integer.MAX_VALUE 直接返回Integer.MAX_VALUE
小于Integer.MIN_VALUE  直接返回Integer.MIN_VALUE

像+-123这种直接返回0


所以,有必要解释一下题目的要求:

1. 首先需要丢弃字符串前面的空格;

2. 然后可能有正负号(注意只取一个,如果有多个正负号,那么说这个字符串是无法转换的,返回0。比如测试用例里就有个“+-2”);

3. 字符串可以包含0~9以外的字符,如果遇到非数字字符,那么只取该字符之前的部分,如“-00123a66”返回为“-123”;

4. 如果超出int的范围,返回边界值(2147483647或-2147483648)。


 77.46 % 



public class Solution {
    public int myAtoi(String str) {
        if (str == null || str.length() == 0) return 0;
        char[] chars = str.trim().toCharArray();
        Long result = 0L;
        boolean negative = false;
        for (int i = 0; i < chars.length; i++) {
            if (result > Integer.MAX_VALUE) break;
     
            if(chars[i] == '-') {
                if (i == 0) negative = true;
                else return 0;
            }
            else if (chars[i] == '+') {
                if (i == 0) negative = false;
                else return 0;
            }
            else {
                //check if it is a digit
                int digit = chars[i] - '0';
                if (digit < 0 || digit > 9) break;
                result = result * 10 + digit;
            }
        }
        if (negative) {
            result = -1L * result;
            if (result < Integer.MIN_VALUE) return Integer.MIN_VALUE;
        }
        if (result > Integer.MAX_VALUE) return Integer.MAX_VALUE;
        return result.intValue();
    }
}


73. Set Matrix Zeroes

二刷
和一刷做法基本一样
第一行表示每一个column是否是0,第一列表示每一行是否是0
matrix[0][0] 既可以表示第一行也可以表示第一列所以产生了歧义
r0代表的是第一行是否是0
matrix[0][0]表示第一列是否是0

Time O(mn) Space O(1)
100.00 %
class Solution {
    public void setZeroes(int[][] matrix) {
        // matrix[0][j] -> for the columns
        // matrix[i][0] -> for the rows
        int rows = matrix.length, cols = matrix[0].length;
        int r0 = -1; // r0 for the 1st row
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (matrix[i][j] == 0) {
                    if (i == 0) {
                        r0 = 0;
                    } else {
                        matrix[i][0] = 0;
                    }
                    matrix[0][j] = 0;
                }
            }
        }
        for (int i = 1; i < rows; i++) {
            for (int j = 1; j < cols; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        if (matrix[0][0] == 0) {
            for (int i = 0; i < rows; i++) {
                matrix[i][0] = 0;
            }
        }
        if (r0 == 0) {
            for (int j = 0; j < cols; j++) {
                matrix[0][j] = 0;
            }
        }
    }
}

一刷
两个bug
1.虽然boolean 的default值是false,但是如果在后面要用这个boolean值而没有初始化的话,会报"XXX might not be initialized" 所以必须初始化

2.第二遍置零的时候两层for loop 都要从1开始,一旦matrix[0][0]是0的话后面整个matrix都是全0

一开始想法是遇到0之后把它的行和列都设为x,最后再扫一遍
但是这样的时间复杂度是O(mn(m + n))


现在这个方法用第一行和第一列代表了整个matrix的状态,用两个boolean 代表了第一行和第一列的状态,时间复杂度是O(mn)
22.97 %

public class Solution {
 
    //use the 1st row/column to represent which column/row should be set to zeroes
    //use 2 booleans (firstRow, firstCol) to represent if the first row/column shoule be set to zeroes
    public void setZeroes(int[][] matrix) {
        if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return;
        int rows = matrix.length;
        int cols = matrix[0].length;
        boolean firstRow = false, firstCol = false;
        for (int row = 0; row < rows; row++) {
            for (int col = 0; col < cols; col++) {
                if (matrix[row][col] == 0) {
                    if (row == 0) firstRow = true;
                    if (col == 0) firstCol = true;
                    matrix[row][0] = 0;
                    matrix[0][col] = 0;        
                }
            }
        }
     
        for (int row = 1; row < rows; row++) {
            for (int col = 1; col < cols; col++) {
                if (matrix[row][0] == 0 || matrix[0][col] == 0) {
                    matrix[row][col] = 0;      
                }
            }
        }
        if (firstRow) {
            for (int j = 0; j < cols; j++) {
                matrix[0][j] = 0;
            }
        }
        if (firstCol) {
            for (int i = 0; i < rows; i++) {
                matrix[i][0] = 0;
            }
        }
     
    }
}

207. Course Schedule

三刷

Version #1 Topological sort with queue
Time O(E + V)
Space O(E + V)
Runtime: 7 ms, faster than 62.28% of Java online submissions for Course Schedule.
Memory Usage: 47.1 MB, less than 68.33% of Java online submissions for Course Schedule.

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // prerequisites[i] = [ai, bi]
        // bi -> ai
        ArrayList<Integer>[] graph = new ArrayList[numCourses];
        for (int i = 0; i < graph.length; i++) {
            graph[i] = new ArrayList<>();
        }
        int[] inDegree = new int[numCourses];
        for (int[] prerequisite : prerequisites) {
            int from = prerequisite[1];
            int to = prerequisite[0];
            inDegree[to]++;
            graph[from].add(to);
        }
        Queue<Integer> que = new ArrayDeque<>();
        for (int i = 0; i < inDegree.length; i++) {
            if (inDegree[i] == 0) {
                que.offer(i);
            }
        }
        int taken = 0;
        while (!que.isEmpty()) {
            Integer curr = que.poll();
            taken++;
            for (Integer next : graph[curr]) {
                inDegree[next]--;
                if (inDegree[next] == 0) {
                    que.offer(next);
                }
            }
        }
        return taken == numCourses;
    }
}



Version #1 DFS + pruning
去掉pruning以后直接TLE了。。。
看到有人的答案没有pruning也过了,大概beats 20%左右

以前以为topological sort直接就是算indegree
看了算法哥的课以后,才发现其实更直观的方法是dfs
不仅能够判断有没有环,而且能够输出sort之后的路径
如果需要路径的话,直接在end of the recursion的地方path.add(0, node)就可以了
因为一定是之后的nodes都遍历过之后,当前node才会return,所以之后的node都已经在当前node之前添加过了

二刷
69.01 %
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        // [0, 1] 1 is prerequisite of 0
        List<List<Integer>> graph = buildGraph(numCourses, prerequisites);
        Set<Integer> visited = new HashSet<>();
        Set<Integer> path = new HashSet<>();
        for (int i = 0; i < numCourses; i++) {
            if (hasCycle(i, graph, visited, path)) return false;
        }
        return true;
    }
    private boolean hasCycle(int root, List<List<Integer>> graph, Set<Integer> visited, Set<Integer> path) {
        if (path.contains(root)) return true;
        if (visited.contains(root)) return false;
        List<Integer> neighbors = graph.get(root);
        path.add(root);
        for (int neighbor : neighbors) {
            if (hasCycle(neighbor, graph, visited, path)) {
                path.remove(root);
                return true;
            }
        }
        path.remove(root);
        visited.add(root);
        return false;
    }
    private List<List<Integer>> buildGraph(int numCourses, int[][] prerequisites) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
        }
        int start = 0;
        int end = 0;
        for (int[] prerequisite : prerequisites) {
            graph.get(prerequisite[1]).add(prerequisite[0]);
        }
        return graph;
    }
}

73.76 % 

public class Solution {
    // To check if there's any cycle
    //      course   prerequisite
    //      [0,      1          ]  1 --> 0
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (prerequisites == null || prerequisites.length == 0) return true;
        //index-course, value-next courses to take
        List<List<Integer>> graph = buildGraph(numCourses,  prerequisites);
        Set<Integer> visited = new HashSet<>();
        Boolean[] finishMem = new Boolean[numCourses];
        for (int i = 0; i < numCourses; i++) {
            if (!canFinish(i, graph, visited, finishMem)) return false;
        }
        return true;
    }
 
    private boolean canFinish(Integer course, List<List<Integer>> graph, Set<Integer> visited, Boolean[] finishMem) {
        // Starting from current course, we want to make sure that there isn't any cycles
        if (finishMem[course] != null) return finishMem[course];
        if (visited.contains(course)) {
            finishMem[course] = false;
            return false;
        }
     
        visited.add(course);
        boolean currCanFinish = true;
        for (Integer neighbor : graph.get(course)) {
            if (!canFinish(neighbor, graph, visited, finishMem)) {
                currCanFinish = false;
                break;
            }
        }
        visited.remove(course);
        finishMem[course] = currCanFinish;
        return currCanFinish;
    }
 
    private List<List<Integer>> buildGraph(int numCourses, int[][] prerequisites) {
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<Integer>());
        }
        for (int[] prerequisite : prerequisites) {
            //pre[0] is curr, pre[1] is pre
            //pre[1] -> pre[0] in graph
            graph.get(prerequisite[1]).add(prerequisite[0]);
        }
        return graph;
    }
 
}

Version #2 Topological Sort
二刷
21.55 %
class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        int[] indegree = new int[numCourses];
        // to add all courses with indegrees of zero into queue -> means they don't have any prerequisite
        // when we polled one course out, we finished this course and all courses whose prerequisite contains this course can do a indegree--
        // if any courses indegree becomes zero, we can push it into queue
        Map<Integer, List<Integer>> map = new HashMap<>();
        for (int[] pre : prerequisites) {
            // pre[0] -> curr, pre[1] -> pre
            map.computeIfAbsent(pre[1], list -> new ArrayList<>()).add(pre[0]);
            indegree[pre[0]]++;
        }
        Queue<Integer> que = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) {
                que.offer(i);
            }
        }
        int visited = 0;
        while (!que.isEmpty()) {
            int curr = que.poll();
            visited++;
            if (!map.containsKey(curr)) continue;
            for (int next : map.get(curr)) {
                if (indegree[next] > 0) {
                    indegree[next]--;
                    if (indegree[next] == 0) {
                        que.offer(next);
                    }
                }
            }
        }
        return visited == numCourses;
    }
}

一刷
典型的topological sorting with in-degree
这里不用写出sorting 的顺序,只要判断有没有环
判断依据是把所有indegree为0的点加入que,记录一个visitedCount
如果最终visit点的个数和nodes个数不相等证明有环,证明把所有indegree为0的点删除后图上面还有剩余的点

 71.11 %
public class Solution {
    //[0,1]  [curr, pre]
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        if (prerequisites == null || prerequisites.length == 0) return true;
        Integer[] indegree = new Integer[numCourses];
        List<List<Integer>> graph = buildGraph(numCourses, prerequisites, indegree);
        int visitedCount = 0;
        Queue<Integer> que = new LinkedList<>();
        for (int i = 0; i < numCourses; i++) {
            if (indegree[i] == 0) que.add(i);
        }
        while (!que.isEmpty()) {
            List<Integer> neighbors = graph.get(que.poll());
            visitedCount++;
            for(Integer neighbor : neighbors) {
                if (--indegree[neighbor] == 0) que.add(neighbor);
            }
        }
        return visitedCount == numCourses;
    }
    private List<List<Integer>> buildGraph(int numCourses, int[][] prerequisites, Integer[] indegree) {
        List<List<Integer>> graph = new ArrayList<>();
        //index->course number, list->its next neighbors
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
            indegree[i] = 0;
        }
        for (int[] prerequisite : prerequisites) {
            //prerequisite[0] -> curr
            //prerequisite[1] -> pre
            graph.get(prerequisite[1]).add(prerequisite[0]);
            indegree[prerequisite[0]] += 1;
        }
        return graph;
    }
}


Monday, August 7, 2017

459. Repeated Substring Pattern

Version #3 九章答案TODO

Version #2 KMP之类的?懒得想了实在

Version #1 无脑分割然后append然后与原string比较
10.35 %
public class Solution {
    public boolean repeatedSubstringPattern(String s) {
        if (s == null || s.length() <= 1) return false;
        int length = s.length();
        // 0, 1, 2, 3   length = 4
        //sub = 0, 1    s.subString(0, length / 2)
        // 0, 1, 2      length = 3
        //sub = 0, 1    s.subString(0, length / 2) ... we don't care
     
        for (int end = 1; end <= length / 2; end++) {
            String sub = s.substring(0, end);
            if (isRepeated(s, sub)) return true;
        }
        return false;
    }
    private boolean isRepeated(String s, String sub) {
        int shortLength = sub.length();
        int longLength = s.length();
        if (longLength % shortLength != 0) return false;
        int multi = longLength / shortLength;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < multi; i++) {
            sb.append(sub);
        }
        //System.out.println(sb.toString());
        return sb.toString().equals(s);
     
    }
}