Showing posts with label ##. Show all posts
Showing posts with label ##. Show all posts

Saturday, January 12, 2019

# Knapsack

0-1 背包问题
问题:给大小为n的数组weights[]和values[], 求在不超过W的情况下能取得的value总和最大是多少,每一个数只能最多取一次(或者不取)

本质是排列组合,复杂度O(2^n)
public static void main(String[] ss) {

        int[] a = {1,2,3};
        int[] b = {4,2,1};
        int w = 3;

        System.out.println(knap(a, b, w));

    }

    private static int maxValue = 0;
    private static int knap(int weights[], int values[], int maxW) {
        dfs(weights, values, 0, 0, 0, maxW);
        return maxValue;
    }


    private static void dfs(int[] weights, int[] values, int curV, int curW, int index, int maxWeight) {
        if (curW > maxWeight) {
            return;
        }

        maxValue = Math.max(maxValue, curV);
        for (int i = index; i < weights.length; i++) {
            dfs(weights, values, curV + values[i], curW + weights[i], i + 1, maxWeight);
        }
    }

另一种写法。这种写法更容易看出我们可以用[index, curW] = weight来标记每一个状态(递归树上的结点)
O(2^n)
    private static int knap(int weights[], int values[], int maxW) {
        return dfs(weights, values, 0, 0, maxW);
    }

    private static int dfs(int[] weights, int[] values, int curW, int index, int maxWeight) {
        if (index == weights.length) {
            return 0;
        }
        int max_1 = 0;
        if (curW + weights[index] <= maxWeight) {
            max_1 = dfs(weights, values, curW + weights[index], index + 1, maxWeight) + values[index];
        }
        int max_2 =  dfs(weights, values, curW, index + 1, maxWeight);
        return Math.max(max_1, max_2);
    }

Memoization
O(maxW * n)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[][] dp = new int[n][maxW + 1];

        return dfs(weights, values, 0, 0, maxW, dp);
    }

    private static int dfs(int[] weights, int[] values, int curW, int index, int maxWeight, int[][] dp) {
        if (index == weights.length) {
            return 0;
        }

        if (dp[index][curW] != 0) {
            return dp[index][curW];
        }
        int max_1 = 0;
        if (curW + weights[index] <= maxWeight) {
            max_1 = dfs(weights, values, curW + weights[index], index + 1, maxWeight, dp) + values[index];
        }
        int max_2 =  dfs(weights, values, curW, index + 1, maxWeight, dp);
        int rt = Math.max(max_1, max_2);
        dp[index][curW] = rt;

        return rt;
    }

iterative dp, 通项公式跟递归是一摸一样
O(maxW * n)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[][] dp = new int[n][maxW + 1];

        for (int i = 0; i < maxW + 1; i++) {
            if (weights[0] <= i) {
                dp[0][i] = values[0];
            }
        }

        for (int i = 1; i < n; i ++) {
            for (int j = 1; j < maxW + 1; j++) {
                if (j - weights[i] >= 0) {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i]] + values[i]);
                }else {
                    dp[i][j] = dp[i - 1][j];
                }
            }
        }

        return dp[n - 1][maxW];
    }
可以发现当前的dp[row]只依赖于之前一行dp[row - 1], 所以可以简化为一维dp
    private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[] dp1 = new int[maxW + 1];
        int[] dp2 = new int[maxW + 1];

        for (int i = 0; i < maxW + 1; i++) {
            if (weights[0] <= i) {
                dp1[i] = values[0];
            }
        }
        
        for (int i = 1; i < n; i ++) {
            for (int j = 1; j < maxW + 1; j++) {
                if (j - weights[i] >= 0) {
                    dp2[j] = Math.max(dp1[j], dp1[j - weights[i]] + values[i]);
                }else {
                    dp2[j] = dp1[j];
                }
            }

            int[] tmp = dp1;
            dp1 = dp2;
            dp2 = tmp;
            Arrays.fill(dp2, 0);
        }

        return dp1[maxW];
    }

Unbounded knapsack
如果按0-1的解法的话会形成3个嵌套的for循环
换一种思路
 , 
O(n * maxW)
private static int knap(int weights[], int values[], int maxW) {
        int n = weights.length;
        int[] dp = new int[maxW + 1];

        for (int i = 1; i < maxW + 1; i++) {
            for (int j = 0; j < n; j++) {
                if (i >= weights[j]) {
                    dp[i] = Math.max(dp[i], dp[i - weights[j]] + values[j]);
                }
            }
        }

        return dp[maxW];
    }
       

ToDo
可以用贪心算法吗?v / w 最大的那个一直加

Bounded knapsack

Thursday, June 25, 2015

Day 113, ##, Basic Calculator II, Summary Ranges

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.
You may assume that the given expression is always valid.
Some examples:
"3+2*2" = 7
" 3/2 " = 1
" 3+5 / 2 " = 5
Note: Do not use the eval built-in library function.
--------------------------------------------------
look ahead,COME_BACK, 如果加入括号呢?
注意对i的处理
class Solution {
public:
    long long lookAhead(string &s, int &i) {
        long long num = 0;
        while (i < s.length()) {
            if (s[i] == ' ') {
                i++;
            }else if (isdigit(s[i])) {
                num = 10 * num + s[i] - '0'; 
                i++;
            }else
                break;
        }
        
        i--;
        return num;
    }

