AdSense

Saturday, July 23, 2016

Kth Smallest Number in Sorted Matrix

Find the kth smallest number in at row and column sorted matrix.
Example
Given k = 4 and a matrix:
[
  [1 ,5 ,7],
  [3 ,7 ,8],
  [4 ,8 ,9],
]
return 5

Use a PriorityQueue. First put the first row / column into the queue. Then poll out the first value, and add the next value (next row / column after this) into the queue for k - 1 times.


 public int kthSmallest(int[][] matrix, int k) {
        PriorityQueue<Point> queue = new PriorityQueue<>();
        int rows = matrix.length;
        int cols = matrix[0].length;
        for (int i = 0; i < cols; i++) {
            queue.add(new Point(matrix[0][i], 0, i));
        }
        
        while (k > 1) {
            Point curr = queue.poll();
            if (curr.x < rows - 1) {
                int x = curr.x;
                int y = curr.y;
                queue.add(new Point(matrix[x + 1][y], x + 1, y));
            }
            k--;
        }
        return queue.poll().val;
    }
    
    private class Point implements Comparable<Point> {
        int x;
        int y;
        int val;
        
        public Point (int val, int x, int y) {
            this.val = val;
            this.x = x;
            this.y = y;
        }
        
        @Override
        public int compareTo (Point another) {
            return this.val - another.val;
        }
    }

Monday, July 18, 2016

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 skylineformed by these buildings collectively (Figure B).
BuildingsSkyline Contour
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_MAX0 < 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], ...]

The basic idea is that whenever there is a height difference, we need to record it as an outline. To implement it, we can use a list to store all defining points of each building. The list is sorted from left to right to get the order of occurrence of each building. To distinguish the left and right side, we select one as positive and one as negative. We then use a priority queue to track the heights of the buildings. When left point is seen, we add the height to the queue, when right point is seen, we know we no longer need to consider the building, so we remove the height from the queue. The top of the queue is the highest building we need to consider, so if it's different from the previous one, we are seeing a height difference, so we need to add the point to the result. Remember to add height 0 to the queue at first. This allows us to track if there is a gap between two buildings.


public List getSkyline(int[][] buildings) {
        List keyPoints = new ArrayList<>();
        List rst = new ArrayList<>();
        
        for (int[] point : buildings) {
            keyPoints.add(new int[]{point[0], -point[2]});
            keyPoints.add(new int[]{point[1], point[2]});
        }
        
        Collections.sort(keyPoints, new Comparator() {
            
           @Override
           public int compare(int[] first, int[] second) {
               if (first[0] != second[0]) {
                   return first[0] - second[0];
               } else {
                   return first[1] - second[1];
               }
           } 
        });
        
        int prev = -1;

        PriorityQueue heights = new PriorityQueue<>(new Comparator() {
            
            @Override
            public int compare(Integer a, Integer b) {
                return b - a;
            }
            
        });
        heights.add(0);
        for(int[] point : keyPoints) {
            if (point[1] < 0) {
                heights.add(-point[1]);
            } else {
                heights.remove(point[1]);
            }
            int curr = heights.peek();
            if (curr != prev) {
               rst.add(new int[]{point[0], curr});
                prev = curr;
            }
        }
        return rst;
    }

Tuesday, July 12, 2016

Russian Doll Envelopes


You have a number of envelopes with widths and heights given as a pair of integers (w, h). One envelope can fit into another if and only if both the width and height of one envelope is greater than the width and height of the other envelope.
What is the maximum number of envelopes can you Russian doll? (put one inside other)
Example:
Given envelopes = [[5,4],[6,4],[6,7],[2,3]], the maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]).

The first thought of this problem is to sort the array and uses DP. However, it requires O(n2) complexity so definitely won't be accepted.

To optimize, we still sort the array. Instead of using regular sorting method, we put a[i] in front of a[j] if a[i][0] = a[j][0] && a[i][1] > a[j][1]. I will explain why we do this later.

Then we create a list and use binary search to find the position of each element in the list. If the position is larger than the size of the list, we append the element to the list, otherwise we set the value of the position as the element.

Now let's explain why we want to reverse the order if two elements contains the same a[i][0]. Consider we order it in the normal way. Then the position of the new element will get a position that is larger than the inserted one, which has the same a[i][0]. This will make us insert one more element, which is not correct.

Note:
1. why do we need to do "low <= high" instead of "low < high"?
Because if the first element is [2, 3] and the second is [5, 4], we want to add a new one instead of overriding the first one.
2. Why do we only check midA[1] < curr[1]?
Because we sort the array by o1[0] < o2[0], so curr[0] is guaranteed to be at least equal to midA[1].