    int calculate(string s) {
        long long num = 0, rt = 0;
        long long sign = 1;
        
        for (int i = 0; i < s.length(); i++) {
            if (isdigit(s[i])) {
                num = lookAhead(s,i);
            }else if (s[i] == '+') {
                rt += sign * num;
                sign = 1;
                num = 0;
            }else if (s[i] == '-') {
                rt += sign * num;
                sign = -1;
                num = 0;
            }else if (s[i] == '*') {
                i++;
                num *= lookAhead(s,i);
            }else if (s[i] == '/') {
                i++;
                num /= lookAhead(s,i);
            }
        }
        
        if (num > 0) return rt + sign * num;
        return rt;
    }
};

Summary Ranges 
Given a sorted integer array without duplicates, return the summary of its ranges.
For example, given [0,1,2,4,5,7], return ["0->2","4->5","7"].
----------------------------------------------------------
nums[i] == nums[i - 1] + 1 与 nums[i] - nums[i - 1] == 1 相比较,前者不会有溢出的问题
re-factory之后的代码
class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        int start = 0;
        vector<string> rt;
        if (nums.size() == 0) return rt;
        
        for (int i = 1; i <= nums.size(); i++) {
            if (i == nums.size() || nums[i] != nums[i - 1] + 1) {
                if (i - start == 1) {
                    rt.push_back(to_string(nums[start]));
                }else {
                    rt.push_back(to_string(nums[start]) + "->" + to_string(nums[i - 1]));
                }
                start = i;
            }
        }
        
        return rt;
    }
};
re-factory之前的代码
class Solution {
public:
    vector<string> summaryRanges(vector<int>& nums) {
        int start = 0;
        vector<string> rt;
        if (nums.size() == 0) return rt;
        
        for (int i = 1; i < nums.size(); i++) {
            if ((long long)nums[i] - (long long)nums[i - 1] == 1) {
                continue;
            }
            if (i - start == 1) {
                string s = to_string(nums[start]);
                rt.push_back(s);
                start = i;
            }else {
                string s = to_string(nums[start]) + "->" + to_string(nums[i - 1]);
                rt.push_back(s);
                start = i;
            }
        }
        
        if (start == nums.size() - 1) {
            string s = to_string(nums[start]);
            rt.push_back(s);
        }else {
            string s = to_string(nums[start]) + "->" + to_string(nums.back());
            rt.push_back(s);
        }
        
        return rt;
    }
};

Wednesday, June 24, 2015

Day 112, ##, Count Complete Tree Nodes, Rectangle Area, Basic Calculator, Implement Stack using Queues, Invert Binary Tree

Count Complete Tree Nodes
Given a complete binary tree, count the number of nodes.
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 2hnodes inclusive at the last level h.
--------------------------------------------
O(logN * logN),遇到perfect tree, 直接计算并返回,反之继续数儿子们
看了答案
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int countNodes(TreeNode* root) {
       if (root == NULL) return 0;
       int left = 0, right = 0;
       TreeNode *leftItr = root, *rightItr = root;
       while (leftItr != NULL) {
           left++;
           leftItr = leftItr->left;
       }
       
       while (rightItr != NULL) {
           right++;
           rightItr = rightItr->right;
       }
       
       if (left == right) return pow(2,left) - 1;
       return 1 + countNodes(root->left) + countNodes(root->right);
    }
};

iterative
检查右子树是不是perfect tree,如果是,则缺口在左子树,此时加上右边的个数。
如果不是,则缺口在右子树,此时加上左边的个数
此题关键在于对高度的判断,然后对树的分解
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int getHeight(TreeNode *root) {
        int height = 0;
        while (root != NULL) {
            height++;
            root = root->left;
        }
        
        return height;
    }

    int countNodes(TreeNode* root) {
        int height = getHeight(root);
        int count = 0;
        while (root != NULL) {
            if (getHeight(root->right) == height - 1) {
                count += 1 << height - 1;
                root = root->right;
            }else {
                count += 1 << height - 2;
                root = root->left;
            }
            
            height--;
        }
        
        return count;
    }
};

Rectangle Area
Find the total area covered by two rectilinear rectangles in a 2D plane.
Each rectangle is defined by its bottom left corner and top right corner as shown in the figure.
Rectangle Area
Assume that the total area is never beyond the maximum possible value of int.
----------------------------------------------
看了答案
class Solution {
public:
    int computeArea(int A, int B, int C, int D, int E, int F, int G, int H) {
        if (D < F || H < B || G < A || C < E) {
            return (H - F) * (G - E) + (C - A) * (D - B);
        }
        
        int right = min(D,H) - max(B,F);
        int top = min(C,G) - max(E,A);
        
        return (D - B) * (C - A) + (H - F) * (G - E) - right * top;
    }
};

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 .
You may assume that the given expression is always valid.
Some examples:
"1 + 1" = 2
" 2-1 + 2 " = 3
"(1+(4+5+2)-3)+(6+8)" = 23
---------------------------------------------------------------------
看了答案,用初始化 sign = 1来处理符号,能解决特殊情况.
对()的处理
class Solution {
public:
    int calculate(string s) {
        stack<int> st;
        int sign = 1;
        int rt = 0;
        int num = 0;
        
        for (int i = 0; i < s.length(); i++) {
            if (isdigit(s[i])) {
                num = num * 10 + s[i] - '0';
            }else if (s[i] == '+') {
                rt += sign * num;
                sign = 1;
                num = 0;
            }else if (s[i] == '-') {
                rt += sign * num;
                sign = -1;
                num = 0;
            }else if (s[i] == '(') {
                st.push(rt);
                st.push(sign);
                sign = 1;
                rt = 0;
            }else if (s[i] == ')') {
                rt += num * sign;
                sign = st.top();
                st.pop();
                rt = sign * rt + st.top();
                st.pop();
                num = 0;
            }
        }
        
        if (num != 0) {
            return rt + sign * num;
        }
        
        return rt;
    }
};
Java, updated on Aug-16th-2018. 整体思路类似,只不过遇到数字做加减。上面c++的算法是遇到符号做加减。
class Solution {
    public int calculate(String s) {
        Stack<Integer> st = new Stack<>();
        int i = 0;
        int eval = 0;
        int sign = 1;
        
        while (i < s.length()) {
            char c = s.charAt(i);
            if (c == '(') {
                st.push(eval);
                st.push(sign);
                eval = 0;
                sign = 1;
            }else if (c == ')') {
                sign = st.pop();
                eval = st.pop() + eval * sign;
            }else if (Character.isDigit(c)) {
                String cur = "";
                while (i < s.length() && Character.isDigit(s.charAt(i))) {
                    cur += s.charAt(i);
                    i++;
                }
                i--;
                eval += sign * Integer.parseInt(cur);
            }else if (c == '-') {
                sign = -1;
            }else if (c == '+') {
                sign = 1;
            }
            
            i++;
        }
        
        return eval;
    }
}

Implement Stack using Queues
Implement the following operations of a stack using queues.
  • push(x) -- Push element x onto stack.
  • pop() -- Removes the element on top of the stack.
  • top() -- Get the top element.
  • empty() -- Return whether the stack is empty.
Notes:
  • You must use only standard operations of a queue -- which means only push to backpeek/pop from frontsize, and is empty operations are valid.
  • Depending on your language, queue may not be supported natively. You may simulate a queue by using a list or deque (double-ended queue), as long as you use only standard operations of a queue.
  • You may assume that all operations are valid (for example, no pop or top operations will be called on an empty stack).
----------------------------------------------------
用queue的大小来控制倒腾,只用一个queue,而不是两个
class Stack {
public:
    // Push element x onto stack.
    void push(int x) {
        topElem = x;
        q.push(x);
    }

    // Removes the element on top of the stack.
    void pop() {
        for (int i = 0; i < q.size() - 1; i++) {
            topElem = q.front();
            q.push(q.front());
            q.pop();
        }
        q.pop();
    }
    
    // Get the top element.
    int top() {
        return topElem;
    }

    // Return whether the stack is empty.
    bool empty() {
        return q.empty();
    }
private:
    queue<int> q;
    int topElem;
};

Invert Binary Tree
Invert a binary tree.
     4
   /   \
  2     7
 / \   / \
1   3 6   9
to
     4
   /   \
  7     2
 / \   / \
9   6 3   1
---------------------------------------------------
recursive
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return NULL;
        TreeNode *temp = root->left;
        root->left = invertTree(root->right);
        root->right = invertTree(temp);
        
        return root;
    }
};

iterative
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if (root == NULL) return root;
        queue<TreeNode *> que;
        que.push(root);
        
        while (!que.empty()) {
            TreeNode *node = que.front();
            que.pop();
            TreeNode *temp = node->left;
            node->left = node->right;
            node->right = temp;
            
            if (node->left != NULL) {
                que.push(node->left);
            }
            if (node->right != NULL) {
                que.push(node->right);
            }
        }
        
        return root;
    }
};

Tuesday, June 23, 2015

Day 111, ##, Contains Duplicate II, Contains Duplicate III, Maximal Square

Contains Duplicate II
Given an array of integers and an integer k, find out whether there there are two distinct indices i and j in the array such that nums[i] = nums[j] and the difference between iand j is at most k. ------------------------------------------------
this is one of the onsite interview questiona at fb, and I failed
class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        unordered_map<int,int> dic;
        for (int i = 0; i < nums.size(); i++) {
            if (dic.find(nums[i]) != dic.end()) {
                return true;
            }
            dic[nums[i]] = i;
            
            // save space
            if (i - k >= 0) {
                dic.erase(nums[i - k]);
            }
        }
        return false;
    }
};
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 difference between nums[i] and nums[j] is at most t and the difference between i and j is at most k.
------------------------------------------------------------
方法1,bucket sort,O(n),如果每个bucket里出现2个元素,则可直接返回true。所以确保了在程序运行过程中每个bucket只可能存在1个或者0个元素
casting的问题
k < 1 或 t < 0 直接返回false

(long long)t + 1, + 1是为了防止当t 为0
class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        if (k <= 0 || t < 0) return false;
        unordered_map<long long,int> bucket;
        
        for (int i = 0; i < nums.size(); i++) {
            long long inter = nums[i] + 2147483648;
            long long curBucket = inter / ((long long)t + 1);
            if (bucket.find(curBucket) != bucket.end() 
                || (bucket.find(curBucket - 1) != bucket.end() && abs((long long)nums[i] - bucket[curBucket - 1]) <= t)
                || (bucket.find(curBucket + 1) != bucket.end() && abs((long long)nums[i] - bucket[curBucket + 1]) <= t)) {
                return true;
            }
            
            bucket[curBucket] = (long long)nums[i];
            if (i - k >= 0) {
                long long oldBucket = (nums[i - k] + 2147483648) / ((long long)t + 1);
                bucket.erase(oldBucket);
            }
        }
        
        return false;
    }
};