public int maxEnvelopes(int[][] envelopes) {
              int len = envelopes.length;
        if (len <= 1) {
            return len;
        }
        Arrays.sort(envelopes, new Comparator() {

            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[0] < o2[0] || (o1[0] == o2[0] && o1[1] > o2[1]))
                    return -1;
                else if (o1[0] == o2[0] && o1[1] == o2[1])
                    return 0;
                else
                    return 1;
            }
        });

        List<int[]> rst = new ArrayList<>();
        for (int[] curr : envelopes) {
            int low = 0, high = rst.size() - 1;
            while (low <= high) {
                int mid = (low + high) / 2;
                int[] midA = rst.get(mid);
                if (midA[1] < curr[1]) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            }
            if (low < rst.size()) {
                rst.set(low, curr);
            } else {
                rst.add(curr);
            }


        }
        return rst.size();
        
    }

Monday, July 11, 2016

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:
Given matrix = [
  [1,  0, 1],
  [0, -2, 3]
]
k = 2
The answer is 2. Because the sum of rectangle [[0, 1], [-2, 3]] is 2 and 2 is the max number no larger than k (k = 2).
Note:
  1. The rectangle inside the matrix must have an area > 0.
  2. What if the number of rows is much larger than the number of columns?

This problem utilizes an algorithm called Kadane's algorithm. To understand it using the link provided is quite an effort. Briefly speaking, it first fixes the left bound and right bound of rows. Then it creates a sum array of dimension equals to the column of the matrix, then find contiguous sum of each row. After that, it moves the left bound and right bound to find the max value.

Remember to switch cols and rows if rows is larger than cols.

The algorithm also uses a TreeSet. This set is used to track the previous sum within current fixed left bound and right bound. If a sum that is larger than k, we can find a smallest sum that when current sum reduce that one, it will be smaller than k.


public int maxSumSubmatrix(int[][] matrix, int k) {
        int rows = matrix.length;
        int cols = matrix[0].length;
        if (rows == 0 || cols == 0)
            return 0;
        int maxValue = Integer.MIN_VALUE;
        
        int length, width;
        if (rows >= cols) {
            length = rows;
            width = cols;
        } else {
            length = cols;
            width = rows;
        }
        
        for (int i = 0; i < width; i++) {
            int[] sums = new int[length];
            for (int j = i; j < width; j++) {
                int currSum = 0;
                TreeSet preSum = new TreeSet<>();
                for (int z = 0; z < length; z++) {
                    sums[z] += length == cols ? matrix[j][z] : matrix[z][j];
                    currSum += sums[z];
                    if (currSum <= k) {
                        maxValue = Math.max(currSum, maxValue);
                    }
                    // when currSum is greater than k.
                    Integer dif = preSum.ceiling(currSum - k);
                    //remove previous sum to get a value that may be the max value.
                    if (dif != null) {
                        maxValue = Math.max(maxValue, currSum - dif);
                    }
                    preSum.add(currSum);
                }
            }
        }
        return maxValue;
            
    }

Sunday, July 10, 2016

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:
Given nums = [5, 2, 6, 1]

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.
Return the array [2, 1, 1, 0].

The problem has couple approaches. The easiest one to understand is to use a BST. We add elements from right to left, each time we add a new element, we track the value and number of left children so far. Then we traverse the tree and each left children of a node is the one we want.

I didn't implement this approach, because it's too complicated to write. I found another approach which also takes O(nlogn) but with much shorter code: Using binary search.

In this approach, we create a new array, then we use binary search to find the position of each number (still from right to left) in the new array. Since we traverse the array from right to left, all numbers smaller than the one to be inserted are elements that are smaller right to current one. And the position we found is the number of elements that are smaller.


   public List<integer> countSmaller(int[] nums) {
        List<integer> rst = new ArrayList<>(nums.length);
        for (int i = 0; i < nums.length; i++)
            rst.add(0);
        List<integer> sorted = new ArrayList<>();
        
        for (int i = nums.length - 1; i >= 0; i--) {
            int left = 0, right = sorted.size();
            while (left < right) {
                int mid = (left + right) / 2;
                if (sorted.get(mid) < nums[i]) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }
            rst.set(i, right);
            sorted.add(right, nums[i]);
        }
        return rst;
    }


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


"()())()" -> ["()()()", "(())()"]
"(a)())()" -> ["(a)()()", "(a())()"]
")(" -> [""]

The problem uses a BFS approach. Each iteration we remove one parenthesis from the string. Check if it is valid, if it is, then the length of the substring is the length of all valid answers (all shorter substring requires removing more parenthesis).


public List removeInvalidParentheses(String s) {
        List validParentheses = new ArrayList<>();
        Queue queue = new LinkedList<>();
        Set visited = new HashSet<>();
        queue.add(s);
        int done = -1;
        while (!queue.isEmpty()) {
            String curr = queue.poll();
            if (curr.length() < done)
                break;
            if (isValid(curr)) {
                validParentheses.add(curr);
                done = curr.length();
            } else {
                for (int i = 0; i < curr.length(); i++) {
                    if ("()".indexOf(curr.charAt(i)) < 0)
                        continue;
                    String next = curr.substring(0, i) + curr.substring(i + 1);
                    if (visited.add(next)) {
                        queue.add(next);
                    }
                }
            }
        }
        return validParentheses;

    }
    
    private boolean isValid(String s) {
        Stack left = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == '(') {
                left.add(i);
            } else if (s.charAt(i) == ')') {
                if (left.isEmpty())
                    return false;
                else
                    left.pop();
            }
        }
        return left.isEmpty();
    }