Maximal Square
Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area.
For example, given the following matrix:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
Return 4.
-------------------------------------------------------------
看了提示tag
O(m * n) 空间, dp[i][j] 代表以i,j为右下角的正方形边长
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m = matrix.size();
        if (m == 0) return 0;
        int n = matrix[0].size();
        vector<vector<int> > dp(m,vector<int>(n,0));
        int maxSquare = 0;
        
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == '1') {
                dp[i][0] = 1;
                maxSquare = 1;
            }
        }
        for (int i = 0; i < n; i++) {
            if (matrix[0][i] == '1') {
                dp[0][i] = 1;
                maxSquare = 1;
            }
        }
        
        
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == '1') {
                    dp[i][j] = 1 + min(dp[i - 1][j - 1],min(dp[i - 1][j],dp[i][j - 1]));
                    maxSquare = max(maxSquare,dp[i][j]);
                }
            }
        }
        
        return maxSquare * maxSquare;
    }
};

O(n)空间优化,注意 else 语句将 dp[j] 清0
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m = matrix.size();
        if (m == 0) return 0;
        int n = matrix[0].size();
        vector<int> dp(n + 1, 0);
        int maxSquare = 0, pre = 0;

        for (int i = 0; i < m; i++) {
            for (int j = 1; j < n + 1; j++) {
                int temp = dp[j];
                if (matrix[i][j - 1] == '1') {
                    dp[j] = 1 + min(dp[j - 1],min(pre,dp[j]));
                    maxSquare = max(maxSquare,dp[j]);
                }else {
                    dp[j] = 0;
                }
                
                pre = temp;
            }
        }
        
        return maxSquare * maxSquare;
    }
};

Sunday, June 21, 2015

Day 110, ##, The Skyline Problem

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).
Buildings Skyline 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], ...]
---------------------------------------------------
Runtime error, C++ vector<> has its maximum size.
Use a heightMap of size INT_MAX
class Solution {
public:
    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
        vector<int> heightMap(INT_MAX,0);
        for (int i = 0; i < buildings.size(); i++) {
            int left = buildings[i][0];
            int height = buildings[i][1];
            int right = buildings[i][2];
            
            for (int j = left; j < right; j++) {
                heightMap[j] = max(height,heightMap[j]);
            }
        }
        
        vector<pair<int,int> > rt; 
        int preHeight = 0;
        for (int i = 0; i <= INT_MAX; i++) {
            if (preHeight != heightMap[i]) {
                rt.push_back(make_pair(i,heightMap[i]));
            }
            
            preHeight = heightMap[i];
        }
        
        return rt;
    }
};
方法1:http://www.cnblogs.com/easonliu/p/4531020.html
一句话,找线段。将所有点按照x坐标排序,注意当点重复时,将线段左边点排在线段后边点之前。遍历所有点,遇到左点说明遭遇新线段,插入它的高度在heap。遇到右点说明线段终止,从heap删除该高度(线段)。对比当前跟之前的高度,就能确定线段分界点
因为只需要求的是building的轮廓,所以heap_top保留的是当前x坐标下最高的线段

以下为方法1
处理重复点
c++ 中operator方法的写法
priority_queue不提供中间删除操作
用map而不是set,防止当不同线段的头和尾重复时,确保删除正确
class Solution {
public:
    struct cmp {
        bool operator() (const pair<int, int>& p1, const pair<int, int>& p2) {
            if (p1.first == p2.first) {
                return p1.second < p2.second;
            }
            return p1.first < p2.first;
        }
    };

    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
        vector<pair<int,int> > points, rt;
        for (int i = 0; i < buildings.size(); i++) {
            points.push_back(make_pair(buildings[i][0],-buildings[i][2]));
            points.push_back(make_pair(buildings[i][1],buildings[i][2]));
        }
        sort(points.begin(),points.end(),cmp());
        
        priority_queue<int> heightMap;
        heightMap.push(0); // to handle end of a building
        unordered_map<int,int> deleted;
        int pre = 0;
        for (int i = 0; i < points.size(); i++) {
            // left point
            if (points[i].second < 0) {
                heightMap.push(-points[i].second);
            }else {
                // right point
                deleted[points[i].second]++;
                while (!heightMap.empty() && deleted[heightMap.top()] > 0) {
                    deleted[heightMap.top()]--;
                    heightMap.pop();
                }
            }
            
            int cur = heightMap.top();
            if (cur != pre) {
                rt.push_back(make_pair(points[i].first,cur));
                pre = cur;
            }
        }
        
        return rt;
    }
};

方法2:http://www.geeksforgeeks.org/divide-and-conquer-set-7-the-skyline-problem/
basic divide and conquer, 难度在于细节处理上。注意线段的结束点高度设为0
class Solution {
public:
    vector<pair<int,int>> merge(vector<pair<int,int>> left,vector<pair<int,int>> right) {
        vector<pair<int,int>> rt;
        int h1 = 0, h2 = 0;
        int i = 0, j = 0;
        while (i < left.size() && j < right.size()) {
            int x = 0, h = 0;
            if (left[i].first < right[j].first) {
                x = left[i].first;
                h1 = left[i].second;
                h = max(h1,h2);
                i++;
            }else if (left[i].first > right[j].first){
                x = right[j].first;
                h2 = right[j].second;
                h = max(h1,h2);
                j++;
            }else {
                x = left[i].first;
                h1 = left[i].second;
                h2 = right[j].second;
                h = max(h1,h2);
                i++;
                j++;
            }
            if (rt.size() == 0 || rt.back().second != h) {
                rt.push_back(make_pair(x,h));
            }
        }
        
        rt.insert(rt.end(),left.begin() + i,left.end());
        rt.insert(rt.end(),right.begin() + j,right.end());
        return rt;
    }

    vector<pair<int,int>> divide(vector<vector<int>>& buildings, int left,int right) {
        if (left == right) {
            vector<pair<int,int>> rt;
            rt.push_back(make_pair(buildings[left][0],buildings[left][2]));
            rt.push_back(make_pair(buildings[left][1],0));
            return rt;
        }
        int mid = (left + right) / 2;
        vector<pair<int,int>> leftPart = divide(buildings,left,mid);
        vector<pair<int,int>> rightPart = divide(buildings,mid + 1,right);
        return merge(leftPart,rightPart);
    }
    

    vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {
        vector<pair<int, int>> rt;
        if (buildings.size() == 0) return rt;
        return divide(buildings,0,buildings.size() - 1);
    }
};

阅读:https://briangordon.github.io/2014/08/the-skyline-problem.html
Java
class Solution {
    public List<int[]> getSkyline(int[][] buildings) {
        
        List<int[]> points = getSortedPoints(buildings);
        return getSkyline(points);        
    }
    
    private List<int[]> getSkyline(List<int[]> points) {
        PriorityQueue<Integer> que = new PriorityQueue<>((a, b) -> b - a);
        Map<Integer, Integer> map = new HashMap<>();
        List<int[]> rt = new ArrayList<>();
        que.add(0);
        map.put(0, 1);
        
        for (int[] point : points) {
            int x = point[0];
            int height = point[1];
            
            if (height > 0) {
                if (que.peek() < height) {
                    rt.add(new int[]{x, height});
                }
                
                que.add(height);
                map.put(height, map.getOrDefault(height, 0) + 1);
            }else {
                
                map.put(-height, map.get(-height) - 1);
                while (map.get(que.peek()) == 0)  {
                    que.poll();
                }
                
                if (-height > que.peek()) {
                    rt.add(new int[]{x, que.peek()});
                }
                
            }
        }
        
        return rt;
    }
    
    private List<int[]> getSortedPoints(int[][] buildings) {
        List<int[]> points = new ArrayList<>();
        
        for (int[] building : buildings) {
            points.add(new int[]{building[0], building[2]});
            points.add(new int[]{building[1], -building[2]});    
        }
        
        Collections.sort(points, (a, b) -> {
            if (a[0] == b[0]) {
                return b[1] - a[1];
            }
            return a[0] - b[0];
        });
        
        return points;
    }
}

Thursday, June 18, 2015

Day 109, 215, ##, Kth Largest Element in an Array, Combination Sum III, Contains Duplicate

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.
For example,
Given [3,2,1,5,6,4] and k = 2, return 5.
Note: 
You may assume k is always valid, 1 ≤ k ≤ array's length.
----------------------------------------------
quick-select。因为是in-place,注意storedIndex的初始值为left
class Solution {
public:
    void swap(vector<int> &nums,int index_1, int index_2) {
        int temp = nums[index_1];
        nums[index_1] = nums[index_2];
        nums[index_2] = temp;
    }

    int partition(vector<int> &nums,int left, int right) {
        int mid = left + (right - left) / 2;
        swap(nums,mid,right);
        
        int storedIndex = left;
        for (int i = left; i < right; i++) {
            if (nums[i] <= nums[right]) {
                swap(nums,i,storedIndex);
                storedIndex++;
            }    
        }
        swap(nums,storedIndex,right);
        
        return storedIndex;
    }

    int findKthLargest(vector<int>& nums, int k) {
        int left = 0, right = nums.size() - 1;
        
        while (left <= right) {
            int pivot = partition(nums,left,right);
            if (pivot == nums.size() - k) {
                return nums[pivot];
            }
            if (pivot < nums.size() - k) {
                left = pivot + 1;
            }else {
                right = pivot - 1;
            }
        }
        
        return -1;
    }
};

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.
Ensure that numbers within the set are sorted in ascending order.

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]]
-------------------------------------------------
class Solution {
public:
    void dfs(vector<vector<int>> &rt, vector<int> cur, int startNum, int k, int sum) {
        if (k == 0 && sum == 0) {
            rt.push_back(cur);
            return;
        }
        
        if (k < 0 || sum < 0) return;
        
        for (int i = startNum; i < 10; i++) {
            vector<int> temp = cur;
            temp.push_back(i);
            dfs(rt,temp,i + 1,k - 1,sum - i);
        }
        
    }

    vector<vector<int>> combinationSum3(int k, int n) {
        vector<vector<int>> rt;
        vector<int> cur;
        dfs(rt,cur,1,k,n);
        
        return rt;
    }
};
类似
class Solution {
public:
    void helper(vector<vector<int> > &rt, vector<int> cur, int target,int k, int index) {
        if (k == 0 && target == 0) {
            rt.push_back(cur);
            return;
        }
        if (k < 0 || target < 0 || index > 9) return;
        
        helper(rt,cur,target,k,index + 1);
        cur.push_back(index);
        helper(rt,cur,target - index,k - 1, index + 1);
    }