Wednesday, July 6, 2016

Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Note:
  1. You must not modify the array (assume the array is read only).
  2. You must use only constant, O(1) extra space.
  3. Your runtime complexity should be less than O(n2).
  4. There is only one duplicate number in the array, but it could be repeated more than once.


At first glance, we will think of using a HashSet, which then makes the problem really easy. However, the problem requires us NOT to use extra space. However, it mentions that there are n elements in an array with n + 1 length, which means there must be one that is duplicated. To find the duplicated one, we can utilize pigeonhole principle.

For a sorted array, each element should be in the position of itself, i.e., nums[i] = i, if no element is duplicated. What we can do is to "sort" the array by swapping elements that do not belong to the position. If there is a correct element in the position that the current one is supposed to be in, then we know this is a duplicated one.

If every one is in the correct space and we know there is a duplicated one, then we know it is in pos = 0.

public int findDuplicate(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] != i) {
                while (nums[i] != i) {
                    if (nums[i] == nums[nums[i]])
                        return nums[i];
                    swap(nums, i, nums[i]);
                }
            }
        }
        return nums[0];
    }
    
    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }

Friday, July 1, 2016

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:
  1. The array is only modifiable by the update function.
  2. You may assume the number of calls to update and sumRange function is distributed evenly.

Update 2016-11-12

I reviewed binary index tree again and found this interesting post, so this update is an explanation in English. All credits give to the original post.

The idea is to transfer the search index to binary format, each layer represents the lowest bit of the index number:


For example, the lowbit, which is the lowest bit 1 in the number, of 3 is 1 because the binary format of 3 is 11. The lowbit of 8 is 8 because the binary format of 8 is 1000, so the lowest bit is also the highest bit, which is itself. 

Lowbit of a number can be calculated by x&(-x) because in the binary format, negative format of a number is calculated by two's complement. For example, in a 8 bit representation, 4210 = 001010102, -4210 = 110101102 = 1 highest bit as sign + (~42 + 1). So the lowest bit of 42 is 2 (10 in binary). Now we can construct the tree based on bits:


Every row represents the layer of lowbit, every column represents the index in the array. As we can see on the chart, every higher bit index stores sum of itself and all its left children. For example, 4 is a higher bit then 2 and 1, it stores the sum of v1 to v4. 5 has lowbit 1, so it doesn't have any left children, and it only stores value of itself. Now it becomes easier to understand the following initialization code: for each index, we need to update the value of itself and all its right parent's value. Updating the structure means we add the difference to the index and all its right parent. 

Since each index only takes care of the value itself and all its left children, when we need to find the sum of an index, we need to add all its left parent's value. For example, if we want to find sum of index 5, we need to get the value of index 5 and its left parent, which is index 4. So what we need to do is to sum up its index and index - lowbit of itself. 

So the total initialization and search complexity are O(nlogn) and O(logn). 

Naturally I would think of using the same approach as we did in the immutable ones (i.e., using a "sum" array). However, if we do that way, the update will take too long time that it will exceed time limit.

I searched a little bit, and found this interesting data structure: Fenwick tree. I haven't fully understand how it works. But in short, we need a helper array, say bit[], each element i is responsible for nums[i - 1] and all elements that are smaller than the least bit of 1 i.e., (i&-i). The way we calculate it is for each nums[i - 1], we add nums[i - 1] to each i where i = i + (i&-i).

private void init(int pos, int val) {
        while (pos < bit.length) {
            bit[pos] += val;
            pos += (pos & -pos);
        }
    }


Now comes the sum part, reversely, sum += bit[i + 1], then i = i - (i&-i). However, this is the part that I don't quite understand. At least it works.

public class NumArray {
    private int[] bit;
    private int[] nums;
    public NumArray(int[] nums) {
        this.nums = nums;
        bit = new int[nums.length + 1];
        bit[0] = 0;
        for (int i = 1; i <= nums.length; i++) {
            init(i, nums[i - 1]);
        }
        
    }
    
    private void init(int pos, int val) {
        while (pos < bit.length) {
            bit[pos] += val;
            pos += (pos & -pos);
        }
    }

    void update(int i, int val) {
        int difference = val - nums[i];
        init(i + 1, difference);
        nums[i] = val;
    }

    public int sumRange(int i, int j) {
        return getSum(j + 1) - getSum(i);
    }
    
    private int getSum(int i) {
        int sum = 0;
        while (i > 0) {
            sum += bit[i];
            i -= (i&-i);
        }
        return sum;
    }
    
}