    vector<vector<int>> combinationSum3(int k, int n) {
        vector<vector<int>> rt;
        vector<int> cur;
        helper(rt,cur,n,k,1);
        
        return rt;
    }
};
Contains Duplicate
Given an array of integers, find if the array contains any duplicates. Your function should return true if any value appears at least twice in the array, and it should return false if every element is distinct. ------------------------------------------------
hash set
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_set<int> st;
        for (int i = 0; i < nums.size(); i++) {
            if (st.find(nums[i]) != st.end()) {
                return true;
            }
            st.insert(nums[i]);
        }
        
        return false;
    }
};
sort
class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        for (int i = 1; i < nums.size(); i++) {
            if (nums[i - 1] == nums[i]) {
                return true;
            }
        }
        
        return false;
    }
};

Tuesday, June 16, 2015

Day 108, ##,Add and Search Word - Data structure design, House Robber II, Shortest Palindrome

Add and Search Word - Data structure design
Design a data structure that supports the following two operations:
void addWord(word)
bool search(word)
search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter.
For example:
addWord("bad")
addWord("dad")
addWord("mad")
search("pad") -> false
search("bad") -> true
search(".ad") -> true
search("b..") -> true
Note:
You may assume that all words are consist of lowercase letters a-z.
--------------------------------------------------------------
Trie,
class TrieNode {
public:
    vector<TrieNode *> children;
    bool end;
    TrieNode() {
        end = false;
        children = vector<TrieNode *>(26,NULL);
    }
};

class WordDictionary {
public:
    WordDictionary() {
        root = new TrieNode();
    }
    
    // Adds a word into the data structure.
    void addWord(string word) {
        TrieNode *itr = root;
        for (int i = 0; i < word.length(); i++) {
            if (itr->children[word[i] - 'a'] == NULL) {
                itr->children[word[i] - 'a'] = new TrieNode();
            }
            itr = itr->children[word[i] - 'a'];
        }
        itr->end = true;
    }

    bool searchHelper(string word, int index,TrieNode *itr) {
        if (index == word.length()) return itr->end;
        if (word[index] != '.') {
            TrieNode* t = itr->children[word[index] - 'a']; 
            if (t != NULL) {
                return searchHelper(word,index + 1, t);
            }
            return false;
        }
        
        for (int i = 0; i < 26; i++) {
            TrieNode* t = itr->children[i]; 
            if (t != NULL && searchHelper(word,index + 1, t)) {
                return true;
            }
        }
        
        return false;
    }

    // Returns if the word is in the data structure. A word could
    // contain the dot character '.' to represent any one letter.
    bool search(string word) {
        return searchHelper(word,0,root);
    }
private:
    TrieNode* root;
};

// Your WordDictionary object will be instantiated and called as such:
// WordDictionary wordDictionary;
// wordDictionary.addWord("word");
// wordDictionary.search("pattern");


Java, addWord可以用for循环,也可以递归。searchWord的时候只能递归了,因为遇到‘.’得尝试所有26位,然后backtrace
class WordDictionary {
    
    private Node root;

    /** Initialize your data structure here. */
    public WordDictionary() {
        root = new Node('0');
    }
    
    /** Adds a word into the data structure. */
    public void addWord(String word) {
        
        Node itr = root;
        for (int i = 0; i < word.length(); i++) {
            char c = word.charAt(i);
            if (!itr.letters.containsKey(c)){
                itr.letters.put(c, new Node(c));
            }
            
            if (i == word.length() - 1) {
                itr.letters.get(c).isWord = true;
            }
            
            itr = itr.letters.get(c);
        }
        
    }
    
    /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
    public boolean search(String word) {
        
        return helper(word, 0, root);
    }
    
    private boolean helper(String word, int index, Node node) {
        
        if (index == word.length()) {
            return node.isWord;
        }
        
        char c = word.charAt(index);
        if (node.letters.containsKey(c)) {
            if (helper(word, index + 1, node.letters.get(c))) return true;
        }else if (c == '.') {
            for (Map.Entry entry : node.letters.entrySet()) {
                if (helper(word, index + 1, entry.getValue())) {
                    return true;
                }
            }
        }
        
        return false;
        
    }
}

class Node{
    public char c;
    public Map letters;
    public boolean isWord;
    
    public Node(char c) {
        this.c = c;
        letters = new HashMap();
        isWord = false;
    }
}

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary obj = new WordDictionary();
 * obj.addWord(word);
 * boolean param_2 = obj.search(word);
 */
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.
For example,
Given words = ["oath","pea","eat","rain"] and board =
[
  ['o','a','a','n'],
  ['e','t','a','e'],
  ['i','h','k','r'],
  ['i','f','l','v']
]
Return ["eat","oath"].
Note:
You may assume that all inputs are consist of lowercase letters a-z.
You would need to optimize your backtracking to pass the larger test. Could you stop backtracking earlier?
If the current candidate does not exist in all words' prefix, you could stop backtracking immediately. What kind of data structure could answer such query efficiently? Does a hash table work? Why or why not? How about a Trie? If you would like to learn how to implement a basic trie, please work on this problem: Implement Trie (Prefix Tree) first.
----------------------------------------------------
Used prefix tree from the previous question
class TrieNode {
public:
    TrieNode() {
        next = vector<TrieNode*>(26,NULL);
        terminal = false;
    }
 
    char value;
    vector<TrieNode*> next;
    bool terminal;
};

class Trie {
public:
    Trie() {
        root = new TrieNode();
    }
 
    // Inserts a word into the trie.
    void insert(string s) {
        TrieNode *cur = root;
        for (int i = 0; i < s.length(); i++) {
            char c = s[i] - 'a';
            if (cur->next[c] == NULL) {
                TrieNode *trie = new TrieNode();
                trie->value = c;
                cur->next[c] = trie;
            }
            cur = cur->next[c];
        }
        cur->terminal = true;
    }
 
    // Returns if the word is in the trie.
    bool search(string key) {
        TrieNode *cur = root;
        for (int i = 0; i < key.length(); i++) {
            char c = key[i] - 'a';
            if (cur->next[c] == NULL) {
                return false;
            }
             
            cur = cur->next[c];
        }
         
        return cur->terminal;
    }
 
    // Returns if there is any word in the trie
    // that starts with the given prefix.
    bool startsWith(string prefix) {
        TrieNode *cur = root;
        for (int i = 0; i < prefix.length(); i++) {
            char c = prefix[i] - 'a';
            if (cur->next[c] == NULL) {
                return false;
            }
             
            cur = cur->next[c];
        }
         
        return true;
    }
private:
    TrieNode* root;
};

class Solution {
public:
    void search(vector<string> &rt, vector<vector<char>>& board, 
            vector<vector<bool> > &visit, Trie &trie, string word, int row, int col, unordered_set<string> &hadIt) {
        if (row < 0 || row >= board.size() || col < 0 || col >= board[0].size() || visit[row][col]) {
            return;
        }
        word += board[row][col];
        visit[row][col] = true;
        if (hadIt.find(word) == hadIt.end() && trie.search(word)) {
            hadIt.insert(word);
            rt.push_back(word);
        }
        
        if (trie.startsWith(word)) {
            search(rt,board,visit,trie,word,row + 1,col,hadIt);
            search(rt,board,visit,trie,word,row - 1,col,hadIt);
            search(rt,board,visit,trie,word,row,col + 1,hadIt);
            search(rt,board,visit,trie,word,row,col - 1,hadIt);
        }
        visit[row][col] = false;
    }

    vector<string> findWords(vector<vector<char>>& board, vector<string>& words) {
        Trie trie;
        for (int i = 0; i < words.size(); i++) {
            trie.insert(words[i]);
        }
        
        vector<string> rt;
        vector<vector<bool> > visit(board.size(),vector<bool>(board[0].size(),false));
        unordered_set<string> hadIt;
        for (int i = 0; i < board.size(); i++) {
            for (int j = 0; j <board[0].size(); j++) {
                search(rt,board,visit,trie,"",i,j,hadIt);
            }
        }
        
        return rt;
    }
};

House Robber II
Note: This is an extension of House Robber.
After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.
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.
----------------------------------------------
看了答案!
class Solution {
public:
    int robHelper(vector<int>& nums, int left, int right) {
        int pre_1 = 0, pre_2 = 0, pre_3 = 0;
        for (int i = left; i <= right; i++) {
            int temp = pre_3;
            pre_3 = max(pre_3,max(pre_1,pre_2) + nums[i]);
            pre_1 = pre_2;
            pre_2 = temp;
        }
        
        return pre_3;
    }

    int rob(vector<int>& nums) {
        if (nums.size() == 0) return 0;
        if (nums.size() == 1) return nums[0];
        return max(robHelper(nums,0,nums.size() - 2),robHelper(nums,1,nums.size() - 1));
    }
};

Shortest Palindrome
Given a string S, you are allowed to convert it to a palindrome by adding characters in front of it. Find and return the shortest palindrome you can find by performing this transformation.
For example:
Given "aacecaaa", return "aaacecaaa".
Given "abcd", return "dcbabcd".
---------------------------------------------------------------
O(n^2) 找prefix是palindrome。超时
class Solution {
public:
    bool isPalindrome(string s) {
        for (int i = 0; i < s.length() / 2; i++) {
            if (s[i] != s[s.length() - 1 - i]) return false;
        }
        
        return true;
    }
    
    int longestPal(string s) {
        for (int i = s.length(); i > 0; i--) {
            if (isPalindrome(s.substr(0,i))) {
                return i;
            }
        }
        
        return 0;
    }
    
    string shortestPalindrome(string s) {
        int length = longestPal(s);
        string copy = s;
        for (int i = 0; i < copy.length() - length; i++) {
            s = copy[length + i] + s;    
        }
        
        return s;
    }
};
看了答案!对KMP要灵活运用
s + special char + reverse(s)
然后运用KMP的failure function计算出s的prefix和revsers(s)的suffix相等的最长长度
ref: https://leetcode.com/discuss/36807/c-8-ms-kmp-based-o-n-time-%26-o-n-memory-solution
class Solution {
public:
    vector<int> computePrefixTable(string pattern) {
        int m = pattern.length();
        vector<int> table(m,0);
        int matchedLength = 0;
        
        for (int i = 1; i < m; i++) {
            // until find the next char at matchedLength is equal to char at i
            // or matchedLength is zero
            while (matchedLength > 0 && pattern[matchedLength] != pattern[i]) {
                matchedLength = table[matchedLength - 1];
            }
            if (pattern[matchedLength] == pattern[i]) {
                matchedLength++;
            }
            table[i] = matchedLength;
        }
        
        return table;
    }
    string shortestPalindrome(string s) {
        string rev = s;
        reverse(rev.begin(),rev.end());
        string pattern = s + "*" + rev;
        vector<int> table = computePrefixTable(pattern);
        
        return rev.substr(0,rev.length() - table[table.size() - 1]) + s;
    }
};

Sunday, June 14, 2015

Day 107, ##, Minimum Size Subarray Sum, Course Schedule II

Minimum Size Subarray Sum
Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn't one, return 0 instead.
For example, given the array [2,3,1,2,4,3] and s = 7,
the subarray [4,3] has the minimal length under the problem constraint.
More practice:
If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n).
-------------------------------------------------------
Sliding window, O(n)

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        if (nums.size() == 0) return 0;
        int minLen = INT_MAX;
        int right = 0, left = 0;
        
        int sum = nums[right];
        while (right < nums.size()) {
            if (sum < s) {
                right++;
                sum += nums[right];
            }else {
                minLen = min(right - left + 1, minLen);
                sum -= nums[left];
                left++;
            }
        }
        
        if (minLen == INT_MAX) return 0;
        return minLen;
    }
};
O(NlogN),
sums[i]计算了nums[0 : i + 1]的连续值,所以sums内的值是递增,由此可以用binary search
所求的最短subarray就是计算 sums[i] + s >= sums[j], j取[1: ]
注意各variable的取值
binary()返回的值可能会等于 sums.size(),


class Solution {
public:
    int binary(int s, vector<int> &sums, int left) {
        int right = sums.size() - 1;
        int minLen = INT_MAX;
        int sum = 0;
        
        while (left <= right) {
            int mid = left + (right - left) / 2; 
            if (sums[mid] >= s) {
                right = mid - 1;
            }else {
                left = mid + 1;
            }
        }
        
        return left;
    }

    int minSubArrayLen(int s, vector<int>& nums) {
        if (nums.size() == 0) return 0;
        int minLen = INT_MAX;
        
        vector<int> sums(nums.size() + 1,0);
        for (int i = 1; i <= nums.size(); i++) {
            sums[i] = sums[i - 1] + nums[i - 1];
        }
        
        for (int i = 0; i < nums.size(); i++) {
            int end = binary(s + sums[i - 1],sums,i);
            if (end == sums.size()) break;
            minLen = min(end - i + 1, minLen);
        }
        
        if (minLen == INT_MAX) return 0;
        return minLen;
    }
};

Java O(n)版本
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int n = nums.length;
        int start = 0;
        int minLen = n + 1;
        int sum = 0;
        
        for (int i = 0; i < n; i++) {
            sum += nums[i];
            
            while (sum >= s) {
                minLen = Math.min(minLen, i - start + 1);
                sum -= nums[start];
                start++;
            }
        }
        
        return minLen > n ? 0 : minLen;
    }
}
Course Schedule II
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, return the ordering of courses you should take to finish all courses.
There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array.
For example:
2, [[1,0]]
There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1]
4, [[1,0],[2,0],[3,1],[3,2]]
There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3].
Note:
The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented.
Hints:
  1. This problem is equivalent to finding the topological order in a directed graph. If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses.
  2. Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort.
  3. Topological sort could also be done via BFS.
-----------------------------------------------------
topological sort, 考虑cycle问题
DFS
class Solution {
public:
    bool dfs(vector<vector<int>> &edges,int course,vector<int> &visit,vector<int> &path) {
        if (visit[course] == -1) return false;
        if (visit[course] == 1) return true;
        visit[course] = -1;
        
        for (int i = 0; i < edges[course].size(); i++) {
            if (!dfs(edges,edges[course][i],visit,path)) return false;
        }
        path.push_back(course);
        visit[course] = 1;
        return true;
    }

    vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<vector<int>> edges(numCourses,vector<int>());
        vector<int> path;
        vector<int> visit(numCourses,0);    
        
        for (int i = 0; i < prerequisites.size(); i++) {
            edges[prerequisites[i].second].push_back(prerequisites[i].first);
        }
        for (int i = 0; i < numCourses; i++) {
            if (!dfs(edges,i,visit,path)) {
                vector<int> rt;
                return rt;
            }
        }
        reverse(path.begin(),path.end());
        return path;
    }
};
BFS
ref: https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
class Solution {
public:
    vector<int> findOrder(int numCourses, vector<pair<int, int>>& prerequisites) {
        vector<vector<int> > edges(numCourses);
        vector<int> order;
        vector<int> inDegree(numCourses,0);
        queue<int> que;
        
        for (int i = 0; i < prerequisites.size(); i++) {
            edges[prerequisites[i].second].push_back(prerequisites[i].first);
            inDegree[prerequisites[i].first]++;
        }
        
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] == 0) {
                que.push(i);
            }
        }
        
        while (!que.empty()) {
            int current = que.front();
            que.pop();
            order.push_back(current)
            ;
            for (int i = 0; i < edges[current].size(); i++) {
                int neighbor = edges[current][i];
                inDegree[neighbor]--;
                if (inDegree[neighbor] == 0) {
                    que.push(neighbor);
                }
            }
        }
        
        for (int i = 0; i < numCourses; i++) {
            if (inDegree[i] != 0) {
                order = vector<int>();
                return order;
            }
        }
        
        return order;
    }
};