leetcode 题解 (1-500题,持续更新,part 1)

除了前面25道题目,为了省时间,后面只做hard的题目

part1包含了1000题以内的题目(未加锁部分hard题)

500-1000见LeetCode题解part2

1000题以上的见LeetCode题解part3

1. Two  Sum (Easy)

题意:给一个数组和一个数,求数组中哪两个数的和为这个数
题解:先排序,然后两个指针扫。容易证明这是可以的。因为如果当前的某个指针和已经扫过的某个数的和为目标值,那么则不可能达到现在这个状态。
时间复杂度是O(nlogn),不过如果数字都比较小,可以用计数的方法查找另一个数是否存在(或者用hash),这个解法为O(n)的解法。
语言:C++
#include 
#include 
#include 
#include 
#include 

using namespace std;


class Solution {
struct Node{
    int value;
    int index;
    const bool operator < (const Node & b) {
        return value < b.value;
    }
};

public:
    vector twoSum(vector &nums, int target) {
        int len = nums.size();
        Node *node = new Node[nums.size()];
        for(size_t i = 0;i < nums.size(); ++i){
            node[i].value = nums[i];
            node[i].index = i;
        }

        sort(node, node + len);
        size_t left = 0, right = len - 1;
        int idL = - 1, idR = -1;
        while(left < right){
            if(node[left].value + node[right].value == target){
                idL = node[left].index;
                idR = node[right].index;
                break;
            }else if(node[left].value + node[right].value < target){
                ++left;
            }else{
                --right;
            }
        }
        delete node;
        assert(idL != -1);
        vector ans(2);
        ans[0] = idL;
        ans[1] = idR;
        return ans;

    }
};

int main()
{
    vector num(3);
    int target = 6;
    num[0] = 3; num[1] = 2; num[2] = 4;
    Solution s;
    vector ans = s.twoSum(num, target);

    cout<

2. Add Two Numbers (Medium)

题意:给两个链表,每个链表表示一个数字(逆序的,个位在最前面),求他们的和,返回同样的格式(一个链表)
题解:简单的模拟加法过程即可
语言:C++

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode rootNode(0);
        ListNode *pCurNode = &rootNode;
        int a = 0;
        while (l1 || l2)
        {
            int v1 = (l1 ? l1->val : 0);
            int v2 = (l2 ? l2->val : 0);
            int temp = v1 + v2 + a;
            a = temp / 10;
            ListNode *pNode = new ListNode((temp % 10));
            pCurNode->next = pNode;
            pCurNode = pNode;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        if (a > 0)
        {
            ListNode *pNode = new ListNode(a);
            pCurNode->next = pNode;
        }
        return rootNode.next;

    }
};

把上面的模换成下面的
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode rootNode(0);
        ListNode *pCurNode = &rootNode;
        int a = 0;
        while (l1 || l2)
        {
            int v1 = (l1 ? l1->val : 0);
            int v2 = (l2 ? l2->val : 0);
            int temp = v1 + v2 + a;
            a = temp > 9;
            ListNode *pNode = new ListNode(temp - a * 10);
            pCurNode->next = pNode;
            pCurNode = pNode;
            if (l1) l1 = l1->next;
            if (l2) l2 = l2->next;
        }
        if (a > 0)
        {
            ListNode *pNode = new ListNode(a);
            pCurNode->next = pNode;
        }
        return rootNode.next;

    }
};

3. Longest Substring Without Repeating Characters(Medium)

 题意:给定一个字符串,找出没有重复字符的最长子串。
题解:首先可以线性时间求解出当前字符上一次出现的位置,用一个数组last_pos保存下来。然后可以得到,一个子串(从begin开始到end位置结束的串),没有重复字符当且仅当在这个子串中last_pos() < b。
解法就是枚举子串开始位置i,然后用index索引枚举结束位置。注意到如果j > i,那么从j开始的最长不重复子串的末尾一定在以i开始的那个最长子串末尾的后面(至少相等)。也就是说index不用从i 枚举,而是从上一次枚举的位置开始,这样就保证了线性的时间,每次index都会加1。
渐进时间复杂度:O(n)
语言: python
class Solution(object):
    def lengthOfLongestSubstring(self, s):
        lens = len(s);
        w = [-1] * 100;
        last_pos = [0] * lens;
        #pos位置的字符上一次出现的位置,第一次出现,标记为-1
        for pos, c in enumerate(s):
            last_pos[pos] = w[ord(c) - ord('a')];
            w[ord(c) - ord('a')] = pos;
        ans = 0;
        index = 0;

        for i in range(lens):
            while(index < lens - 1 and last_pos[index + 1] < i):
                index = index + 1;
            ans = max(ans,index - i + 1);

        return ans;

4. Median of Two Sorted  Arrays (Hard)

题意:两个有序数组,求中值
题解:可以使用中间二分形式进行,每次判断中间的值的大小,舍弃不可能是答案的某个数组的一半,当数组一个已经为空时,直接得到答案。具体如下:
      对于两个数组: a[apre, amid,asuf],b[apre,bmid,bsuf],pre表前半段,suf表后半段,mid表中间元素(pre和suf可为空)。则答案在这6个段中的一个。(假设pre和suf都非空,因为如果为空,答案显然)比较a[amid]和b[bmid],如果a[amid] < b[bmid],那么可以确定顺序的有a[apre] <= a[amid] <= b[bmid] <= b[bsuf]。故而可以根据实际情况舍弃掉a[apre]或者b[bsuf]。那什么情况舍弃哪一个呢?有比较多的情况需要考虑。这个 方法虽然可以,但是到最后又非常多的边界需要判断,非常容易混乱。而如果不是取中位,而是k/2位的话,边界简单得多,思路和上面一样,只不过舍弃的情况没那么复杂。
时间复杂度(O(logn)

#include 
#include 

using namespace std;


double findKth(int *a, int m, int *b, int n, int k)
{
    if (m > n)
        return findKth(b, n, a, m, k);

    if (m == 0)
        return b[k - 1];

    if (k == 1)
        return a[0] < b[0] ? a[0] : b[0] ;
    int pa = k / 2 < m ? k / 2 : m, pb = k - pa;
    if (a[pa - 1] < b[pb - 1])
        return findKth(a + pa, m - pa, b, n, k - pa);
    else if (a[pa - 1] > b[pb - 1])
        return findKth(a, m, b + pb, n - pb, k - pb);
    else
        return a[pa - 1];
}
double findMedianSortedArrays(int* A, int m, int* B, int n) {

        int total = m + n;
        if (total & 0x1)
            return findKth(A, m, B, n, total / 2 + 1);
        else
            return (findKth(A, m, B, n, total / 2)
                    + findKth(A, m, B, n, total / 2 + 1)) / 2;
}


int main()
{
    int *a ,b[] = {1};
    cout<

 5. Longest Palindromic Substring (Medium)

题意:最大回文子串
题解:manacher算法;枚举中心,利用前面的信息。具体见manacher算法,无需多说。
渐进时间复杂度(O(n))

#include 
#include 

using namespace std;

class Solution {
public:
    string longestPalindrome(string s) {
        string str(s.size() * 2 + 3,'#');
        str[0] = '$';
        str[s.size() * 2 + 2] = '@';
        for(size_t i = 0;i < s.size(); ++i){
            str[i * 2 + 2] = s[i];
        }
        return findLongestPalindrome(str, s);
    }

    string findLongestPalindrome(string str, string s){
        size_t *d = new size_t[str.size()];
        size_t mx = 0, mxId = 0, longestLenth = 1 , longestLenthPos = 0;
        d[0] = 1;

        for(size_t i = 1;i < str.size(); ++i){

            if(mx <= i)  d[i] = 1;
            else d[i] = min(d[2 * mxId - i] , mx - i);

            while(str[i - d[i]] == str[i + d[i]]) ++d[i];

            if(d[i] + i > mx){
                mx = d[i] + i;
                mxId = i;
            }
            if(d[i] > longestLenth){
                longestLenth = d[i] - 1;
                longestLenthPos = i;
            }
        }
        delete d;
        size_t startPos = (longestLenthPos - longestLenth) / 2;
        return s.substr(startPos, longestLenth);
    }
};

int main()
{
    Solution s;
    string str = "ccd";
    cout<

6. ZigZag Conversion

题意:将一个字符串转化成zigZag形状,然后按行重新排列输出。
题解:简单模拟。
不过编译器好像跟本地的不太一样,对关于vector初始化,需要push_back过才能取出,否则是runtimeError 而不是取出空串。
代码:
#include 
#include 
#include 
using namespace std;

string convert(string s, int nRows) {

    if(nRows <= 1 || s.length() <= nRows)
        return s;
    vector zigZags(nRows);

    int curRow = 0, step = 1;
    for (string::iterator s_iter = s.begin(); s_iter != s.end(); ++s_iter){
        zigZags[curRow].push_back(*s_iter);

        if(curRow == 0) step = 1;
        else if(curRow == nRows - 1) step = -1;
        curRow += step;
    }
    s.clear();
    string::iterator s_it;
    for(vector::iterator vit = zigZags.begin(); vit != zigZags.end(); ++vit){
        s += *vit;
    }
    return s;
}

int main()
{
    string s =  "PAYPALISHIRING;
    int nRows = 3;
    cout<

7. Reverse Integer

题意:将数字翻转
题解:直接转,注意前导零和溢出即可
代码:
#include 
#include 
#include 
#include 
using namespace std;

int reverse(int x) {
    if (x == 0) return 0;
    int sign = (x > 0) * 2 - 1;
    long long y = abs(x);
    long long res = 0;
    while(y > 0){
        res = res * 10 + y % 10;
        y /= 10;
    }
    res = sign * res;
    if(res > (1LL << 31) - 1 || res < -(1LL << 31))
        res = 0;
    return res;
}


int main()
{
    cout<

8. String to Integer (atoi)

题意:按照题目将字符串转成int。规则是,前缀不合法输出前缀,合法前缀为空输出0,超出int输出maxint,小于minint输出minint即可
题解:主要是题目各种输入情况没有说明

#include 
#include 
#include 
#include 
using namespace std;

int myAtoi(string str) {
    int sign = 1;
    int max_int = 2147483647;
    int min_int = -2147483648;
    string::iterator s_it = str.begin();
    while(s_it != str.end() && *s_it == ' ') ++s_it;
    if(s_it != str.end()  && (*s_it == '+' || *s_it == '-')){
        sign = ((*s_it == '+') * 2 - 1);
        ++s_it;
    }
    long long res = 0;
    int num = 0;
    for(; s_it != str.end() ; ++ s_it){
        if(*s_it < '0' || *s_it > '9') break;
        res = res * 10 + sign * (*s_it - '0');
        ++num;
        if(res > max_int) {
                res = max_int;
                break;
        }
        if(res < min_int){
            res = min_int;
            break;
        }
    }
    if(num == 0) return 0;
    return res;
}

int main()
{
    cout<

9. Palindrome Number

题意:判断一个整数是不是回文数
题解:将x的后半段倒置,然后和前半段比较即可,注意可能有奇数个数字或偶数,末尾有0,以及负数的情形即可。
class Solution {
public:

    bool isPalindrome(int x) {
        if(x < 0 || (x != 0 && x % 10==0)) 
            return false;

        int y = 0;
        while(x > y){
            y = y * 10 + x % 10;
            x /= 10;
        }
        return (x == y) || (x == y / 10);
    }
};

10. Regular Expression Matching

题意:简单的正则表达式,判断给定字符串是不是符合给定的模式(只包含字符和*以及.)
题解:按照多年的acm经验,一眼看出是简单DP题。
dp[i][j]表示字符串和0到i个位置的子串,是否能与模式串0到j匹配。然后就分几种情况进行转移即可。注意在前面增加了空串(即0为置表示空串),能够简化代码。
解如下:
设d[i][j]表示原字符串的0到i能否和模式串的0到j匹配。字符串从1位置开始,0表示空串(方便处理)
故d[0][0] = 1,下面求转移方程
分三种情况:
p[j] = '.', p[j] = '*’和其他
当p[j] = '*' 时,要使得d[i][j] = 1,必须有如下情况之一发生
    a.d[i][j - 2] = 1 (p[j - 1], p[j] 匹配空串)
    b.d[i - 1][j - 1] = 1且(s[i] = p[j - 1]或 p[j - 1] = '.')
当p[j] = '.', 要使得d[i][j] = 1,必须有
    d[i - 1][j - 1] = 1
其他:
    d[i - 1][j - 1] = 1且 s[i] = p[j]


class Solution {
public:
    static const size_t maxN = 1000;
    static bool dp[maxN][maxN];
    bool isMatch(string s, string p) {
        size_t slen = s.length();
        size_t plen = p.length();

        dp[0][0] = true;
        for (size_t i = 0; i <= slen; ++i)
            for (size_t j = 1; j <= plen; ++j)
                if (p[j - 1] == '*')
                     dp[i][j] = dp[i][j - 2] ||  (i > 0 && (s[i - 1] == p[j - 2] || p[j - 2] == '.') && dp[i - 1][j]);
                else dp[i][j] = i > 0 && dp[i - 1][j - 1] && (s[i - 1] == p[j - 1] || p[j - 1] == '.');

        return dp[slen][plen];
    }
};
bool Solution::dp[maxN][maxN];

11. Container With Most Water

题意:给定一组数据,d0,d1,...dn,表示第i个位置有长为di的竖线,问这些竖线中那两根和x轴围成的容器能装最多的水?
题解:考虑双指针。从两边开始,注意到,如果两个指针(i和j)的一边比较短,那么,这一个边就可以抛弃掉,因为它不可能是最优解(不妨设i比较短(或者等长),假设它和另外一根线k组成最优解,那么k可能在i前面,ij中间,或者j后面。首先k不可能在i前面或者j后面,不然的话,我们不可能把k抛弃掉。另外,它也不可能在ij中间,因为如果在ij中间的话ik肯定没有ij这一对好,所以i可以抛弃掉)。
class Solution {
public:
    int maxArea(vector& height) {
        int ans = 0, left = 0, right = height.size() - 1;
        while (left < right) {
            ans = max(ans, min(height[left], height[right]) * (right - left));
            if (height[left] < height[right])
                ++left;
            else
                --right;
        }
        return ans;
    }

};

12. Integer to Roman

题意:将一个整数转换成罗马数字(罗马数字参考http://baike.baidu.com/link?url=uhmgSPP-MZpOKblUj9gO6pvpuLzgx2XteJGqyPoJjmrTpRZ8H_Jx_WUz2gmIxMwnLKKZKpnkPOUVbkMc4RY0fjYjnYU4Y6snyWn0lmLXu9gHxsUoLi7PgJBr_cdm-NNd)
数字在1到3999之间
简单题,这是别人写的代码
class Solution {
public:
    string intToRoman(int num) {
        string M[] = {"", "M", "MM", "MMM"};
        string C[] = {"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"};
        string X[] = {"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"};
        string I[] = {"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"};
        return M[num / 1000] + C[(num % 1000) / 100] + X[(num % 100) / 10] + I[num % 10];        
    }
};

13. Roman to Integer

题意:将罗马数字转换成整数
题解:简单题
别人的代码:http://blog.csdn.net/booirror/article/details/43197595
class Solution {
public:

    int getVal(char a){
        switch (a) {
        case 'I':
            return 1;
        case 'V':
            return 5;
        case 'X':
            return 10;
        case 'L':
            return 50;
        case 'C':
            return 100;
        case 'D':
            return 500;
        case 'M':
            return 1000;
        }
        return 0;
    }

    int romanToInt(string s){
        int res = 0;
        char max = 'I';
        for (int i = s.size()-1; i >= 0; --i) {
            if (getVal(s[i]) >= getVal(max)) {
                max = s[i];
                res += getVal(s[i]);
            } else {
                res -= getVal(s[i]);
            }
        }
        return res;
    }


};

14. Longest Common Prefix

题意:给定一个字符串数组,找出他们的最长公共前缀。
题解:简单题
class Solution {
public:
    string longestCommonPrefix(vector& strs) {
        string prefix = "";
        if(strs.size() < 1) return prefix;
        for(size_t i = 0; i < strs[0].size();prefix += strs[0][i++])
            for(size_t j = 0;j < strs.size(); ++j)
                if(i >= strs[j].size() || strs[j][i] != strs[0][i]) return prefix;
        return prefix;
    }
};

15. 3Sum

题意:给定一个数组,三个数和为0的数对有哪些
题解:为了避免重复,先排序。先枚举第一个数,然后2和3 用two sum的方法。下面代码修改自
1.two sum(不过这里不需要给出下标),渐进时间复杂度为)(n^2)。容易改成计数或者hash的做法,这里不给出。

quadruple

class Solution {
public:
    vector> threeSum(vector& nums) {
        sort(nums.begin(), nums.end());
        vector> ans;
        int len = nums.size();

    for(size_t i = j + 1; i < len; ++i){
            int target = -nums[i] - nums[j];
            size_t left = i + 1, right = len - 1;
            while(true){
                while(left > i + 1 && left < right && nums[left] == nums[left - 1]) ++left;
                while(right < len - 1 && left < right && nums[right] == nums[right + 1]) --right;
                if(left >= right) break;
                if(nums[left] + nums[right] == target){
                    vector triplet(3, 0);
                    triplet[0] = nums[i];
                    triplet[1] = nums[left++];
                    triplet[2] = nums[right--];
                    ans.push_back(triplet);
                }else if(nums[left] + nums[right] < target){
                    ++left;
                }else{
                    --right;
                }
            }
        }

        return ans;

    }
};

16. 3Sum Closest

题意:给定一个数组和一个目标值,求离目标最近的三个数的和是多少?
题解:和15没啥不一样,改一下代码即可
class Solution {
public:
    int threeSumClosest(vector& nums, int target) {        
        sort(nums.begin(), nums.end());
        int len = nums.size();
        int ans = nums[0] + nums[1] + nums[2];
        int dis = abs(ans - target);
        for(size_t i = 0; i < len; ++i){
            if(i > 0 && nums[i] == nums[i - 1]) continue;
            size_t left = i + 1, right = len - 1;
            int T = target - nums[i];
            while(true){
                while(left > i + 1 && left < right && nums[left] == nums[left - 1]) ++left;
                while(right < len - 1 && left < right && nums[right] == nums[right + 1]) --right;
                if(left >= right) break;
                if(abs(nums[left] + nums[right] - T) < dis){
                    dis = abs(nums[left] + nums[right] - T);
                    ans =  nums[left] + nums[right] + nums[i];
                }
                if(dis == 0) break;
                if(nums[left] + nums[right] < T){
                    ++left;
                }else{
                    --right;
                }
            }
        }
        return ans;

    }
};

17. Letter Combinations of a Phone Number

题意:给定一串数字字符串,问按照电话上的字母转换规则,能转换成那些字母的字符串。
题解:可以有多种方法解决,我是利用计数迭代的方法,遍历所有的排列。

class Solution {
public:
    vector letterCombinations(string digits) {
        string kvmaps[8] = {"abc", "def", "ghi",  "jkl",  "mno",  "pqrs",  "tuv",  "wxyz" };
        int  length[8] = {3,3,3,3,3,4,3,4};
        vector ans;
        if(digits == "") return ans;
        vector iter(digits.size(),0);
        bool flag = true;
        int digit;
        while(flag){
            string s = "";
            for(int i = 0 ; i < digits.size(); ++i){
                digit = digits[i] - '0';
                if(digit == 0) digit = 10;
                s += kvmaps[digit - 2][iter[i]];
            }
            ans.push_back(s);
            ++iter[0];
            for(int i = 0; i < digits.size(); ++i){
                digit = digits[i] - '0';
                if(digit == 0) digit = 10;
                if(iter[i] >= length[digit - 2]){
                    if(i == digits.size() - 1){flag = false; break;}
                    else{
                        iter[i] = 0;
                        ++iter[i + 1];
                    }

                }

            }

        }
        return ans;

    }
};

18. 4Sum

题意:给定一个数组和一个目标整数,求数组中不重复的四元组的和为目标的四元组
题解:在15题加一层循环即可

class Solution {
public:
    vector> fourSum(vector& nums,int T) {
        sort(nums.begin(), nums.end());
        vector> ans;
        int len = nums.size();
        for(size_t j = 0;j < len; ++j){
            if(j > 0 && nums[j] == nums[j - 1]) continue;    
            for(size_t i = j + 1; i < len; ++i){
                    if(i > j + 1 && nums[i] == nums[i - 1]) continue;
                    int target = T - nums[i] - nums[j];
                    size_t left = i + 1, right = len - 1;
                    while(true){
                        while(left > i + 1 && left < right && nums[left] == nums[left - 1]) ++left;
                        while(right < len - 1 && left < right && nums[right] == nums[right + 1]) --right;

                        if(left >= right) break;
                        if(nums[left] + nums[right] == target){
                            vector quadruples(4, 0);
                            quadruples[0] = nums[j];
                            quadruples[1] = nums[i];
                            quadruples[2] = nums[left++];
                            quadruples[3] = nums[right--];
                            ans.push_back(quadruples);
                        }else if(nums[left] + nums[right] < target){
                            ++left;
                        }else{
                            --right;
                        }
                    }
                }
        }
        return ans;

    }
};

19. Remove Nth Node From End of List

题意:通过一次遍历,将链表的倒数第n个删掉
题解:虽然说是一次遍历,实际上只不过是两次遍历写在一个循环里面罢了。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* h1 = head, *h2 = head;
        while(n-->0) h2 = h2->next;
        if(!h2) return head->next;
        h2 = h2->next;
        while(h2 != NULL){
            h1 = h1->next;
            h2 = h2->next;
        }
        h1->next = h1->next->next;   // the one after the h1 need to be removed
        return head;
}
};

20. Valid Parentheses

题意: 给定一个包含三种括号的字符串,判断是否是合法的括号嵌套。
题解:模拟匹配过程即可
class Solution {
public:
    bool isValid(string s) {
        stackS;
        if(s.empty()) return true;
        if(s[0] != '(' && s[0] != '[' && s[0] != '{') return false;
        S.push(s[0]);
        for(int i = 1;i < s.length(); ++i){
            if(s[i] == ')' || s[i] == ']' || s[i] == '}'){
                if(S.empty()) return false;
                if(s[i] == ')' && S.top() != '(' || s[i] == ']' && S.top() != '[' || s[i] == '}' && S.top() != '{') return false;
                S.pop();
            }else{
                S.push(s[i]);
            }
        }
        return S.empty();
    }
};

21. Merge Two Sorted Lists

题意:将两个有序链表合并(从小到大)
题解:简单题
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(l1 == NULL) return l2;
        if(l2 == NULL) return l1;
        ListNode * listHead, *p;
        listHead = p = new ListNode(0);
        while(l1 && l2){
            if(l1->val < l2->val) {
                p->next = l1;
                l1 = l1->next;
            }else{
                p->next = l2;
                l2 = l2->next;
            }
            p = p->next;
        }
        if(l1 != NULL) p->next = l1;
        else p->next = l2;
        p = listHead->next;
        delete listHead;
        return p;
    }
};

22. Generate Parentheses

题意:给定一个n,生成所有可能长度为n的嵌套括号组合
题解:可以用dfs求解。不过我这里用bfs,然后用位标记法代替字符串以节省内存。

class Solution {
public:
    vector generateParenthesis(int n) {
        vector ans;
        n *= 2;
        //1是右括号,0为左括号
        cout<<'yes';
        queue > Q;
        Q.push(make_pair(0LL,0x01));
        while(!Q.empty()){
            pair p = Q.front();
            Q.pop();
            if(p.second == n){
                ans.push_back(genString(p.first,n));
                continue;
            } 
            //添加左括号
            char leftParenthesisNum = p.second - bitCount(p.first);
            //cout< p.second - leftParenthesisNum)Q.push(make_pair(p.first | (1 << p.second), p.second + 1));1<>1) &0x5555555555555555) ; 
        n = (n &0x3333333333333333) + ((n >>2) &0x3333333333333333) ; 
        n = (n &0x0f0f0f0f0f0f0f0f) + ((n >>4) &0x0f0f0f0f0f0f0f0f) ; 
        n = (n &0x00ff00ff00ff00ff) + ((n >>8) &0x00ff00ff00ff00ff) ; 
        n = (n &0x0000ffff0000ffff) + ((n >>16) &0x0000ffff0000ffff) ; 
        n = (n &0x00000000ffffffff) + ((n >>32) &0x00000000ffffffff) ; 
        return char(n&0xff) ; 
    }
    
    string genString(long long parentheses, int n){
        string s = "";
        for(int i = 0;i < n; ++i){
            if((parentheses>>i) & 1) s+=')';
            else s+= '(';
        } 
        return s; 
    }
};

23. Merge k Sorted Lists

题意:将k个有序链表合并成一个
题解:用优先队列即可。主义c++优先队列是大顶堆
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    struct cmp
    {
        bool operator()(ListNode * &a,ListNode * &b){
            return a->val > b->val;
        }
    };
    ListNode* mergeKLists(vector& lists) {
        priority_queue, cmp>Q;
        ListNode *head,*p;
        head = p = new ListNode(0);
        for(size_t i = 0;i < lists.size(); ++i)
            if(lists[i] != NULL) Q.push(lists[i]);
        while(!Q.empty()){
            ListNode *node = Q.top();
            Q.pop();
            p->next = node;
            p = p->next;
            if(node->next) Q.push(node->next);
        }
        p = head->next;
        delete head;
        return p;
    }
};

24. Swap Nodes in Pairs

题意:交换链表中的连续的对(交换(1,2),(3,4 ) 以此类推)。
题解:模拟即可
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        int index = 0;
        ListNode* l1, *l2, *tmp = new ListNode(0);
        tmp->next = head;
        head = tmp;
        while(tmp){
            if(!tmp->next) break;
            l1 = tmp->next;
            if(!l1->next) break;
            l2 = l1->next;
            tmp->next = l2;
            l1->next = l2->next;
            l2->next = l1;
            tmp = l1;
        }
        tmp = head->next;
        delete head;
        return tmp;
    }
};

31. Next Permutation

题意:给定一个数组,求排列中,下一个排列。
题解:注意可能有重复数字。模仿我们人计算过程。首先判断是否为最后一个排列,是就输出翻转后的结果。不然,将排列分成三部分(我们假设长度大于等于2,第一部分可为空)[a, b, c](a,c为序列,b为一个数字),其中c呈倒序,而且b小于c的第一个数字,那么下一个排列将为,a不变,将b和c最后一个大于它的数交换,然后将c翻转。(这个是很容易想到的,实现也简单)
class Solution {
public:
    
    int decreaseLen(vector & nums){
        int len = 1;
        for(int i = nums.size() - 2; i >= 0 && nums[i] >= nums[i + 1] ;--i,++len);
        return len;
    }
    void nextPermutation(vector& nums) {
        if(nums.size() <= 1) return;
        int len = decreaseLen(nums);
        if(len == nums.size()){reverse(nums.begin(),nums.end());return;}
        int id = nums.size() - len - 1;
        int pos = id;
        //找到后面最后一个大于它的数
        for(int i = id + 1;nums[id] < nums[i] && i <= nums.size(); ++i)++pos; 
        pos = min(pos,int(nums.size() - 1) );
        swap(nums[id],nums[pos]);
        reverse(nums.begin() + id + 1,nums.end());
    }
};

32. Longest Valid Parentheses

题意:给一个包含'('和')'的括号串,求最长的合法配对子括号串长度。
题解:动态规划,dp[i]表示以第i个位置作为结束时的最长串长度。
当s[i] == '('时,dp[i] = 0.
当s[i] == ')'时候,看前面那个配对到哪里,然后这个括号能否配对,如果不能,也是dp[i] = 0,能的话递推。
class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.length();
        if(n <= 1) return 0;
        vector dp(n);
        int ans = 0;
        dp[0] = 0;
        for(int i = 1; i < n; ++i){
            if(s[i] == '(') dp[i] = 0;
            else{
                if(s[i - 1] == '('){
                    if(i == 1) dp[i] = 2;
                    else dp[i] = dp[i - 2] + 2; 
                }else{
                    int idx = i - 1 - dp[i - 1];
                    if(idx >= 0 && s[idx] == '('){
                        if(idx > 0)
                        	dp[i] = dp[i - 1] + dp[idx - 1] + 2;
                        else  dp[i] = dp[i - 1] + 2;
                    }else dp[i] = 0;
                }
            }
            ans = max(ans,dp[i]);
        }
    	return ans;
    }
};

在s前面补一个'('可以少个两个判断条件。
class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.length();
        if(n <= 1) return 0;
        s = ')' + s; ++n;
        vector dp(n);
        int ans = 0;
        dp[0] =  dp[1] = 0;
        for(int i = 2; i < n; ++i){
            if(s[i] == '(') dp[i] = 0;
            else{
                if(s[i - 1] == '('){
                    if(i == 1) dp[i] = 2;
                    else dp[i] = dp[i - 2] + 2;
                    
                }else{
                    int idx = i - 1 - dp[i - 1];
                    if(s[idx] == '('){
                        dp[i] = dp[i - 1] + dp[idx - 1] + 2;
                    }else dp[i] = 0;
                }
            }
            ans = max(ans,dp[i]);
        }
    	return ans;
    }
};

37. Sudoku Solver

题意:求解一个数独
题解:深度搜索和剪枝
‘class Solution {
public:
    void solveSudoku(vector>& board) {
        vector >rowVis(9,vector(9,false));
        vector >colVis(9,vector(9,false));
        vector >lat (9,vector(9,false));
        
        for(int i = 0;i < 9; ++i)
            for(int j = 0;j < 9; ++j){
                if(board[i][j] == '.') continue;
                int x = board[i][j] - '1';
                rowVis[i][x] = colVis[j][x] = lat[(i / 3) * 3 + j / 3][x] = true;
            }
        dfs(board,rowVis,colVis,lat,0,0);
    }
    
    bool dfs(vector>& board,vector >& rowVis,vector >& colVis,vector >&lat,int x,int y){
        if(y == 9) y = 0,++x;
        if(x == 9) return true;
        if(board[x][y] == '.'){
             for(int i = 0; i < 9; ++i){
                 if(rowVis[x][i] || colVis[y][i] || lat[(x / 3) * 3 + y / 3][i]) continue;
                 board[x][y] = i + '1';
                 rowVis[x][i] = colVis[y][i] = lat[(x / 3) * 3 + y / 3][i] = true;
                 
                 if(dfs(board,rowVis,colVis,lat,x,y + 1)) return true;
                 board[x][y] = '.';
                 rowVis[x][i] = colVis[y][i] = lat[(x / 3) * 3 + y / 3][i] = false;
                 
             }
        }else
            return dfs(board,rowVis,colVis,lat,x,y + 1);
        return false;
    }
};

41. First Missing Positive 

class Solution {
    //ye keyi yong zhengfu biaoshi zhuangtai jiang ta fangdao ziji de weizhi shang
public:
    int firstMissingPositive(vector& nums) {
        int n = nums.size();
        for(int i = 0; i < n; ++ i)
            while(nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i])
                swap(nums[i], nums[nums[i] - 1]);
         
        for(int i = 0; i < n; ++ i)
            if(nums[i] != i + 1)
                return i + 1;
         
        return n + 1;
    }
     
};

42. Trapping Rain Water 



题意:给定一个一维的柱形图,把它看成一容器,问最多能装多少雨水。
题解:计算出左边,右边的最高点即可。
class Solution {
public:
    int trap(vector& height) {
        int n = height.size();
        if(n <= 1) return 0;
         
         
        vector left(n,0),right(n,0);
        left[0] = height[0];
        right[n - 1] = height[n - 1];
         
        for(int i = 1;i < n; ++i){
            left[i] = max(left[i - 1],height[i]);
            right[n - i - 1] = max(height[n - i - 1],right[n - i]);
        }
        int ans = 0;
         
        for(int i = 0;i < n; ++i){
            ans += min(left[i],right[i]) - height[i];
        }
         
        return ans;
         
    }
};

44.Wildcard Matching  

题意:给一个字符串和一个模式串,问能不能匹配。其中模式串中字符'?'能匹配任意一个字符,'*'能匹配任意多个字符(可以是0个)。
题解:dp(O(n^2))或双指针
动态规划思路:dp[i][j]表示模式串0到i - 1能不能匹配源串的0~j - 1的子串。
初始dp[0][0] = true, dp[i][0] = true,当模式串前缀0 到i - 1为'*'
分别分三种情况进行转移:
p[i - 1] == '?' 时, dp[i][j] =  dp[i - 1][j - 1];
p[i - 1] 为字母 , dp[i][j] = dp[i - 1][j - 1] && s[j - 1] == p[i - 1]
p[i - 1] == '*', dp[i][j] = dp[i][j - 1] || dp[i - 1][j] (即*至少匹配一个字符和不匹配两种情况)
然后由于dp[i]只和dp[i - 1]有关系,可以利用滚动数组,利用一维数组就可以搞定。

class Solution {
public:
    bool isMatch(string s, string p) {
        int n,m;
        n = s.length();
        m = p.length();
        if(m == 0 && n == 0) return true;
        
        vector dp(n + 1,false);
        bool curstate,prestate;
        dp[0] = true;
        
        for(int j = 1;j <= m; ++j){
                prestate = dp[0];
         	dp[0] = dp[0] && p[j - 1] == '*';
            for(int i = 1;i <= n; ++i){
                curstate = dp[i];
                if(p[j - 1] == '*')
                    dp[i] = dp[i - 1] || dp[i];
                else
                    dp[i] = prestate && (s[i - 1] == p[j - 1] || p[j - 1] == '?');
	            prestate = curstate;   
            }
        }
        return dp[n];
    }
};

看了题解发现还有双指针的写法:  	
class Solution {
public:
    bool isMatch(string s, string p) {
        int  slen = s.size(), plen = p.size(), i, j, iStar=-1, jStar=-1;

        for(i=0,j=0 ; i=0)
                    { // met a '*' before, then do traceback
                        i = iStar++;
                        j = jStar;
                    }
                    else return false; // otherwise fail
                }
            }
        }
        while(p[j]=='*') ++j;
        return j==plen;
    }
};

45. Jump Game II

题意:给定一个数组,其中每一个位置的数值表示从这个位置可以往后跳至多多少个位置。问从第开头位置开始,至少多少次可以跳到末尾
题解:简单的BFS。由于是至多可以往后跳多少个位置,所以我们只需要标记最后一个进队的元素下标(或者是第一个还没进队的)。因为进队肯定是从前往后的。每个元素至多进一次,出一次,所以是O(n)时间复杂度 

class Solution {
public:
    int jump(vector& nums) {
        int n = nums.size();
        if(n <= 1) return 0;
        queue > Q;
        Q.push(make_pair(0,0));
        vector vis(n, false);
        int index = 1;

        while(!Q.empty()){
            pair u = Q.front();
            Q.pop();
            int cur = u.first;
            int step = u.second;
            if(cur == n - 1) return step;
            for(;index <= nums[cur] + cur && index < n; ++index){
                Q.push(make_pair(index,step + 1));
            }
        }
        return 0;
    }
};

51. N-Queens

题意:返回n皇后所有合法状态,以字符串的形式,Q为皇后,.为空
题解:dfs枚举
class Solution {
public:

    vector> solveNQueens(int n) {
        vector> res;

        if(n == 0) return res;
        vector colVis(n,false);
        vector hypVis(2*n - 1,false);
        vector rhypVis(2*n -1,false);
        vector tmp;
        dfs(n,0,colVis,hypVis,rhypVis,tmp,res);
        return res;
    }

    void dfs(const int n,int row,vector &colVis,vector &hypVis,vector &rhypVis,vector &pos,vector> &res){
        if(row == n){
            vector tmp = vector();
            for(int i = 0;i < n; ++i){
                string s(n,'.');
                s[pos[i]] = 'Q';
                tmp.push_back(s);
            }
            res.push_back(tmp);

        }else{
            for(int i = 0;i < n; ++i){
                if(colVis[i] || hypVis[row - i + n - 1] || rhypVis[row + i]) continue;
                colVis[i] = hypVis[row - i + n - 1] = rhypVis[row + i] = true;
                pos.push_back(i);
                dfs(n, row + 1,colVis,hypVis,rhypVis,pos,res);
                pos.pop_back();
                colVis[i] = hypVis[row - i + n - 1] = rhypVis[row + i] = false;
            }
        }
    }


};

52. N-Queens II

题意:和51一样,只不过是返回数目
题解:只要计算数目,改下51的代码即可
class Solution {
public:
    int totalNQueens(int n) {
        if(n <= 1) return 1;
        int res = 0;
        vector colVis(n,false);
        vector hypVis(2*n - 1,false);
        vector rhypVis(2*n -1,false);
        vector tmp;
        dfs(n,0,colVis,hypVis,rhypVis,tmp,res);
        return res;
    }

    void dfs(const int n,int row,vector &colVis,vector &hypVis,vector &rhypVis,vector &pos, int & res){
        if(row == n){
            ++res;

        }else{
            for(int i = 0;i < n; ++i){
                if(colVis[i] || hypVis[row - i + n - 1] || rhypVis[row + i]) continue;
                colVis[i] = hypVis[row - i + n - 1] = rhypVis[row + i] = true;
                pos.push_back(i);
                dfs(n, row + 1,colVis,hypVis,rhypVis,pos,res);
                pos.pop_back();
                colVis[i] = hypVis[row - i + n - 1] = rhypVis[row + i] = false;
            }
        }
    }

};

57. Insert Interval

 题意:给定一个互不相交区间的数组,还有一个区间。将区间合并后输出
题解:模拟手动合并过程,不知道为什么是hard的。
/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class Solution {
public:
    vector insert(vector& intervals, Interval newInterval) {
        int s = 0,n = intervals.size();
        vector ans;
        
        for(s = 0;s < n && intervals[s].end < newInterval.start;++s){
            Interval t(intervals[s].start,intervals[s].end);
            ans.push_back(t);
        }
        int e = s ;
        while(e < n && intervals[e].start <= newInterval.end){
            ++e;
        }
        
        if(e == s) {
            ans.push_back(newInterval);
        }
        else{
            Interval t(min(newInterval.start,intervals[s].start), max(newInterval.end,intervals[e-1].end));
            ans.push_back(t);
        }
        
        for(int i = e;i < n; ++i){
            Interval t(intervals[i].start,intervals[i].end);
            ans.push_back(t);
        }
        return ans;
    }
};

65.Valid Number    

题意:给一个字符串,判断是不是数字
题解:用状态机异常简洁
列出合法情况,考虑每一种情况的转移矩阵转移矩阵
1.空格+ 数字 +空格
2.空格+ 点 + 数字 +空格
3.空格+ 符号 + 数字 + 空格
4.空格 + 符号 + 点 + 数字 +空格
5.空格 + (1, 2, 3, 4) + e + (1, 2, 3, 4) +空格

有限状态机的状态转移过程:
状态-1:非法状态
状态0:空格串
状态1:数字(非指数,没有小数点)
状态2:带小数点的数字(非指数,最后是小数点)
状态3:符号
状态4:带小数点数字
状态5:指数状态(最后字符为E)
状态6:指数带符号(最后一个为符号)
状态7:指数数字
最后为数字的终止状态有:1,4,7,8

状态8:数字合法结束状态(只接受空格)

起始为0:
  当输入空格时,状态仍为0,
  输入为符号时,状态转为3,3的转换和0是一样的,除了不能再接受符号,故在0的状态的基础上,把接受符号置为-1;
  当输入为数字时,状态转为1, 状态1的转换在于无法再接受符号,可以接受空格,数字,点,指数;状态1为合法的结束状态;
  当输入为点时,状态转为2,状态2必须再接受数字,接受其他均为非法;
  当输入为指数时,非法;
状态1:
  接受数字时仍转为状态1,
  接受点时,转为状态4,可以接受空格,数字,指数,状态4为合法的结束状态,
  接受指数时,转为状态5,可以接受符号,数字,不能再接受点,因为指数必须为整数,而且必须再接受数字;
状态2:
  接受数字转为状态4;
状态3:
  和0一样,只是不能接受符号;
状态4:
  接受空格,合法接受;
  接受数字,仍为状态4;
  接受指数,转为状态5,
状态5:
  接受符号,转为状态6,状态6和状态5一样,只是不能再接受符号,
  接受数字,转为状态7,状态7只能接受空格或数字;状态7为合法的结束状态;
状态6:
  只能接受数字,转为状态7;
状态7:
  接受空格,转为状态8,状态7为合法的结束状态;
  接受数字,仍为状态7;
状态8:
  接受空格,转为状态8,状态8为合法的结束状态;

class Solution
{
public:
    bool isNumber(string str)
    {
        enum InputType
        {
            INVALID,    // 0
            SPACE,      // 1
            SIGN,       // 2
            DIGIT,      // 3
            DOT,        // 4
            EXPONENT,   // 5
            //NUM_INPUTS  // 6
        };

        int transitionTable[][NUM_INPUTS] =
        {
            -1,  0,  3,  1,  2, -1,     // next states for state 0
            -1,  8, -1,  1,  4,  5,     // next states for state 1
            -1, -1, -1,  4, -1, -1,     // next states for state 2
            -1, -1, -1,  1,  2, -1,     // next states for state 3
            -1,  8, -1,  4, -1,  5,     // next states for state 4
            -1, -1,  6,  7, -1, -1,     // next states for state 5
            -1, -1, -1,  7, -1, -1,     // next states for state 6
            -1,  8, -1,  7, -1, -1,     // next states for state 7
            -1,  8, -1, -1, -1, -1,     // next states for state 8
        };

        int state = 0;
        string::iterator s = str.begin();
        while (s != str.end())
        {
            InputType inputType = INVALID;
            if (isspace(*s))
                inputType = SPACE;
            else if (*s == '+' || *s == '-')
                inputType = SIGN;
            else if (isdigit(*s))
                inputType = DIGIT;
            else if (*s == '.')
                inputType = DOT;
            else if (*s == 'e' || *s == 'E')
                inputType = EXPONENT;

            state = transitionTable[state][inputType];

            if (state == -1)
                return false;
            else
                ++s;
        }

        return state == 1 || state == 4 || state == 7 || state == 8;
    }
};

68. Text Justification

题意:把一个集合的单词按照每行L个字符放,每行要两端对齐,如果空格不能均匀分布在所有间隔中,那么左边的空格要多于右边的空格,最后一行靠左对齐。
题意:模拟即可,不难。
class Solution {
public:
    vector fullJustify(vector& words, int maxWidth) {
        vector ans;
        for(int i = 0;i < words.size(); ++i){
            int len = words[i].length(), totw = 1;
            
            int j = i;
            while(j + 1 < words.size() && words[j + 1].length() + len + 1 <= maxWidth){
                ++j;  ++totw;
                len += words[j].length() + 1;
            }
            string s = "";
            if(totw == 1){
                s = words[i] + string(maxWidth - words[i].length(),' ');
            }else{
            
                if(j + 1 == words.size()){ //last line
                    s = words[i];
                    for(int k = i + 1; k <= j; ++k){
                        s = s + ' ' + words[k];
                    }
                    s += string(maxWidth - len,' ');
                }else{
                    int totSpace = maxWidth - len + totw - 1;
                    int ave = totSpace / (totw - 1);
                    int remain = totSpace - ave * (totw - 1);
                    s = words[i];
                    for(int k = i + 1; k <= j; ++k){
                        s = s + string(ave + (remain > 0),' ') + words[k];
                        --remain;
                    }
                }
            }
            i = j;
            ans.push_back(s);
        }
        return ans;
    }
};

69. Sqrt(x)

 题意:给定给一个整数,对它开方
题解:二分或任意或其他优化算法
class Solution {
public:
    int mySqrt(int x) {
        //可以用牛顿迭代求解,这个一般优化方法也可以解的,这里用二分足够了
        int left = 1,right = x;
        if(x<=1)return x;
        int mid, ans =  0;
        while(left <= right){
            mid = (right - left) / 2 + left;
            if(mid <= x / mid){
                ans = mid;
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }

        return ans;

    }
};

72. Edit Distance

题意:编辑距离,允许插入,删除,修改一个字符,问最少经过多少次操作,将串1变成串2
题解:dp[i][j]表示串1的前缀1~i变成串2的1~j需要多少次操作,答案是dp[len1][len2];
可以利用滚动数组,将空间变成一维。
class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.length();
        int m = word2.length();
        if(n == 0 || m == 0) return max(n,m);
        
        vector > dp(n + 1,vector(m + 1,0));
        for(int i = 1;i <= n; ++i) dp[i][0] = i;
        for(int i = 1;i <= m; ++i) dp[0][i] = i;
        
        
        for(int i = 1;i <= n; ++i){
            for(int j = 1;j <= m; ++j){
                if(word1[i -1] == word2[j - 1]){
                    dp[i][j] = dp[i - 1][j - 1];
                }else{
                    dp[i][j] = 1 + min(dp[i - 1][j - 1],min(dp[i][j - 1],dp[i - 1][j]));
                }
            }
        }
        return dp[n][m];
    }
};

滚动数组:
class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.length();
        int m = word2.length();
        if(n == 0 || m == 0) return max(n,m);
        
        vector dp(m + 1,0);
       
        for(int i = 0; i <= m; ++i) dp[i] = i;
        
        for(int i = 1;i <= n; ++i){
            int pre = dp[0];
            dp[0] = i;
            for(int j = 1;j <= m; ++j){
                int cur = dp[j];
                if(word1[i -1] == word2[j - 1]){
                    dp[j] = pre;
                }else{
                    dp[j] = 1 + min(pre,min(dp[j - 1],dp[j]));
                }
                pre = cur;
            }
        }
        return dp[m];
    }
};

76. Minimum Window Substring

题意:给定两个字符串,s,t问s中含有t所有字符的最小子段。(保证唯一)
题解:计数就好了。没有什么难度
class Solution {
public:
    string minWindow(string s, string t) {
        if(t.size() == 0 || s.size() == 0) return "";
        int vis[256] = {0};
        int contain[256] = {0};
        
        int left = 0,right = 0,pos = INT_MAX,hasvis = 0,len = INT_MAX;
        
        for(int i = 0;i < t.size(); ++i) ++contain[t[i] + 127];
        
        while(left < s.size()){
            if(hasvis == t.size()){
                ++vis[s[left++] + 127];
            }
            while(left < s.size() && hasvis < t.size()){
                if( vis[s[left] + 127]++ < contain[s[left] + 127]) ++hasvis; 
                ++left;
            }
            
            while(right < left && vis[s[right] + 127] - 1 >= contain[s[right] + 127]){
                --vis[s[right] + 127];
                
                ++right;
            }
            
            if(hasvis != t.size()) return "";
            if(left - right  < len){
                len = left - right ;
                pos = right;
            }
        }
        return s.substr(pos,len);
    }
};

78. Subsets

题意:给定一个数字各不相同的数字的集合(数组),给出它的所有子集
题解:因为数字都不相同,那就很容易了,从子集大小慢慢一个个网上加,以递增的顺序保证不重复即可。
class Solution {
public:
    vector> subsets(vector& nums) {
        queue> Q;
        //sort(nums.begin() , nums.end());
        vector> ans;
        
        Q.push(vector());
        while(!Q.empty()){
            vector q = Q.front(); Q.pop();
            ans.push_back(q);
            if(q.size() >= nums.size()) continue;
            for(int i = 0;i < nums.size(); ++i){
                if(q.size() && nums[i] <= q[q.size() - 1]) continue;
                vectortmp(q);
                tmp.push_back(nums[i]);
                Q.push(tmp);
            }
        }
        return ans;
    }
};

84. Largest Rectangle in Histogram

题目:如题
题解:见85。单调栈dp。计算的是以当前点为最低点的最大长方形。
class Solution {
public:
    int largestRectangleArea(vector &height) {
    height.push_back(0);
        
    stack s;
    int maxSize = height[0];
    for(int i = 0; i < height.size(); ){
        if(s.empty() || height[i] >= height[s.top()]){
            s.push(i++);
        }
        else{
            int temp = height[s.top()];
            s.pop();
            maxSize = max(maxSize, temp * (s.empty() ? i  : i - 1 - s.top()));
        }
    }
    return maxSize;
}
};

85. Maximal Rectangle

题意:给定一个0-1矩阵,问其中最大的都为1的矩形面积。
题解:动态规划,先枚举矩的底边所在的行,然后对于每个点,记录这个点连续1的高度。问题变成了在一个条状图中寻找最大矩形(84题)。假设这个条状图为height数组。这个可以用动态规划+单调栈优化解决。用dp[i]表示以以height[i]为最高度且包含这一列的最大矩形面积,则只需要向左边和找到第一个比它低的即可,但是这样子太慢,需要用单调栈优化。以下有两种写法,第二中理解简单,第一种简洁。第二种写法是,分别计算左边的面积和右边的面积,加起来。第一种则一次算完。注意到S.top出栈的时候,我们就可以知道左边第一个比它低的是栈的下一个元素,而右边比它低的是当前元素。

class Solution {
    public:
int maximalRectangle(vector > &matrix) {
    int n = matrix.size();
    if(n == 0) return 0;
    int m =  matrix[0].size();
    
    
    vector height(m + 1, 0);
    int maxRec = 0;
    
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < m; ++j){
            if(matrix[i][j] == '0')
                height[j] = 0;
            else    
                ++height[j];
        }
        maxRec = max(maxRec, largestRectangleArea2(height,m));
        //maxec = max(mexRec,maxRec, largestRectangleArea(height))
    }
    return maxRec;
}

int largestRectangleArea(vector &height) {
    stack s;
    int maxSize = 0;
    for(int i = 0; i < height.size(); i++){
        if(s.empty() || height[i] >= height[s.top()]){
            s.push(i);
        }
        else{
            int temp = height[s.top()];
            s.pop();
            maxSize = max(maxSize, temp * (s.empty() ? i : i - 1 - s.top()));
            i--;
        }
    }
    return maxSize;
}
    
//more easier to understand
int largestRectangleArea2(vector &height , int n){
    stack S;
    int maxRec;
    vector tmp(height.size(),0);
    tmp[0] = maxRec = height[0];
    S.push(0);
    
    for(int i = 1;i < n; ++i){
        while(!S.empty() && height[S.top()] >= height[i]) S.pop();
        
        if(S.empty()) tmp[i] = height[i] * (i + 1);
        else tmp[i] = height[i] * (i - S.top());
        S.push(i);
    }
    
    S = stack();
    
    S.push(n - 1);
    maxRec = max(maxRec,tmp[n- 1]);
    
    for(int i = n - 2; i >= 0 ; --i){
        while(!S.empty() && height[S.top()] >= height[i]) S.pop();
        if(S.empty()) maxRec = max(maxRec, tmp[i] + height[i] * (n - i - 1));
        else maxRec = max(maxRec,tmp[i] + height[i] * (S.top() - i - 1));
        S.push(i);
    }
    return maxRec;
}
    
    
};

97. Interleaving String

题意:给三个串,问第三个串是不是前两个串的交叉得到。
题解:动态规划,dp[i][j]表示第一个的子串1~i和第二个串1~j交叉能否得到第三个串的子串1~i+j。然后用滚动数组,可以变成一维的空间。

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int n,m;
        n = s1.size();
        m = s2.size();
        if(n == 0) return s2 == s3;
        if(m == 0) return s1 == s3;
        if(m + n != s3.size()) return false;
        
        vector dp(m + 1, 0);
        dp[0] = true;
        for(int i = 0;i < m; ++i) dp[i + 1] = dp[i] && (s2[i] == s3[i]);
        
        for(int i = 1;i <= n; ++i){
            dp[0] = dp[0] && (s1[i - 1] == s3[i - 1]);
            for(int j = 1;j <= m; ++j){
                dp[j] = ((s1[i - 1] == s3[i + j - 1]) && dp[j]) || 
                        ((s2[j - 1] == s3[i + j - 1]) && dp[j - 1]);
            }
        }
        return dp[m];
        
    }
};

99. Recover Binary Search Tree

题意:给定一个二叉排序树,其中有两个节点的值交换了。目的是把这棵树恢复(但是只能用O(1)的辅助空间 
题解:对树进行中序遍历,注意到,第一个点比它的后继大,第二个比它前驱小。找出这两个点然后交换即可。中序遍历用morris traversal。

/**
 * 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 *a,*b;
    void recoverTree(TreeNode* root){
        a = b = NULL;
        inorderMorrisTraversal(root);
        int tmp = a->val;
        a->val = b->val;
        b->val = tmp;
    }
    
    void inorderMorrisTraversal(TreeNode *root) {
        TreeNode *cur = root, *prev = NULL,*c = NULL, *p = NULL;
        while (cur != NULL)
        {
            if (cur->left == NULL)          .
            {
                p = c;
                c = cur;
                cur = cur->right;
            }
            else
            {
                prev = cur->left;
                while (prev->right != NULL && prev->right != cur)
                    prev = prev->right;

                if (prev->right == NULL)   
                {
                    prev->right = cur;
                    cur = cur->left;
                }
                else                       
                {
                    prev->right = NULL;
                    p = c;
                    c = cur;
                    cur = cur->right;
                }
            }
            if(p != NULL){
                if(a == NULL && p->val >= c->val)
                    a = p;
                if(a != NULL && p->val >= c->val)
                    b = c;
            }   
        }     
  }  
};

115. Distinct Subsequences

题意:给定两个字符串,问第一个串中,有多少个和第二个串匹配的子序列
题解:动态规划,dp[i][j] 表示第一个串第0到i位置有多少个第二个串0到j这样的子序列。转移方程是显然的。
class Solution {
public:
    int numDistinct(string s, string t) {
        int **dp= new int* [s.length() + 1];
        for(int i = 0;i <= s.length(); ++i)
            dp[i] = new int[t.length() + 1];
        for(int i = 0;i <= t.length(); ++i) dp[0][i] = 0;
        
        for(int i = 1;i <= s.length(); ++i)
            for(int j = 1;j <= t.length(); ++j){
                dp[i][j] = dp[i - 1][j];
                if(j == 1){
                    dp[i][j] += (s[i - 1] == t[j - 1]);
                }else{
                    if(s[i - 1] == t[j - 1]) dp[i][j] += dp[i - 1][j - 1];
                }
            }
        return dp[s.length()][t.length()];
    }
};

或者
class Solution {
public:
    int numDistinct(string s, string t) {
        if(s == "") return 0;
        
        int **dp= new int* [s.length()];
        for(int i = 0;i < s.length(); ++i)
            dp[i] = new int[t.length()];
        
        for(int i = 0;i < t.length(); ++i) dp[0][i] = 0;
        dp[0][0] = (s[0] == t[0]);
        
        for(int i = 1;i < s.length(); ++i)
            for(int j = 0;j < t.length(); ++j){
                
                dp[i][j] = dp[i - 1][j];
                if(j == 0){
                    dp[i][j] += (s[i] == t[j]);
                }else{
                    if(s[i] == t[j]) dp[i][j] += dp[i - 1][j - 1];
                }
                
            }
        return dp[s.length() - 1][t.length() - 1];
    }
};

123. Best Time to Buy and Sell Stock III

 
题意:给定一个数组,表示每一天的股票价格。你只能进行至多两次交易(买入卖出,交易时间不想交),问最大的获益。
题解:动态规划。先遍历一次,用dp[i]算出第一次卖出时间点为i时的最大获利。然后枚举第二次卖出的时间点即可,再进行一次和前面一样的操作。多次也是一样的。具体见代码。
class Solution {
public:
    int maxProfit(vector& prices) {
        int n = prices.size();
        if(n <= 1) return 0;
        vector dp(n),dp2(n);
        int minimum = prices[0];
        dp[0] = 0;
        int ans = 0;
        for(int i = 1;i < n; ++i){
            minimum = min(minimum,prices[i]);
            dp[i] = prices[i] - minimum;
            ans = max(ans,dp[i]);
        }
        
       	int maximum ,tmp;
        maximum = tmp =  -prices[0];
       
        for(int i = 1;i < n; ++i) {
            maximum = max(maximum, dp[i - 1]);
            tmp = max(tmp, maximum - prices[i]);
            ans = max(ans,prices[i] + tmp);
            
        }
        return ans;
    }
};

124. Binary Tree Maximum Path Sum

题意:求出树种和最大的路径的和
题解:递归。左子树求最大,右子树求最大,还有经过当前点的最大。
懒得写了,别人的代码
/**
 * 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 maxPathSum(TreeNode *root) {
        int maxPath = INT_MIN;
        dfsMaxPath(root, maxPath);
        return maxPath;
    }

    int dfsMaxPath(TreeNode *root, int &maxPath) {
        if (!root) return 0;
        int l = max(0, dfsMaxPath(root->left, maxPath));
        int r = max(0, dfsMaxPath(root->right, maxPath));
        maxPath = max(maxPath, l + r + root->val);
        return root->val + max(l, r);
    }
};

126. Word Ladder II

题意:给定一个begin_word,一个end_word。以及一个wordList字符串数组,问所有从begin_word转换到end_word的最短序列。每次转换只能改变一个字符,改变的字符串必须在wordList中

题解:如果不是要输出所有结果而只求最少的转换次数,就是直接建图,然后bfs完事。而现在需要保存所有最短转换序列。我们只需要在bfs的时候记录下每个节点的遍历深度,然后再用一次dfs按照可能的边走一遍即可(用深度判断边是不是应该加入)。

class Solution {
    
public:
    bool is_connected(const string &a,const string &b){
        int diff = 0;
        for(int i = 0;i < a.length(); ++i){
            if(a[i] != b[i]) ++diff;
            if(diff > 1) break;
        }
        return diff == 1;
    }
    
    void dfs(int cur_node, const int &end_node, const vector& wordList, const vector> &graph, const vector &depth, vector &cur_list,  vector> &ans){
        cur_list.push_back(wordList[cur_node]);
        
        if(cur_node == end_node){
            ans.push_back(vector(cur_list));
        }else{
            
            for(int i = 0;i < graph[cur_node].size(); ++i) {
                int x = graph[cur_node][i];
                if(depth[cur_node] - 1 == depth[x]){
                    
                    dfs(x, end_node, wordList, graph, depth, cur_list, ans);
                }
            } 
        }
        cur_list.pop_back();
    }
    
    
    vector> findLadders(string beginWord, string endWord, vector& wordList) {
        int n = wordList.size();
        int begin_node = -1, end_node = -1;
        for(int i = 0;i < wordList.size(); ++i){
            if(wordList[i] == beginWord) begin_node =  i;
            else if(wordList[i] == endWord) end_node = i;
            
            if(begin_node != -1 and end_node != -1) break;
        }
        //end_word不在wordList中,自然不可能转换到
        if(end_node == -1){
            return {};
        }
        
        if(begin_node == -1){
            begin_node = n++;
            wordList.push_back(beginWord);
        }
        
        vector> graph(n, vector());
        vector vis(n, 0);
        
        //建图,可以用TrieTree加速
        for(int i = 0;i < n; ++i){
            for(int j = i + 1;j < n; ++j){
                if(is_connected(wordList[i], wordList[j])){
                    graph[i].push_back(j);
                    graph[j].push_back(i);
                }
            }
        }
        
        
        //bfs
        int min_step = INT_MAX;
        queue> Q;
        vector depth(n, 0);
        
        //正向BFS也一样。只不过一开始写这样,后面懒得改了
        Q.push(make_pair(end_node, 1));
        vis[end_node] = true;
        
        while(!Q.empty()){
            pair u = Q.front(); Q.pop();
            int x = u.first;
            int d = u.second;
            if(d > min_step) continue;
            if(x == begin_node){
                min_step = d;
            }
            
            depth[x] = d;
            
            for(int i = 0;i < graph[x].size(); ++i){
                int v = graph[x][i];
                if(vis[v]) continue;
                vis[v] = true;
                Q.push(make_pair(v, d + 1));
            }
        }
        
        
        if(min_step > n) return {};
        
        //dfs 按照深度找边
        vector> ans;
        vector cur_list;
        dfs(begin_node, end_node, wordList, graph, depth, cur_list, ans);
        
        return ans;
    }
};

 

128. Longest Consecutive Sequence

题意:给定一个无序数组,求最长的连续数字长度
题解:用hash_set标记即可。
class Solution {
public:
    int longestConsecutive(vector& nums) {
        if(nums.size() == 0) return 0;
        unordered_set S;
        int ans = 0;
        
        for(int i = 0;i < nums.size(); ++i) S.insert(nums[i]);
        
        for(int i = 0;i < nums.size(); ++i){
            if(S.erase(nums[i])){
                int count = 1;
                int val = nums[i];
                while(S.erase(val - 1)) ++count,--val;
                val = nums[i];
                while(S.erase(val + 1)) ++count,++val;
                ans = max(count,ans);
            }
        }
        return ans;
    }
};

132. Palindrome Partitioning II

题意:给定一个字符串,问至少多少次分割,可以把它分成一些回文串
题解: dp[i]表示第i个位置截止,经过多少次可以分成回文串。通常我们需要记录i到j是不是回文串,会用到O(n^2)的空间,但是如果枚举回文串的中间位置的话,就不需要任何记录了。

class Solution {
public:
    int minCut(string s) {
        int n = s.size();
        if(n <= 1) return 0;
        vector minCut(n + 1, 0);  
        
        for (int i = 0; i <= n; i++) minCut[i] = i - 1;
        
        for (int i = 0; i < n; ++i) {
            for (int j = 0; i - j >= 0 && i + j < n && s[i - j]==s[i + j] ;++j) // odd length palindrome
                minCut[i+j+1] = min(minCut[i + j + 1],1 + minCut[i - j]);

            for (int j = 1; i - j + 1 >= 0 && i + j < n && s[i - j + 1] == s[i + j]; ++j) // even length palindrome
                minCut[i + j + 1] = min(minCut[i + j + 1],1 + minCut[i - j + 1]);
        }
        return minCut[n];
    }
};

135. Candy


题意:有一些小朋友排成一列,每个人有个rating。现在要分给小朋友糖。rating比旁边大的要分更多,最少得一个。问最少需要多少糖才够分
题解:贪心。从rating小的开始分。如果旁边有人已经分有了,那么如果数值不是一样大,那么需要比他多一个。如果两边都一样大,那么分一个。

class Solution {
public:
    struct cmp{
      
        bool operator()(pair&a,pair&b){
            
            return a.first < b.first;
        }
        
    };
    int candy(vector& ratings) {
        vector>v;
        vector c(ratings.size() + 2,0);
        if(ratings.size() <= 1) return ratings.size();
        
        for(int i = 0;i < ratings.size(); ++i){
            v.push_back(make_pair(ratings[i],i + 1));
        }
        
        sort(v.begin(),v.end(),cmp());
        for(int i = 0;i < v.size(); ++i){
            pair u = v[i];
            int pos = u.second;
            c[u.second] = 1;
            
            if(u.second > 1 && ratings[u.second - 2] < ratings[u.second - 1]) c[u.second] = c[u.second - 1] + 1;
            if(u.second <= ratings.size() && ratings[u.second] < ratings[u.second - 1]) c[u.second] = max(c[u.second],c[u.second + 1] + 1);
            
        }
        int ans = 0;
        for(int i = 0;i < c.size(); ++i) ans += c[i];
        
        return ans;
        
        
    }
};

140. Word Break II

题意:给定一个串和一个字典,输出这个串按字典分割的所有可能结果
题解:记忆搜索,动态规划

class Solution {
public:
map> maps;
 vector wb(string s, set& wordDict) {
    if(maps.count(s)) return maps[s];
     
    vector res;
    int n = s.length();
    if(n == 0) return res;
    //judge if s can be break
    for(int j = n - 1; j >= 0; j--){
        if(wordDict.count(s.substr(j)))
            break;
        else if(j == 0)
            return res;
    }
    for(int i = 1; i < n; ++i)
    {
        if(wordDict.count(s.substr(0,i)))
        {
            vector strs = wb(s.substr(i),wordDict);
            
            for(int j = 0;j < strs.size(); ++j){
        	    res.push_back(s.substr(0,i) + " " + strs[j]);
            }
        }
    }
    if(wordDict.count(s)) res.push_back(s);
       
    maps[s] = res;
    return res;
}
   vector wordBreak(string s,  vector& wordDict) {  
   	   set dict(wordDict.begin(),wordDict.end());
       maps.clear();
       return wb(s,dict);
   }
};

146. LRU Cache

题意:设置一个支持两种O(1)操作的cache(插入和查询),key,val都是整数,val为正数。插入(key,val),如果容量不足,那么删除一个上一次访问时间最前的,再插入。查询,如果key存在,返回val,否则返回-1。
题解:用hashmap和链表。
class LRUCache {
public:
    int capacity,curc;
    struct Node{
        int val,key;
        Node(){}
        Node(int a,int b){key = a;val = b;}
        Node *pre,*next;
    }*head,*tail,h,t;
    unordered_map map;
    
    LRUCache(int capacity) {
        h = Node();
        t = Node();
        tail = &h;
        head = &t;
        head ->next = tail;
        tail -> pre = head;
        curc = 0;
        this->capacity = capacity;
        map = unordered_map();
    }
    
    int get(int key) {
        Node *node = map[key];
        if(node == NULL) return -1;
        Node *pre = node->pre,*next = node->next;
        pre->next = next;
        next->pre = pre;
        next = head->next;
        head ->next = node;
        node ->pre = head;
        node ->next = next;
        next ->pre = node;
        return node->val;
    }
    
    void put(int key, int value) {
        if(get(key) == -1){
            checkCap();
            addNode(key,value);
        }else{
            rmNode(map[key]);
            addNode(key,value);
        }
    }
    
    void rmNode(Node *node){
        Node *pre = node ->pre;
        Node *next = node ->next;
        pre -> next = next;
        next ->pre = pre;
        map[node->key] = NULL;
        delete node;
        --curc;
    }
    
    void addNode(int key,int value){
        Node *node = new Node(key,value);
        map[key] = node;
        Node * next = head ->next;
        head -> next = node;
        node ->pre = head;
        node ->next = next;
        next -> pre = node;
        ++curc;
    }
    
    void checkCap(){
        if(curc == capacity){
            rmNode(tail->pre);
        }
    }   
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

149. Max Points on a Line

题意:给n个二维平面的整数点,位于同一直线的点最多多少个
题解:枚举每个点。然后以这个点,计算出所有直线,由于都是整数,直线可以用两个整点表示。而我们固定了其中一个点,对于一条直线,如果我们取这条直线上离它最近的且位于右上方的不同整点,那么表示是唯一的。复杂的O(n^2logn),如果用hashmap,可以是O(n^2)。这题没啥意义。
/**
 * Definition for a point.
 * struct Point {
 *     int x;
 *     int y;
 *     Point() : x(0), y(0) {}
 *     Point(int a, int b) : x(a), y(b) {}
 * };
 */
class Solution {
public:
    int gcd(int a,int b){return b? gcd(b,a%b) : a;}
    static bool cmp(pair &a,pair &b){
        if(a.first == b.first){
            return a.second < b.second;
        }
        return a.first < b.first;
    }
    
    int maxPoints(vector& points) {
        int n = points.size();
        
        if(n <= 1) return n;
        int ans = 1;
        
        for(int i = 0; i < n; ++i){
            vector > lines;
            int same = 1,maxp = 0;
            for(int j = i + 1; j < n; ++j){
                int x = points[i].x - points[j].x;
                int y = points[i].y - points[j].y;
                if(x == 0 && y == 0) {++same; continue;}
                int d = gcd(x,y);
                lines.push_back(make_pair(x/d,y/d));
            }
            sort(lines.begin(),lines.end(),cmp);
            int tmp = lines.size() > 0;
            for(int j = 1;j < lines.size(); ++j){
                if(lines[j] == lines[j - 1]) ++tmp;
                else{
                    maxp = max(maxp,tmp);
                    tmp = 1;
                }
            }
            maxp = max(maxp,tmp);
            ans = max(ans,maxp + same);
        }
        return ans;
    }
};

153. Find Minimum in Rotated Sorted Array

见154,只是没有重复的而已。154的代码可直接用。

154. Find Minimum in Rotated Sorted Array II

题意:给定一个由有序序列翻转过的数组,求其中的最小值(可能存在重复值)。所谓翻转,就是讲前面一段移到后面去。
题解:二分查找即可,用最后一个元素做查找,由于有重复,我们将和最后一个元素相同的元素去掉,这样就没有任何不同了。

class Solution {
public:
    int findMin(vector& nums) {
        int n = nums.size();
        if(n == 1) return nums[0];
        int  left,right,mid,sentry = nums[n - 1];
        while(n > 1 && nums[n - 2] == sentry) --n;
        left = 0;
        while(left < n && nums[left] == sentry) ++left;
        int ans = sentry;
        right = n - 1;
        
        if(n == 1) return nums[0];
        while(left <= right){
            mid = (left + right) / 2;
            if(nums[mid] > sentry){
                left = mid + 1;
            }else if(nums[mid] <= sentry){
                right = mid - 1;
                ans = mid;
            }
        }
        return nums[ans];
    }
};

164. Maximum Gap

题解:给定一个无序正整数数组,问它排序后的相邻两数的最大间隔是多少?
题解:桶排序。设min为最小值,max为最大值,数组大小为n。那么最大gap的最小可能就是ceil((max - min) / n),只有当均匀分布时才达到。所以我们只要准备ceil((max - min) / ceil((max - min) / n))个桶,每个桶只要保存它的最小和最大值即可,gap肯定在相邻桶达到。
另一种是基数排序,因为数字是32位的,我们能使用基数排序。
桶排序:
class Solution {
public:
    int maximumGap(vector &num) {
        int n = num.size();
        if(n <= 1) return 0;
        
        int res =0;
        int minV, maxV;
        
        minV =  maxV = num[0];
        for(int i = 1; i < n; ++i){
            if(minV > num[i]) minV = num[i];
            else if(maxV < num[i]) maxV = num[i];
        }
        
        int bucket_size = max(1, (maxV - minV ) / (n - 1)); //the size of every bucket
        int bucket_num  = (maxV - minV) / bucket_size + 1; //num of buckets

        if(bucket_num <= 1) return (maxV - minV);    
        vector bucket_max(bucket_num, INT_MIN);
        vector bucket_min(bucket_num, INT_MAX);
        vector bucket_count(bucket_num, 0);
        
        for(int i = 0; i < n; ++i){
            int bucket_id = (num[i] - minV) / bucket_size;
            ++bucket_count[bucket_id];
            bucket_min[bucket_id] = bucket_min[bucket_id] = min(num[i], bucket_min[bucket_id]);
            bucket_max[bucket_id] = bucket_max[bucket_id] = max(num[i], bucket_max[bucket_id]);
        }
        
        int pre_max = minV;
        int maxGap = 0;
        for(int i = 0; i < bucket_num; ++i)
        {
            if(bucket_count[i])
            {
                maxGap = max(maxGap, bucket_min[i]- pre_max);
                pre_max = bucket_max[i];
            }
        }
        return maxGap;
    }

};
基数排序:
class Solution {
public:
    
    void radixSort(vector &num){
        for(int i = 0;i < 31; ++i){ //positive number
            vector zeroBucket,oneBucket;
            int neddle = 1 << i;
            for(auto &n: num){
                if(neddle & n) oneBucket.push_back(n);
                else zeroBucket.push_back(n);
            }
            num = zeroBucket;
            num.insert(num.end(),oneBucket.begin(),oneBucket.end());
        }
    }
    
    int maximumGap(vector &num) {
        int n = num.size();
        if(n <= 1) return 0;
        radixSort(num);
        
        int maxGap = 0;
        for(int i = 1;i < n;++i){
            maxGap = max(maxGap,num[i] - num[i - 1]);
        }
        return maxGap;
    }

};

166. Fraction to Recurring Decimal

题意:将有理数转成小数,循环小数的循环部分用括号括起来
题解:直接模拟,记录出现过的余数(我用map存)。注意int溢出还有符号问题。
class Solution {
public:
    string fractionToDecimal(int numerator, int denominator) {
        long long z = 1LL * numerator / denominator;
        long long remainder;
        int sign = (numerator < 0 && denominator > 0) || (numerator > 0 && denominator < 0);
        remainder = abs(1LL * numerator);
        long long denominatorL = abs(1LL * denominator);
        stringstream strStream;
        if(sign) strStream<<"-";
        strStream< maps;
        while(remainder){
            maps[remainder] = index++;
            remainder *= 10;
            
            tmp += char('0' + remainder / denominatorL);
            
            remainder %= denominatorL;
            
            unordered_map::iterator it = maps.find(remainder);
            if(it != maps.end()){
                 tmp = tmp.substr(0,(*it).second ) + "(" + tmp.substr((*it).second) + ")";
                break;
            }
        }
        
        return ans + tmp;
    }
};

174. Dungeon Game

题意: 给定一个二维的矩阵,每一个点有一个数字,给定一个初始值t,要从左上角移动到右下角(只能往下和往右),每经过一个点,当前值要和格子的数相加。问初始值至少为多少,才能使得能够从左上角到达右下角的过程中,值始终为正。
题解:设dp[i][j]表示从位置i,j到达右下角时到达该点的值至少是多少。那么dp[0][0]即为答案。从右下往左上递推即可。然后用滚动数组,只需一维辅助空间。

class Solution {
public:
    
    int calculateMinimumHP(vector >& dungeon) {
        if(dungeon.empty()) return 1;
        int n = dungeon.size();
        int m = dungeon[0].size();
        
        vector dp(m + 1, INT_MAX);
        dp[m - 1] = 1;
        for(int i = n - 1;i >=  0; --i){
            for(int j = m - 1;j >= 0; --j){
                int tmp = min(dp[j], dp[j + 1]) - dungeon[i][j];
                dp[j] = tmp <= 0 ? 1 : tmp;
            }
   		}
        return dp[0];
    }
        
};

185. Department Top Three Salaries

sql

187. Repeated DNA Sequences

题意:给定一个DNA序列(字符串,含ACGT四种字符),问长度为10且出现超过一次的串有哪些
题解:由于只有四种字符(我们可以用两位来编码一个字符,长度为10的字符只需要20位,能存在一个int数中)。而注意将字母表示成二进制,A ,C,G,T 分别为0101, 0103,0107, 0124.,他们最后一位都不一样,而且最大为7,所以我们能用3位来编码字符,虽然有冗余,但是存在一个int足够了,而且方便。编码后,用map存起来,每次只要找是否存在过,存在则输出当前十个字符。
大神的代码:
lass Solution {
public:
    vector findRepeatedDnaSequences(string s) {
    unordered_map m;
    vector r;
    int t = 0, i = 0, ss = s.size();
    while (i < 9)
        t = t << 3 | s[i++] & 7;
    while (i < ss)
        if (m[t = t << 3 & 0x3FFFFFFF | s[i++] & 7]++ == 1)
            r.push_back(s.substr(i - 10, 10));
    return r;
}
};

188. Best Time to Buy and Sell Stock IV

题意:和123题类似,只不过换成至多k次交易
题解: 下面使用O(kn)的动态规划,和123思路一致
class Solution {
public:
    int maxProfit(int k,vector& prices) {
        int n = prices.size();
        if(k == 0) return 0;
        if(n <= 1) return 0;
        vector > dp(2,vector(n,0));
        int minimum = prices[0];
        dp[0][0] = 0;
        int ans = 0;
        for(int i = 1;i < n; ++i){
            minimum = min(minimum,prices[i]);
            dp[0][i] = prices[i] - minimum;
            ans = max(ans,dp[0][i]);
        }
        
      if(k == 1) return ans;
      if(k > n) k = (n + 1) / 2;
      int maximum ,tmp;
        
       for(int j = 1; j < k; ++j){
            maximum = tmp =  - prices[0];
            for(int i = 1;i < n; ++i) {
                maximum = max(maximum, dp[1 - (j & 1)][i - 1]);
                tmp = max(tmp, maximum - prices[i]);
                dp[j & 1][i] = prices[i] + tmp;
                ans = max(ans,dp[j & 1][i]);
            }
       }
        return ans;
    }
};

这里有个更加好的解法:https://discuss.leetcode.com/topic/9522/c-solution-with-o-n-klgn-time-using-max-heap-and-stack/2

190. Reverse Bits

题意:32位无符号整数二进制翻转后的结果
题解:前导0也要翻转,就是整个32bit翻转

通常就是一位一位翻转,如下代码,当然可以更加快速,利用类似分治和位操作的思想。这个类似于那个bitCount。
class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        uint32_t ans = 0;
        int num = 0;
        while(n){
            ans <<= 1;
            ans |= (n & 1);
            n >>= 1;
            ++num;
        }
        while(num++ < 32) ans <<= 1;


        return ans;
    }
};


class Solution {
public:
    uint32_t reverseBits(uint32_t n) {
        n = (n >> 16) | (n << 16);
        n = ((n & 0xff00ff00) >> 8) | ((n & 0x00ff00ff) << 8);
        n = ((n & 0xf0f0f0f0) >> 4) | ((n & 0x0f0f0f0f) << 4);
        n = ((n & 0xcccccccc) >> 2) | ((n & 0x33333333) << 2);
        n = ((n & 0xaaaaaaaa) >> 1) | ((n & 0x55555555) << 1);
        return n;
    }
};

199. Binary Tree Right Side View

题意:给定一颗二叉树,找出它的右视图

leetcode 题解 (1-500题,持续更新,part 1)_第1张图片

 

 

题解:dfs,右子树先遍历即可。
/**
 * 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:
    void findAns(TreeNode* root, int depth, vector &ans){
        if(!root) return;
        if(depth >= ans.size()) ans.push_back(root->val);
        findAns(root->right,depth + 1,ans); 
        findAns(root->left,depth + 1,ans);
    }
    
    vector rightSideView(TreeNode* root) {
        vector ans;
        findAns(root,0,ans);
        return ans;
    }
};

212. Word Search II

题意:给定一个board,每个位置是一个字符。还有一个words字符串数组。问这个board可以组成那些words中的字符串。组成的方式是从board的任意一个位置开始,沿着上下左右四个位置所能遍历的字符组连接成的字符串(不能重复通过同一个位置)。

题解:将words用一个trie树存起来。然后查询。查询通过dfs进行。(反过来用dfs建树然后word查询会导致内存不足或者超时,可能是board比较大)。我用的是数组版的trie,比较占内存,而且比较耗时

class Trie {
    
    
    
public: 
  //对于字符串比较多的要统计个数的,map被卡的情况下,直接用字典树
    //很多题都是要用到节点下标来表示某个字符串
    static const int maxn = 6e4+1;
    //const int maxn =2e6+5;//如果是64MB可以开到2e6+5,尽量开大
    //int tree[maxn][26];//tree[i][j]表示节点i的第j个儿子的节点编号
    vector> tree;
    int idx[maxn];//表示以该节点结尾是一个单词
    int tot;//总节点数
    Trie():tree(vector(maxn, vector(26, 0))){
        tot = 0;
        memset(idx, -1, sizeof(idx));
        //memset(tree, 0, sizeof(tree));
        //init();
    }
    // void init(){
    //     for(int i = 0;i <= tot; ++i){
    //        //flag[i] = false;
    //        for(int j = 0; j < 26; ++j)
    //            tree[i][j] = 0;
    //     }
    //    tot = 0;//RE有可能是这里的问题
    // }
    
//     void insert_dfs(const int n, const int m, const vector> &board, int x,int y, int depth, vector> &vis, int cur_node){
//         if(depth <= 0) return ;
        
//         //int x = pos.first, y = pos.second;
//         vis[x][y] = true;
        
//         int cur_id = board[x][y] - 'a';

//         if (!tree[cur_node][cur_id]) tree[cur_node][cur_id] = ++tot;
//         cur_node =  tree[cur_node][cur_id];  
        
//         int dir_x[] = {0, 1, 0 , -1};
//         int dir_y[] = {1, 0, -1, 0};
        
//         for(int d = 0; d < 4; ++d){
//             int next_x = x + dir_x[d];
//             int next_y = y + dir_y[d];
//             if(next_x < 0 || next_y < 0 || next_x >= n || next_y >= m || vis[next_x][next_y]) continue;
//             insert_dfs(n, m, board, next_x, next_y, depth - 1, vis, cur_node);

//         }
//         vis[x][y] = false;
//     }
    
    void insert(string str, int i){
       int  len = str.length();
       int root = 0;
       for(int i = 0; i < len; ++i){
           int id = str[i] - 'a';
           if (!tree[root][id]) tree[root][id] = ++tot;
           root = tree[root][id];
       }
       idx[root] = i;
    }
    
    //n*m是board的大小,x,y是当前的位置,depth的剩余的长度,vis表示访问过的位置,cur_node是trie_tree当前的node索引,vis_word_idx记录访问到的单词下标
    
     
    void find_dfs(const vector> &board, int x, int y, int depth, vector> &vis, int cur_node, vector &vis_words_idx){
        if(depth <= 0) return ;
        
        int cur_id = board[x][y] - 'a';
        cur_node =  tree[cur_node][cur_id];  
        if(~idx[cur_node]) 
            vis_words_idx[idx[cur_node]] = true;
            
        
        //没有子节点了,直接返回
        if(!cur_node) return;
        static const int dir_x[] = {0, 1, 0 , -1};
        static const int dir_y[] = {1, 0, -1, 0};
        
        
        vis[x][y] = true;
        for(int d = 0; d < 4; ++d){
            int next_x = x + dir_x[d];
            int next_y = y + dir_y[d];
            if(next_x < 0 || next_y < 0 || next_x >= board.size() || next_y >= board[0].size() || vis[next_x][next_y]) continue;
            find_dfs(board, next_x, next_y, depth - 1, vis, cur_node, vis_words_idx);
        }
        vis[x][y] = false;
    }
    
    // bool find_word(string str){
    //     int len = str.length();
    //     int root = 0;
    //     for(int i = 0; i < len; ++i){
    //         int id = str[i] - 'a';
    //         if(!tree[root][id]) return false;
    //         root = tree[root][id];
    //     }
    //     return true;
    // }
};

class Solution {
public:
    vector findWords(vector>& board, vector& words) {
        int max_len = 0;
        Trie trie_tree;
        
        for(int i = 0;i < words.size(); ++i){
            max_len = max(max_len, int(words[i].length()));
            trie_tree.insert(words[i], i);
        }
        
        int n = board.size(), m = board[0].size();
        
        vector > vis(n, vector(m, false));
        // for(int i = 0;i < n; ++i){
        //     for(int j = 0;j < m; ++j){
        //         trie_tree.insert_dfs(n, m, board, i, j, max_len, vis, 0);
        //     }
        // }
        vector vis_words_idx(words.size(), false);
        
        for(int i = 0;i < n; ++i){
            for(int j = 0;j < m; ++j){
                //cout<<"----"< ans;
        for(int i = 0;i < words.size(); ++i){
            if(vis_words_idx[i])
                ans.emplace_back(words[i]);
        }
        return ans;
    }
};

214. Shortest Palindrome

题意:给定一个串,可以在前面加字符。求经过添加字符,产生的最短回文串。
题解:实际上只要我们求出从起始位置开始的最长回文串,把后面非回文串部分颠倒添加到前面即为答案。求最大回文串就有很多方法了,一个是使用经典的manacher算法,筛选出其中起始位置为开头的最大回文串。另一个是用KMP,把串颠倒,我们求的是这两个串的最长匹配长度。如果我们通过将s翻转得到s1,令s2 = s + '#'+s1,则按照next的定义我们从s2的next数组的最后一个值就可以得到我们要的最长长度。以下是这两种解法,都是O(n)

class Solution {
public:
    
    string shortestPalindrome(string s) {
        if(s.length() <= 1) return s;
        int n = s.length();
        char *str = new char[2 * n + 2];
        char *ns = new char[2 * n + 2];
        for(int i = 0;i < n; ++i) str[2 * i] = '#',str[2 * i + 1] = s[i];
        
        str[2 * n] = '#'; str[2 * n + 1] = '\0';
        n = 2 * n + 1;
        
        vector longestPailindrome(n,1);
        int right = 0,left,pos = 0,dis = 0,id = 0,len;
        
        for(int i = 1;i < n ; ++i){
            if(right <= i) left = i;
            else left = 2 * id - i;
            
            len = min(longestPailindrome[left],right - i + 1);
            while(i + len < n && i - len >= 0 && str[i + len] == str[i - len]) ++len;
            longestPailindrome[i] = len;
            if(i + len - 1 >= right){
                right = i + len - 1;
                id = i;
            }
            if(longestPailindrome[i] == i + 1) pos = i;
        }
        
        right = pos + longestPailindrome[pos];
    
        pos = 0;
        for(int i = n - 1;i >= right; --i) 
            if(str[i] != '#') ns[pos++] = str[i];
        for(int i = 0;i < n; ++i)
            if(i & 1) ns[pos++] = str[i];

        ns[pos] = '\0';
        
        s = string(ns);
        delete ns,str;
        return s;
    }
};

/*
class Solution {
public:
    
    vector getNext(string s){
        vector next(s.length(),0);
        int index = 0;
        for(int i = 1;i < s.length(); ++i){
            if(s[index] == s[i]){
                next[i] = next[i - 1] + 1;
                ++index;
            }else{
                index = next[i - 1];
                while(index > 0 && s[index] != s[i]){
                    index = next[index - 1];
                }
                if(s[index] == s[i]){
                    ++index;
                }
                next[i] = index;
            } 
        }
        return next;
    }
    
    string shortestPalindrome(string s) {
        if(s.length() <= 1) return s;
        int n = s.length();
        string tmp = s;
        cout< next = getNext(tmp);
        int len = next[next.size() - 1];
        return tmp.substr(s.length() + 1, s.length() - len) + s;
    }
};
*/

next数组求法
  vector getNext2(string s){
        vector next(s.length(),0);
        int index = 0,i = 1;
        while(i < s.length()){
            while(index > 0 && s[index] != s[i])
                    index = next[index - 1];

            if(s[index] == s[i]) ++index;
            next[i++] = index;
        }
        return next;
    }
    vector getNext(string s){
        vector next(s.length(),0);
        int index = 0,i = 1;
        while(i < s.length()){
            if(s[index] == s[i]){
                next[i++] = ++index;
            }else{
                if(!index) next[i++] = 0;
                else index = next[index - 1];
            }
        }
        return next;
    }
还可以有优化版本:
vector getNext(string s){
        vector next(s.length(),0);
        int index = 0;
        for(int i = 1;i< s.length(); ++i) {
            while(index > 0 && s[i] != s[index]) index = next[index - 1];  
            if(s[index] == s[i]) ++index;     
            if(s[i] == s[next[i - 1]]) next[i] = next[next[i - 1]] + 1;   
            else next[i] = index;  
        }  
        return next;
    }

218. The Skyline Problem

题意:给定一些矩形(left,right,height)的形式给出,如下图所示。求出其中的关键点,就是覆盖以后那些产生高度变化的点

leetcode 题解 (1-500题,持续更新,part 1)_第2张图片

题解:我是用优先队列求解的。首先这些点肯定是某个矩形的角。所以把所有可能的点先排个序,从小到大。我们判断当前位置是否为关键点。每次判断当前点的最高矩形高度,看看是否产生变化。而求最高高度可用一个优先队列维护。

class Solution {
    struct cmp{
        bool operator () (const vector &a, const vector&b) const{
            if(a[0] != b[0]) return a[0] < b[0];
            return a[2] >= b[2];
        }
    };
   
    
public:
    vector> getSkyline(vector>& buildings) {
        
        int n = buildings.size();
        vector pos(n << 1);
        for(int i = 0;i < n; ++i) {
            pos[i<<1] = buildings[i][0];
            pos[(i<<1) | 1] = buildings[i][1];
        }
        
        sort(pos.begin(), pos.end());
        sort(buildings.begin(), buildings.end(), cmp());
        
        //去重
        int uq_pos_size = unique(pos.begin(), pos.end()) - pos.begin();
        vector> ans;
        priority_queue> Q;
        int building_pos = 0;
        int prev_height = 0, next_right = 0;
        for(int i = 0;i < uq_pos_size; ++i){
            //if(i > 0 and pos[i] == pos[i - 1]) continue;
            int &cur_pos = pos[i];
            while(!Q.empty() and Q.top().second <= cur_pos) Q.pop();
            while(building_pos < n and buildings[building_pos][0] <= cur_pos){
                Q.push(make_pair(buildings[building_pos][2], buildings[building_pos][1]));
                ++building_pos;
            }
            
            int cur_height = Q.empty() ? 0 : Q.top().first;
            if(cur_height != prev_height){
                ans.push_back(vector{cur_pos, prev_height = cur_height});
            }
        }
        
        
        return ans;
    }
};

224. Basic Calculato

题意:计算一个只含有+-()和空格的表达式的值,其中数字是正整数
题解:用栈保存括号前的结果。
class Solution {
public:
    int calculate(string s) {
        stack  nums, ops;
        int num = 0;
        int rst = 0;
        int sign = 1;
        for (char c : s) { 
            if (isdigit(c)) {
                num = num * 10 + c - '0';
            }
            else {
                rst += sign * num;
                num = 0;
                if (c == '+') sign = 1;
                if (c == '-') sign = -1;
                if (c == '(') {
                    nums.push(rst);
                    ops.push(sign);
                    rst = 0;
                    sign = 1;
                }
                if (c == ')' && ops.size()) {
                    rst = ops.top() * rst + nums.top();
                    ops.pop(); nums.pop();
                }
            }
        }
        rst += sign * num;
        return rst;
    }
};

 233. Number of Digit One

题意:给定一个数字,问小于等于它的正整数中,1在各个数字的各个位中总共出现多少次。
题解:我们只要计算每个位上出现的1的个数加起来即可。假设我们现在有数字12145,我们要计算
百位上出现的1的个数。首先考虑前缀数字为0到11之间,对于每个前缀有100个,共有1200个。
再考虑前缀为12的,共46个,所以总共1246个。按这个思路写即可,对于每一位,将数字分成两半,然后计算。
class Solution {
public:
    int countDigitOne(int n) {
        int ans = 0;
        for (long long m = 1; m <= n; m *= 10)
            ans += (n/m + 8) / 10 * m + (n/m % 10 == 1) * (n%m + 1);
        return ans;
    }
};

239. Sliding Window Maximum

题意:给定一个序列,和一个数k,以k大小的窗口滑动,求每k个数中的最大值。
题解:单调队列经典应用。维护一个单调递减的队列,队列头部就是当前的最大值。当队列前超过窗口时移除,然后新进来的数,将小于它的移除。
class Solution {
public:
    vector maxSlidingWindow(vector& nums, int k) {
        if(k <= 1) return nums;
        deque Q;
        vectorans;
        
        for(int i = 0 ;i < nums.size(); ++i){
            while(!Q.empty() && i - Q.front() >= k) Q.pop_front();
            while(!Q.empty() && nums[i] > nums[Q.back()]) Q.pop_back();
            Q.push_back(i);
            if(i >= k - 1) ans.push_back(nums[Q.front()]);
        }
        return ans;
    }
};

260. Single Number III

题意:给定一个整数数组,其中除了两个数外的其他数都出现两次,找出这两个出现一次的数。
要求:常数辅助空间和线性时间
题解:利用位操作。注意到除了这两个数,其他数是一对对的。所以对整个数组求异或,我们得到这两个数的异或。
然后找这个异或结果中,不为0的一位r,这一位不为0表示两个数在这一位不一样。接着将数组分成两部分(按r位0或1分),分别异或就得到这两个数字。取得一位不为零,我们可以取最后一位,利用补码的性质它是x&(-x)

class Solution {
public:
    vector singleNumber(vector& nums) {
        int xorr = 0;
        for(int i = 0;i < nums.size() ; ++i) xorr ^= nums[i];
        int r = xorr & (-xorr);
        int axor=0,bxor=0;
        for(int i = 0;i < nums.size(); ++i){
            if(nums[i] & r) axor^=nums[i];
            else bxor^=nums[i];
        }
        vectorans;
        ans.push_back(bxor);
        ans.push_back(axor);
        return ans;
    }
};

273. Integer to English Words

题意:数字转英文
题解:直接做,用递归
class Solution {
public:
    vector unit = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine"} ;
        vector ten2twenty = {"Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; 
        vector decade = {"", "Ten", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"};
        vector name = {" Hundred "," Thousand "," Million "," Billion "};
        vector format = {100,1000,1000000,1000000000};
    string numberToWords(int num) {
        if(num == 0) return "Zero";
        return getString(num);
    }
    

    
    string getString(int num){
        string result;
        if (num < 10) result = unit[num];
        else if (num < 20) result = ten2twenty[num - 10];
        else if (num < 100) result = decade[num / 10] + " " + getString(num % 10);
        else{
            int dig = 0;
            while(dig < 4 && format[dig] <= num) ++dig;
            
            result = getString(num / format[dig - 1]) + name[dig - 1] +  getString(num % format[dig - 1]);
        }
        result.erase(0,result.find_first_not_of(" "));  
        result.erase(result.find_last_not_of(" ") + 1);  
        return result;
    }
};

282. Expression Add Operators

题意:给定一串数字和一个整数,在这串数字中添加+-*符号成为一个表达式,输出所有表达式的值和这个整个相同的结果。
题解:深度搜索。
class Solution {
public:
    vector addOperators(string num, int target) {
        vector result;
        if(num.size()==0)   return result;
        help(result, "", num, target, 0, 0, 0);
        return result;
    }
    
    void help(vector &result, string path, string num, int target, int pos, long cur, long prev){
        if(pos==num.size()){
            if(cur==target)   result.push_back(path);
            return;
        }
        for(int i=pos; ipos) break;
            string _str=num.substr(pos, i-pos+1);
            long _value=stol(_str);
            if(pos==0)  {
                help(result, path+_str, num, target, i+1, _value, _value);
            }
            else{
                help(result, path+"+"+_str, num, target, i+1, cur+_value, _value);
                help(result, path+"-"+_str, num, target, i+1, cur-_value, -_value);
                help(result, path+"*"+_str, num, target, i+1, cur-prev+prev*_value, prev*_value);
            }
        }
    }
};

295. Find Median from Data Stream

题意:动态增加数字,查找中位数
题解:利用480的算法即可。还可以维护两个堆,一个保存小于的一半数的大顶堆,一个维护大的一半的小顶堆。
class MedianFinder {
public:
    /** initialize your data structure here. */
    multiset window;
    multiset ::iterator mid;
    int totNum = 0;
    MedianFinder() {
        window = multiset();
        mid = window.end();
    }
    
    void addNum(int num) {
        ++totNum;
        window.insert(num);
        if (mid != window.end() ){
            if(num < *mid && totNum & 1) --mid;
            else if(num >= *mid && (totNum & 1) == 0) ++mid;
        }else mid = window.begin();
        
    }
    
    double findMedian() {
        return (double(*mid) + *prev(mid, 1 - totNum % 2)) / 2;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

297. Serialize and Deserialize Binary 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 Codec {
public:


    
    // Encodes a tree to a single string.
    
    string mySerialize(TreeNode* root) {
        if(root == NULL) return string("#");
        string s1 = serialize(root -> left);
        string s2 = serialize(root -> right);
        return to_string(root->val) + ',' + s1 + ',' + s2;
    }
  
    
    string serialize(TreeNode* root) {
        return mySerialize(root);
    }

    // Decodes your encoded data to tree.
     TreeNode* myDeserialize(string &data){
         if(data[0] == '#') {
            if(data.size() > 1) data = data.substr(2);
            return NULL;
        }
        
        int iter = data.find(',');
        
        TreeNode *root = new TreeNode(stoi(data.substr(0,iter)));
        data = data.substr(iter + 1);
        root -> left = myDeserialize(data);
        root -> right = myDeserialize(data);
        
        return root;
     }
    
    TreeNode* deserialize(string data) {
        return myDeserialize2(data);
    }
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

301. Remove Invalid Parentheses 

题意:给定一个括号的字符串(可能带其他字符),计算出所有最长的合法的括号序列(左右能匹配的)
题解:首先用贪心计算合法序列最长长度,然后dfs(bfs也可以)。剪枝就不多说了,还要避免重复的情况。怎么避免重复呢,只要对于连续相同的半边括号,删除总是从后面开始就可以了,(因为是最长度,是不可能跳过两个可以匹配的对的)。
class Solution {
public:
    
    void dfs(const string &s,const int cur,int depth,vector &ans,const string curString,const vector &rightPar,int left,int right,char last){
        if(right > left) return;
        if(depth == 0){
            if(left != right ) return;
            ans.push_back(curString);
            return;
        }
        if(cur == s.length()) return;
        
        if(rightPar[cur] < left - right) return;
        if(s[cur] == '('){
            if(last != '(')
                dfs(s,cur + 1,depth - 1,ans,curString+'(',rightPar,left + 1,right,'0');
            dfs(s,cur + 1,depth,ans,curString,rightPar,left,right,'(');
        }else if(s[cur] == ')'){
            if(last != ')')     
               dfs(s,cur + 1,depth - 1,ans,curString+')',rightPar,left,right + 1,'0');   
            dfs(s,cur + 1,depth,ans,curString,rightPar,left,right,')');
        }else{
             dfs(s,cur + 1,depth - 1,ans,curString+s[cur],rightPar,left,right,'0');
        }
    }
    
    vector removeInvalidParentheses(string s) {
        int depth = 0,remove = 0;
        stack S;
        for(int i = 0;i < s.length(); ++i){
            if(s[i] != ')' && s[i] != '(') continue;
            if(s[i] == '(') S.push(1);
            else{
                if(!S.empty())S.pop();
                else ++remove;
            }
        }
        
        vector rightParentheses(1 + s.length(),0);
        for(int i = s.length() - 1; i >= 0; --i){
            rightParentheses[i] = rightParentheses[i + 1] + (s[i] == ')');
        }
        
        remove += S.size();
        depth = s.length() - remove;
        
        vector ans;
        dfs(s,0,depth,ans,string(""),rightParentheses,0,0,'0');
        
        
        return ans;
    }
};

312. Burst Balloons

题意:有n个数的数组nums,每次从中拿走一个,得分是nums[left] * nums[i] * nums[right](即它乘以它旁边的数,如果旁边只有一个,就只乘一个,没有就是它本身),拿走后,left和right变成邻居。问最大得分是多少。 
题解:动态规划,在两端加两个1。设dp[i][j]表示从i到j依次拿走可以的得分。然后枚举i到j最后剩下的那个即可进行状态转移。 dp[i][j] = max(dp[i][j], nums[i-1]*nums[k]*nums[j+1] + dp[i][k-1] + dp[k+1][j])
class Solution {
public:
    int maxCoins(vector& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        if(n == 1) return nums[0];
        nums.insert(nums.begin(), 1);
        nums.push_back(1);
        vector > dp(n + 2, vector(n + 2, 0));
        for (int len = 1; len <= n; ++len)
            for (int left = 1; left <= n - len + 1; ++left) {
                int right = left + len - 1;
                for (int k = left; k <= right; ++k){
                    dp[left][right] = max(dp[left][right], nums[left-1]*nums[k]*nums[right+1] + dp[left][k-1] + dp[k+1][right]);
                }
            }
        return dp[1][n];
    }
};

 315. Count of Smaller Numbers After Self


题意:给定一个数组,计算出每一个数字后面比它大的数字的个数
题解:先把数组转成数字和下标pair的形式,然后排序(如果一样大,那么下标小的为小)。排完序之后,问题变成了,对于下标,前面有多少个比它大的。然后就可以用树状数组或者归并排序解决之。这里用的是树状数组。
class Solution {
public:
    
    struct cmp{
        bool operator()(const pair &a,const pair &b){
            if(a.first != b.first)   
                return a.first < b.first;
            else return a.second < b.second;
        }
    };
    
    struct BIT{ //binary indexed tree
        int n;
        vectora;
        BIT(int s){
            n = s;
            a = vector(n + 1,0);
        }
        int lowbit(int x){return (x&(-x));} 
    
        int sum(const int &x){  
            int res = 0;  
            for(int i = x;i > 0; i -= lowbit(i)) res += a[i];  
            return res;  
        }  

        void update(const int &x){  
            for(int i = x;i <= n; i += lowbit(i))  
                ++a[i] ;  
        }  
    };
    
    vector countSmaller(vector& nums) {
        int n = nums.size();
        if(n < 1) return vector();
        vector ans(n,0);
        vector > v;
        BIT bit(n);
        for(int i = 0;i < n; ++i){
            v.push_back(make_pair(nums[i],i));
        }
        sort(v.begin(),v.end(),cmp());
        
        
        for(int i = 0;i < n; ++i){
            ans[v[i].second] = i - bit.sum(v[i].second + 1);        
            bit.update(v[i].second + 1);
        }
        return ans;      
    }
};

316. Remove Duplicate Letters

题意:给定一个字符串,要求删除其中重复的字符,要求得到的字符串字典序最小。
题解:贪心。每次枚举最前面的那个字符,从小到大枚举。具体实现需要先记录每个字符的位置,然后判断先枚举第一个字符,然后删掉前面(包括自己)和重复的这个字符,然后得到和原先问题一样的子问题。
class Solution {
public:
    
    bool done(vector> & nums){
        for(int i = 0;i < 26; ++i) if(!nums[i].empty()) return false;
        return true;
    }
    bool checkFirstPos(vector> &nums, int pos){
        for(int i = 0;i < 26; ++i){
            if(nums[i].empty()) continue;
            if(nums[i][0] < nums[pos].back()) return false;
        }
        return true;
    }
    void remove(vector> &nums,int pos){
        int tmp = nums[pos].back();
        for(int i = 0;i < 26; ++i){
            if(nums[i].empty()) continue;
            while(nums[i].back() < tmp) nums[i].pop_back();
        }
        nums[pos].clear();
    }
    string removeDuplicateLetters(string s) {
        vector> nums(26);
        
        for(int i = 0;i < s.length(); ++i){
            nums[s[i] - 'a'].push_back(i);
        }
        
        for(int i = 0;i < 26; ++i) reverse(nums[i].begin(),nums[i].end());    
        int iter = 0;
        string ans = "";
        while(true){
            if(done(nums)) break;
            for(int i = 0;i < 26; ++i){
                if(nums[i].empty()) continue;
                if(checkFirstPos(nums,i)){
                    ans += 'a' + i;
                    remove(nums,i);
                    break;
                }
            }
        }
            
        return ans;
    }
};

321. Create Maximum Number

 

题意:给定两个数组,和一个数k,从这两个数组(每个数组一个数)中选取k个数,组成一个最大的数。其中从各个数组选取的数保持在原数组中的次序

题解:枚举数据1选取的位数和数组2选取的位数,各自产生最大的子序列。然后合并。不过过程我写复杂了。在产生子序列的部分,我采用优先队列贪心选取,实际上并不用,而且在合并部分,情况比较复杂。

第一份是我的代码,写的比较挫,也比较耗时。所以我找了一份更好的代码,修改了一些小地方,加上注释,放在下面。就是第二份代码。会相对比较难理解,但是效率更高。

 

class Solution {
    
    vector gen_max_subs(vector &nums, int k){
        if(k == 0) return vector();
        vector n;
        priority_queue > Q;
        
        for(int i = 0;i < nums.size() - k + 1; ++i){
            Q.push(make_pair(nums[i], -i));
        }
        
        int last_pos = -1;
        while(k--){
            pair u = Q.top(); Q.pop();
            
            if(-u.second > last_pos){ 
                n.push_back(u.first);
                last_pos = -u.second;
            }
            while(!Q.empty() && -Q.top().second <= last_pos) Q.pop();

            if(k > 0){
                Q.push(make_pair(nums[nums.size() - k], k - int(nums.size())) );
            }
        }
        return n;
    }
    
    bool check_max(vector &n1, vector &n2, int pos1 = 0, int pos2 = 0){
        
        bool t = n1.size() - pos1 > n2.size() - pos2;
        for(int i = 0;pos1 + i < n1.size() && pos2 + i < n2.size(); ++i){
            
            if(n1[pos1 + i] > n2[pos2 + i]) return true;
            else if(n1[pos1 + i] < n2[pos2 + i]) return false;
        }
        return t; 
    }
    
    vector merge_subs(vector& n1, vector& n2){
        vector n;
        int i = 0, j = 0;
        while(i < n1.size() or j < n2.size()){
            if(i < n1.size() && j < n2.size() && n1[i] == n2[j]){
                if(check_max(n1, n2, i, j)){
                    n.push_back(n1[i++]);
                }else{
                    n.push_back(n2[j++]);
                }
                continue;
            }
            
            if(j < n2.size() && (i == n1.size() || n1[i] < n2[j])){
                n.push_back(n2[j++]);
            }else{
                n.push_back(n1[i++]);
            }
        }
        return n;
        
    }
    
public:
    vector maxNumber(vector& nums1, vector& nums2, int k) {   
        vector ans;
        
        //i表示从nums1中取i个数
        for(int i = max(0, k - int(nums2.size())); i <= min(k, int(nums1.size())); ++i){

            vector n1 = gen_max_subs(nums1, i);
            vector n2 = gen_max_subs(nums2, k - i);
            vector n = merge_subs(n1, n2);

            if(ans.size() == 0 or check_max(n, ans)){
                ans.swap(n);
            }
        }
        cout<
class Solution {
    void combine(std::vector &res, std::vector &v1, std::vector &v2)
    {
        int len1 = v1.size();
        int len2 = v2.size();
        int len = res.size();
        int left = 0;
        int right = 0;
        for (int i = 0; i < len;) {
            //第二个已经取光或者两个都还有且第一个比第二个当前位置大,push第一个
            if (right == len2 || left != len1 && v1[left] > v2[right]) {
                res[i++] = v1[left++];
            //反过来,即第一个为空,或者两个不为空,且第二个比第一个大,push第二个
            } else if (left == len1 || v2[right] > v1[left]) {
                res[i++] = v2[right++];
            //两个相等,相等的情况比较复杂,需要进一步判断后面的数,直到能确定哪个位置开始选择另一个数组。后面的数如果相等,虽然我们当前不能判断选哪个数组,但是选哪些数我们是直到的
            } else {
                //保留原先的left和right
                int ori_left = left;
                int ori_right = right;
                int tar = left;
                int last_greater = right;
                res[i++] = v1[left];
                
                //从当前位置一直比较,一直到两个不相等,或者有一个结束,或者                                
                while (true) { 
                    ++left;
                    ++right;
                    //如果第一个到尽头,或者两个都还有,且第一个小
                    if (left == len1 || right != len2 && v1[left] < v2[right]) {
                        left = ori_left;
                        break;
                    //如果第二个到尽头,或者两个还有,且第二个小
                    } else if (right == len2 || v1[left] > v2[right]) {
                        right = ori_right;
                        break;
                    }
                    //两个是数组对应位置两个值相等
                    
                    //如果是在上面跳出循环的,说明是两个数组比较出大小了,情况比较简单,哪个大哪个先取
                    
                    //如果当前还不能判断出大小
                    
                    //当后面的数比第一个数大时,就一直进行下去,因为一定是先取一方取完再取另一个数组(数已经确定,只是先取哪个还没确定)
                    //如果出现相同的需要再进一步判断,比如两个数组
                    //6 8 7 6 * * *
                    //6 8 7 6 * * *
                    //到第二个6这里,如果下个位置比8小,则到此为止(不取这个6,取上面的6比较好),必须取另外一个数组了
                    //6 8 7 6 7 * *
                    //6 8 7 6 7 * *
                    //如果大于8,则继续进行,取同一方回归原来的状态
                    //6 8 7 6 9 * *
                    //6 8 7 6 9 * *
                    //如果等于8,则需要继续判断下一位,直到能判断是继续取同一方还是,另外一个数组,如果是同一方,则继续
                    //所以var就是保存比较到的下标
                    //last_greater就是保存跳转的位置。当到某个位置的时候,满足下面第一个条件,就是先取数组1的一段,再取2的这一段,然后继续后面的
                    //比如
                    //6 8 7 6 5 *
                    //6 8 7 6 5 *
                    //这里5比6小,所以肯定是先取6 8 7  6 然后取另一段6 8 7 6,所以我们默认先取第二段,令right = last_greater
                    
                    
                    if (v1[left] < v1[tar]) {
                        left = tar;
                        right = last_greater + 1;
                        break;
                    } else if (v1[left] > v1[tar]) {
                        last_greater = right;
                        tar = ori_left;
                    //相等
                    } else {
                        ++tar;
                    }
                    res[i++] = v1[left];
                }
            }
        }
    }
    void max_num(std::vector &res, std::vector &nums, int k)
    {
        res.clear();
        int len = nums.size();
        int diff = len - k;
        //这里不是用堆贪心。而是直接用栈贪心就可以了。这和堆贪心本质是一样的。第一个数,一定是0到len - k + 1中最大的数,而第二个数一定是下标比第二个大且小于len - k + 2以此类推。所以当前值如果比栈尾大,且满足下面的条件说明属于它的位置在前面。
        //k - res_size > len - i and res_size > 0 变成下面res_size > max(i - diff, 0)
        //就是剩余的数和当前已经选取的要大于k,才能pop掉末尾
        for (int i = 0; i < len; ++i) {
            while (res.size() > std::max(i - diff, 0) && nums[i] > res.back()) {
                res.pop_back();
            }
            if (res.size() < k) {
                res.push_back(nums[i]);
            }
        }
    }
public:
    vector maxNumber(vector& nums1, vector& nums2, int k) {
        std::vector res(k);
        std::vector cur(k);
        std::vector temp1;
        std::vector temp2;

        int len1 = nums1.size();
        int len2 = nums2.size();
        int max_len = std::min(len1, k);
        int min_len = std::max(k - len2, 0);
        //枚举位数
        for (int i = min_len; i <= max_len; ++i) {
            max_num(temp1, nums1, i);
            max_num(temp2, nums2, k - i);
            combine(cur, temp1, temp2);
            //vector原来可以直接比较
            if (cur > res) {
                res.swap(cur);
            }
        }
        return res;
    }
};

327. Count of Range Sum

题意:给定一个数组,还有一个区间[lower,upper],问这个数组中,有多少个子数组(连续的),他们的和在这个区间中。
题解:设原数组为a[1,...,n],我们求前缀和s[0,,...,n],s[0] = 0,s[i] = sum a[1,...i],那么问题变成,s中有多少个对,i>j使得s[i] - s[j]在这个区间中。进一步,我么可以先算出所有可能的对数,然后排除掉那些不在这个区间中的。然后问题就和493题差不多了。解法一样。只不过同样要注意溢出问题。
class Solution {
public:
     int getans(int low,int up,int left, int right, vector& nums){
        if(right - left <= 1) return 0;
        int mid = (right + left) / 2;
        int ans = 0;
        ans = getans(low,up,left, mid, nums);
        ans += getans(low,up,mid, right, nums);
        
        int ind1 = mid, ind2 = right - 1;
        int tmp1 = 0,tmp2 = 0;
        for(int i = left; i < mid; ++i){
            //cout<<"i= "<  nums[ind1]){
               // cout<<"i= "<= left; --i){
            //cout<<"inin"<= mid && nums[i] + up <  nums[ind2]){
                    tmp2 += (i - left + 1);
                    --ind2;
            }
           // cou
           //if(c++ <= 1000 &&c%100 == 0) cout< tmp;
        for(int i = left; i < right; ++i){
            if(indL >= mid) tmp.push_back(nums[indR++]);
            else if(indR >= right) tmp.push_back(nums[indL++]);
            else if(nums[indL] < nums[indR]) tmp.push_back(nums[indL++]);
            else tmp.push_back(nums[indR++]);
        }
        for(int i = left; i < right; ++i){
            nums[i] = tmp[i - left];
        }
        return ans;
    }


    int countRangeSum(vector& nums, int lower, int upper) {
        int ans = 0;
        if(nums.size() == 0) return 0;
        vector newNums(nums.size());
        newNums[0] = nums[0];
        for(int i = 1;i < newNums.size() ;++i) newNums[i] = newNums[i - 1] + nums[i];
        //for(int i = 0; i < newNums.size(); ++i)cout<= lower && newNums[i] <= upper;
       // cout<

329. Longest Increasing Path in a Matrix

题意:给定一个整数矩阵,求最长上升路径长度。路径只能往上下左右四个方向走。
题解:很容易想到,如果我们从最大的树开始dp,设dp[i][j]为以i,j为起始点的最长路长度。
那么最大的dp[i][j]即可。所以我们用bfs就可以保证从最大那个(局部最大)数开始了。
另外一种是dfs,设dp[i][j]为以i,j为起始点的最长路长度。如果周围有比它大的,那么先计算它的dp值。然后记忆搜索即可。如下是dsf版本,不难改成bfs版本。

class Solution {
public:
    
    vector > dp;
    int n,m;
    vector> matrix;
        
    int dfs(int x,int y){
        if(dp[x][y]) return dp[x][y];
        
        vector> directions = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
        for (auto &dir : directions) {
            int newx = x + dir[0], newy = y + dir[1];
            if (newx < 0 || newx >= n || newy < 0 || newy >= m) continue;
            if (matrix[newx][newy] <= matrix[x][y]) continue;
            dp[x][y] = max(dp[x][y], dfs(newx, newy));
        }
        return ++dp[x][y];    
    }
    
    int longestIncreasingPath(vector>& mat) {
        matrix = mat;
        if(matrix .size() == 0 || matrix[0].size() == 0) return 0;
        n = matrix.size(); m = matrix[0].size();
        dp = vector >(n,vector(m,0));
        
        int ans = 0;
        for(int i = 0;i < n; ++i){
            for(int j = 0;j < m; ++j){
                ans = max(ans,dfs(i,j));
            }
        }
        return ans;
    }
};

330. Patching Array

题意:给定一个序列,和一个数n,问需要至少补充多少个数才能使得1到n可表示成这个序列中一些元素的和
题解:贪心。每次从小到大,遇到不能表示的就加入肯定是最优的。
class Solution {
public:
    int minPatches(vector& nums, int n) {
        int cnt=0,i=0;
        long long maxNum=0;
        while (maxNum < n){
           if (i < nums.size() && nums[i] <= maxNum + 1)
                maxNum += nums[i++];
           else{
                maxNum += maxNum + 1; ++cnt;
           }
       }
       return cnt;
    }
};

332. Reconstruct Itinerary

题意:给定图的边(以字符串的形式),求里面以"JFK"开始,且通过所有边的字典序最小的路径(题目保证肯定存在),即最小字典序欧拉路径
题解:首先建图,然后,从"JFK"开始,DFS,贪心,按字典序小的边先走。如果走不通,则到达终点(因为只有终点入度比出度大一)。而进来的边,就是最终到达的边。因为如果一个点,存在一个出边没有遍历,要么点已经访问过,那么通过这条边,肯定会再回到这个点,要么这个点没被访问过。如果是第一种情形,那么实际上只是加了一个环,不会影响最后的边。而第二种情形,它肯定被包含在某个环里面。然后,删掉这个边,在剩下的图里继续找这样的点和边,一直找到全部路径上的边和点。
大神的代码就是简洁
class Solution {
public:
	vector findItinerary(vector> tickets) {
		unordered_map> graph;
		vector itinerary;
		if (tickets.size() == 0){
			return itinerary;
		}
		for (pair eachTicket : tickets){
			graph[eachTicket.first].insert(eachTicket.second);
		}
		stack dfs;
		dfs.push("JFK");
		while (!dfs.empty()){
			string topAirport = dfs.top();
			if (graph[topAirport].empty()){
				itinerary.push_back(topAirport);
				dfs.pop();
			}
			else {
				dfs.push(*(graph[topAirport].begin()));
				graph[topAirport].erase(graph[topAirport].begin());
			}
		}
		reverse(itinerary.begin(), itinerary.end());
		return itinerary;
	}
};

335. Self Crossing

题意:给定一个正整数数组x,表示的是从原点开始,先向北走x[0],然后向西走x[1],然后向南走x[2],然后向东走x[3],以此类推,每次都逆时针旋转再走。问x路径有没有交叉(有没有相交点)

题解:首先注意到,如果要产生交叉,一定是有一条边x[i]不比x[i - 2]长,因为如果都有x[i] > x[i - 2]那么肯定是螺旋式向外走圈。

我们只需要考虑最近6个点就可以了,因为如果当前节点产生交点,那么最近6个点(5条线段)肯定有交叉。

现在我们就只考虑5个或者6个点的情形(4个点不可能相交)。

5个点的情况非常简单就是 x[i - 3] == x[i - 1] && x[i - 4] + x[i] >= x[i - 2]。

6个点会有几种情况,

第一种,最后一条边穿过x[i - 3],这时候有x[i] >= x[i - 2]

第二种,边重合,这时候有x[i - 2] > x[i - 4] && x[i - 1] <= x[i - 3] && x[i - 5] + x[i - 1] >= x[i - 3] && x[i] + x[i - 4] >= x[i - 2]

class Solution {
public:
    bool isSelfCrossing(vector& x) {
        
        if(x.size() < 4) return false;
        bool contraction = false;
        
        for(int i = 2;i < x.size(); ++i){
            if(contraction){
                if((x[i] >= x[i - 2]) || 
                   (i >= 4 && x[i - 3] == x[i - 1] && x[i - 4] + x[i] >= x[i - 2]) || 
                   (i >= 5 && x[i - 2] > x[i - 4] && x[i - 1] <= x[i - 3] && x[i - 5] + x[i - 1] >= x[i - 3] && x[i] + x[i - 4] >= x[i - 2]))                        return true;
                   
                continue;
            }
            if(x[i] <= x[i - 2]) contraction = true;
        }
        
        return false;
    }
};

336. Palindrome Pairs

题意:给定一些串,求出所有连接能构成回文串的对。
题解:用map或trie保存翻转后的结果。然后对每个串,分别枚举前翻转和后翻转的位置,查找串即可。注意空串的存在。懒得写了。
class Solution {
    public:
        vector> palindromePairs(vector& words) {
            unordered_map dict;
            vector> ans;
            
            for(int i = 0; i < words.size(); i++) {
                string key = words[i];
                reverse(key.begin(), key.end());
                dict[key] = i;
            }
            if(dict.find("")!=dict.end()){
                for(int i = 0; i < words.size(); i++){
                    if(i == dict[""]) continue;
                    if(isPalindrome(words[i])) ans.push_back({dict[""], i}); 
                }
            }

            for(int i = 0; i < words.size(); ++i) {
                for(int j = 0; j < words[i].size(); ++j) {
                    string left = words[i].substr(0, j);
                    string right = words[i].substr(j, words[i].size() - j);

                    if(dict.find(left) != dict.end() && isPalindrome(right) && dict[left] != i) {
                        ans.push_back({i, dict[left]});     
                    }

                    if(dict.find(right) != dict.end() && isPalindrome(left) && dict[right] != i) {
                        ans.push_back({dict[right], i});
                    }
                }
            }

            return ans;        
        }

        bool isPalindrome(string str){
            int i = 0;
            int j = str.size() - 1; 

            while(i < j) {
                if(str[i++] != str[j--]) return false;
            }

            return true;
        }

    };

 352. Data Stream as Disjoint Intervals

题意:给定一个整数数组。将相邻的合并为区间。并且支持插入和查询操作。
题解:插入时,二分查找插入位置,然后合并即可。
/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class SummaryRanges {
    private:
    vector intervals;
public:
    
    SummaryRanges() {}
    
    static bool cmp(Interval &a,Interval &b){
        return a.end + 1 < b.start;
    }
    struct comp{  
        bool operator()(const Interval &a,const Interval &b){ 
            return a.end + 1 < b.start;
        }
    };
    
    void addNum(int val) {
        Interval cur(val, val);
        vector res;
        
        vector::iterator begin = lower_bound(intervals.begin(),intervals.end(),cur,comp());
        vector::iterator end = begin;
        
        while(end != intervals.end()){
            Interval tmp = *end;
            if(cur.end + 1 >= tmp.start){
                 cur.start = min(cur.start, tmp.start);
                 cur.end = max(cur.end, tmp.end);
                 ++end;
            }else {
                break;
            };
        }
        int pos = begin - intervals.begin();
        intervals.erase(begin,end);
        intervals.insert(intervals.begin() + pos,cur);
    }
    vector getIntervals() {
        return intervals;    
    }
};

/**
 * Your SummaryRanges object will be instantiated and called as such:
 * SummaryRanges obj = new SummaryRanges();
 * obj.addNum(val);
 * vector param_2 = obj.getIntervals();
 */

354. Russian Doll Envelopes

题意:俄罗斯套娃。有一些箱子(长和宽),一个箱子如果长和宽都严格大于另一个,那么可以套住另一个。问最多可以套多少层。
题解:动态规划。按照箱子大小排序(先按宽度从小到大,如果宽度一样,那么长度从大到小)。问题变成了,最长递增子序列。然后就可以用O(nlogn)解法了。
class Solution {
public:
    
    struct cmp{
        bool operator ()(pair&a, pair &b){
            if(a.first == b.first) return a.second > b.second;
            else return a.first < b.first;
         }
    };
    
    struct cmp2{
        bool operator ()(const pair&a, const pair &b){
            if(a.first >= b.first || a.second >= b.second) return false;
            return true; 
         }
    };
    
    int maxEnvelopes(vector>& envelopes) {
        int n = envelopes.size();
        if(n <= 1) return n;
        
        sort(envelopes.begin(),envelopes.end(),cmp());
        
        vector > head(n + 1,make_pair(INT_MAX,INT_MAX));
        head[0] = make_pair(-1,-1);
        int ans = 0;
        for(int i = 0;i < n; ++i){
            int pos = lower_bound(head.begin(),head.end(),envelopes[i],cmp2()) - head.begin();
            head[pos] = envelopes[i];
            ans = max(ans,pos);
        }
        return ans;
    }
};

363. Max Sum of Rectangle No Larger Than K

题意:给定一个数值矩阵,求其中子矩阵最大不超过k的和
题解:先考虑最大子矩阵和问题。
通常有两种动态规划的解法,一种是枚举起始点和矩阵一边的长度,另外一种是枚举起始行和终止行,然后变成一维求解最大子段和。
类似地我们枚举起始行和终止行,然而求的是最大不超过k的子段和,这个可以用二分查找得到。
时间复杂度为O(min(n,m)^2 max(m,n) log(max(n,m)))

class Solution {
public:
    void rotate(vector>& matrix,int &n,int &m){
        int tmp = n;
        n = m;
        m = tmp;
        vector> matrix2(n,vector(m));
        
        for(int i = 0;i < n; ++i)
            for(int j = 0;j < m; ++j) matrix2[i][j] = matrix[j][i];
        matrix = matrix2;
    }
    
    int maxSumSubmatrix(vector>& matrix, int kk) {
        int n = matrix.size();
        if(n == 0) return 0;
        int m = matrix[0].size();
        if(m == 0) return 0;
        int res = INT_MIN;
        
        if(n > 2 * m) rotate(matrix,n,m);
        
        for(int i = 0;i < n; ++i){
            vector sum(m, 0);
            for(int j = i;j < n; ++j){
                for(int k = 0;k < m; ++k){
                    sum[k] += matrix[j][k];
                }
                set S;
                S.insert(0);
                int curSum = 0;
                
                for(int k = 0;k < m; ++k){
                    curSum += sum[k];
                    set::iterator iter = lower_bound(S.begin(),S.end(),curSum - kk);
                    if(iter != S.end()) res = max(res,curSum - *iter);
                    S.insert(curSum);
                }
            }
        }
        return res;
    }
};

381. Insert Delete GetRandom O(1) - Duplicates allowed

题意:设计一个可以O(1)插入,删除和抽取随机样本的算法(数据结构)。
题解:用Hashmap和vector来做。map将值映射到val的下标(可能有多个,所以是一个vector),vector保存值和在m中val对应vector的位置。
这样。每次插入,只要更新map值对应的vector,而删除,把vector最后位置调换到删除的地方,然后更新对应map。
class RandomizedCollection {
public:
    /** Initialize your data structure here. */
    RandomizedCollection() {
        
    }
    
    /** Inserts a value to the collection. Returns true if the collection did not already contain the specified element. */
    bool insert(int val) {
        auto result = m.find(val) == m.end();
        
        m[val].push_back(nums.size());
        nums.push_back(pair(val, m[val].size() - 1));
        
        return result;
    }
    
    /** Removes a value from the collection. Returns true if the collection contained the specified element. */
    bool remove(int val) {
        auto result = m.find(val) != m.end();
        if(result)
        {
            auto last = nums.back();
            m[last.first][last.second] = m[val].back();
            nums[m[val].back()] = last;
            m[val].pop_back();
            if(m[val].empty()) m.erase(val);
            nums.pop_back();
        }
        return result;
    }
    
    /** Get a random element from the collection. */
    int getRandom() {
        return nums[rand() % nums.size()].first;
    }
private:
    vector> nums;
    unordered_map> m;
};

391. Perfect Rectangle

题意:给定一些长方形(以左下角,右上角形式给出),问它们是否不重叠地拼接成一个长方形。
题解:只要保证,面积一样。且除了四个顶点,其他点出现偶数次即可。

class Solution {
public:
    bool isRectangleCover(vector>& rectangles) {
        set > S;
        if(rectangles.size() <= 1) return true;
        
        int xl,yl,xr,yr;
        
        xl = rectangles[0][0];
        yl = rectangles[0][1];
        xr = rectangles[0][2];
        yr = rectangles[0][3];
        long long area = (long long)(xr - xl) * (yr - yl);
        
        S.insert(make_pair(xl,yl));
        S.insert(make_pair(xl,yr));
        S.insert(make_pair(xr,yl));
        S.insert(make_pair(xr,yr));
        
        for(int i = 1;i < rectangles.size(); ++i){
            
            int xlt = rectangles[i][0];
            int ylt = rectangles[i][1];
            int xrt = rectangles[i][2];
            int yrt = rectangles[i][3];
            
            xl = min(xl,xlt);
            yl = min(yl,ylt);
            xr = max(xr,xrt);
            yr = max(yr,yrt);
            
            pair p1(xlt,ylt),p2(xlt,yrt),p3(xrt,ylt),p4(xrt,yrt);
            
            if(!S.insert(p1).second) S.erase(p1);
            if(!S.insert(p2).second) S.erase(p2);
            if(!S.insert(p3).second) S.erase(p3);
            if(!S.insert(p4).second) S.erase(p4);
                
            area += (xrt - xlt) * (yrt - ylt);
            cout<<(xrt - xlt) * (yrt - ylt)< p1(xl,yl),p2(xl,yr),p3(xr,yl),p4(xr,yr);
        
        if(S.find(p1) == S.end() || S.find(p2) == S.end() || S.find(p3) == S.end() || 
                S.find(p4) == S.end() || S.size() > 4 || area != (long long)(yr - yl) * (xr - xl)) 
            return false;
        
        return true;
    }
};

403. Frog Jump

题意:有一些石头的位置以一个数组给出。一只青蛙从0位置开始跳,每次只可以跳之前跳跨越步数的-1,不变或+1的步数。问能否到达终点。
题解:可以用dfs。这里用map和vector记录每个点到达的步数。
class Solution {
public:
    bool canCross(vector& stones) {
        if(stones.size() <= 1) return true;
        if(stones[1] != stones[0] + 1) return false;
        if(stones.size() == 2) return stones[1] == stones[0] + 1;
        int n = stones.size();
        map m;
        
        for(int i = 0;i < n; ++i){
            m[stones[i]] = i;
        }
        
        
        vector > dp(n);
        dp[0].push_back(0);
        vector jumpStep = {-1,0,1};       
 
        for(int i = 0;i < n; ++i){
            for(int j = 0;j < dp[i].size(); ++j){
                int step = dp[i][j];
                int pos;
                
                for(int jump : jumpStep){
                    if(step + jump <= 0) continue;
                    pos = m[stones[i] + step + jump];
                    if(find(dp[pos].begin(),dp[pos].end(),step + jump) == dp[pos].end()) 
                            dp[pos].push_back(step + jump);
                }
                
            }
            
        }
        return dp[n - 1].size() > 0;    
    }
};

407. Trapping Rain Water II

题意:和42一样,只不过改成二维的。
题解:对于一个点,我们要知道它能trap住多少水,只要知道它到外部的最低高度。所以从最外圈开始,每次取一个最低点,向着4面走,寻求每个点到达外部的最低点。我们用优先队列,先把外围加进队列,每次取最低那个,往里更新即可。还可以用最短路算法来求,我们定义两个点i到j的边权,为j的高度,再加一个外部点,高度为0,所有外围点连接到这个点。定义路径的长度为路径权重最大的边。那么只要求出每个点到达最外点的最短路即可。

class Solution {
public:
    struct point{
        int x,y;
        point(){}
        point(int a,int b):x(a),y(b){}
    };
    
    struct cmp{
        bool operator()(const pair & a,const pair &b) {
            return a.second > b.second;
        }
    };
    
    int trapRainWater(vector>& heightMap) {
        int n = heightMap.size();
        if(n == 0) return 0;
        int m = heightMap[0].size();
        
        priority_queue,vector >, cmp> Q;
        vector >vis(n,vector(m,false));
        
        for(int j = 0;j < m; ++j) {
            Q.push(make_pair(point(0,j),heightMap[0][j]));
            Q.push(make_pair(point(n - 1,j),heightMap[n - 1][j]));
            vis[0][j] = vis[n - 1][j] = true;
        }
        
        for(int i = 1;i < n - 1; ++i){
            Q.push(make_pair(point(i,0),heightMap[i][0]));
            Q.push(make_pair(point(i,m - 1),heightMap[i][m - 1]));
            vis[i][0] = vis[i][m - 1] = true;
        }
        
        int res = 0;
        
        while(!Q.empty()){
            pair u = Q.top();
            Q.pop();
            point p = u.first;
            int height = u.second;
            vector > dirs = {{1,0},{-1,0},{0,1},{0,-1}};
            for(auto &dir : dirs){
                int x = p.x + dir[0], y = p.y + dir[1];
                if(x < 0 || x >= n || y < 0 || y >= m || vis[x][y]) continue;
                res += max(0,height - heightMap[x][y]);
                Q.push(make_pair(point(x,y),max(height,heightMap[x][y])));
                vis[x][y] = true;
            }
        }
        return res;
    }
};

410. Split Array Largest Sum

题意:给定一个非负数组,和一个数m,问把这个数组分成最多m个连续块,对每一块求和。求最大和的最小可能。
题解:动态规划可以解。二分答案可以解。二分的思路是,二分最大和,然后check能否分,用贪心进行check即可。
class Solution {
public:
    int splitArray(vector& nums, int m) {
        int n = nums.size();
        if(n == 0) return 0;
        if(n == 1) return nums[0];
        int tot = 0,maxs = 0;
        
        
        for(int i = 0;i < n; ++i) tot += nums[i],maxs = max(maxs,nums[i]);
        if(m >= n) return maxs;
        long long  left = maxs, right = tot,mid,ans;
       
        
        while(left <= right){
            mid = (right - left) / 2 + left;
            if(check(nums,mid,m)){
                ans = mid;
                right = mid - 1;
            }else left = mid + 1;
        }
        return ans;
    }
    
    int check(vector& nums,long long sum,int m){
        int count = 1;
        long long cur = 0;
        for(int i = 0;i < nums.size(); ++i){
            if(cur + nums[i] > sum){
                cur = 0;
                ++count;
            }
            cur += nums[i];
            if(count > m) return false;
        }
        return true;
    }
    
};

420. Strong Password Checker

题意:给定一个字符串,问至少通过多少次操作能使得它满足以下条件:1,必须有数字,大小写字母。2.不能出现连续三个一样的字符。3.长度在6到20之间。操作有,插入,删除,修改三种。
题解:首先对于小于6的情况,只要使得没有缺失三种字符和长度到达6就可以自然解决问题2,所以没有难度。对于小于等于20的情况,由于不用删除(替换总比删除有效),所以也没有难度。对于大于20的情况就比较麻烦。首先,肯定用不到插入操作,因为替换总比插入有效。所以只有替换和删除两种操作。所以删除的总数我们就确定了,为s.len - 20。显然,我们必须优先删除连续三个或以上的那些字符,另外如果能。假设连续的字符长度为len > 2。
如果len % 3 == 0, 那么删除一个字符,我们可以少一个替换,然后变成len % 3 == 2
如果len % 3 == 1,那么删除两个字符,我们可以少一个替换,然后变成len % 3 == 2
上面这两种情况我们要优先考虑,因为其他的len % 3 == 2要删除3个字符才能少一个替换。
按照上面的思路,计算出需要替换的数目就可以了。

class Solution {
public:
    int strongPasswordChecker(string s) {
        int missing_type = 3;
        bool digit = 0;
        bool lower_case = false;
        bool upper_case = false;
        
        for(int i = 0;i < s.length(); ++i){
            if(s[i] >= 'A' && s[i] <= 'Z') upper_case = true;
            else if(s[i] >= 'a' && s[i] <= 'z') lower_case = true;
            else if(s[i] >= '0' && s[i] <= '9') digit = true;
        }
        missing_type = 3 - digit - lower_case - upper_case;
        int replacement = 0, del = 0, one = 0, two = 0;
        for(int i = 2;i < s.length(); ++i){
            if(s[i] == s[i - 1] && s[i] == s[i - 2]){
                int len = 3;
                while(i + 1 < s.length() && s[i + 1] == s[i]) ++i,++len;
                replacement += len / 3;
                one += (len % 3 == 0);
                two += (len % 3 == 1);
            }
        }
        if(s.length() < 6){
            return max(missing_type,(int)(6 - s.length()));
        }else if(s.length() <= 20){
            return max(missing_type,replacement);
        }else{
            del = s.length() - 20;
        }
      
        replacement -= min(del, one);
        replacement -= min(max(del - one, 0), two * 2) / 2;
        replacement -= max(del - one - 2 * two, 0) / 3;
                
        return del+ max(missing_type, replacement);
    }
};

432. All O`one Data Structure

题意:设计一个数据结构支持四种操作,Inc(key)将key的对应value增1,如果不存在则为1。Dec(key)将key的value减一,如果为0,则删除key。GetMaxKey()返回任意一个value最大的key,如果没有,返回空串。GetMinKey()返回value最小的key。所有操作是O(1)的

题解:用一个list存储每个value的那些key,按value从大到小排列,这样GetMaxKey和GetMinKey就可以由列表头和列表尾得到。由于获取和删除key也要一个常数时间,所以存储可以用hashset。而由于列表不是随机访问的,所以对于每个key,我们还需要记录它在list中的哪个位置,记录可以用hashmap记录。

class AllOne {
    unordered_map>>::iterator> key2freq_keys;
    //从大到小
    list>> freq_keys_list;
public:
    /** Initialize your data structure here. */
    AllOne() {
    }
    
    /** Inserts a new key  with value 1. Or increments an existing key by 1. */
    void inc(string key) {
        int freq = 0;
        list>>::iterator data_iter = freq_keys_list.end();
        //key存在
        if(key2freq_keys.count(key)){
            //如果存在,则删除原有的
            data_iter = key2freq_keys[key];
            //删除key2freq_keys中的key,删除freq_keys_list中对应的集合中的key,如果删除以后该节点保存的key为空,删除节点
            //pair> &freq_keys = *data_iter;
            freq = data_iter->first;
            data_iter->second.erase(key);
            if(data_iter->second.empty()){
                freq_keys_list.erase(data_iter++);
            }
            
            key2freq_keys.erase(key);
        }
        //插入
        //说明freq + 1的节点不存在,需要先创建
        if(data_iter == freq_keys_list.begin() || prev(data_iter)->first != freq + 1){
            data_iter = freq_keys_list.insert(data_iter, pair>(freq + 1, unordered_set()));
        }else{
            --data_iter;
        }
        
        data_iter->second.insert(key);
        key2freq_keys[key] = data_iter;
        
    }
    
    /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */
    void dec(string key) {
        if(!key2freq_keys.count(key)) return;
        list>>::iterator data_iter = key2freq_keys[key];
        int freq = data_iter->first;
        
        //删除原有的
        data_iter->second.erase(key);
        if(data_iter->second.empty()){
            freq_keys_list.erase(data_iter++);
        }else{
            ++data_iter;
        }
        
        key2freq_keys.erase(key);

        if(freq == 1) return;
        //插入
        if(data_iter == freq_keys_list.end() || data_iter->first != freq - 1 ){
            data_iter = freq_keys_list.insert(data_iter, pair>(freq - 1, unordered_set()));
            
        }
        
        data_iter->second.insert(key);
        key2freq_keys[key] = data_iter;
    }
    
    /** Returns one of the keys with maximal value. */
    string getMaxKey() {
        if(freq_keys_list.empty()) return "";
        return *freq_keys_list.front().second.begin();
    }
    
    /** Returns one of the keys with Minimal value. */
    string getMinKey() {
        if(freq_keys_list.empty()) return "";
        return *freq_keys_list.back().second.begin();
    }
};

/**
 * Your AllOne object will be instantiated and called as such:
 * AllOne* obj = new AllOne();
 * obj->inc(key);
 * obj->dec(key);
 * string param_3 = obj->getMaxKey();
 * string param_4 = obj->getMinKey();
 */

440. K-th Smallest in Lexicographical Order

题意:给定一个n,求1到n中字典序排第k的数

题解:给定一个数,如果我们能求其字典序排第几,那么我们逐位枚举即可。那么给定一个数我们怎么求其字典序排第几呢,其实很简单,字典序排在其后的,肯定是前缀比它大,或者前缀一样,后面有0。

我们用get_rank函数来实现这个功能。n_d是一个10的幂次,位数和n一样大。首先将num不断乘以10,一直到位数和n一样多,设为m。那么num的字典序就是这个数m的字典序减去num和n之间的字典序差,而他们排序的差由他们差多少个0决定,所以括号中有--rank。

问题现在转变成,求一个位数和n一样的数的字典序。这个是很容易求的。枚举位数,求位数为t的字典序在这个数前的数。只要前缀小于num前缀那些就是。

class Solution {

    int get_rank(int num, int n, int n_d){
        
        int rank = 0;
        //使num和n同位
        while(num < n_d){
            --rank;
            num *= 10;
        }
        if(num > n) ++rank;
        
        while(num){
            // - (n_d - 1)那些位数不等于当前位数的减掉1到 n_d - 1共n_d - 1个数
            rank += min(num, n) + 1 - n_d;
            n_d /= 10;
            num /= 10;
        }
        
        return rank;
    }    
public:
    int findKthNumber(int n, int k) {
        
        
        int n_digit = 1, d = 1;
        int tmp = n;
        while(tmp /= 10){
            ++n_digit;           
            d *= 10;
        }
        
        int num = 0;
        for(int i = 0;i < n_digit; ++i){
            
            int j = 0;
            if(num == 0) j = 1;
            
            for(; j <= 9; ++j)
                if(get_rank(num * 10 + j, n, d) > k) break;
            
            if(j == 0) break;
            num = num * 10 + j - 1;
        }
        
        return num;
    }
};

446. Arithmetic Slices II - Subsequence

题意:给定一个数组,问其中子序列为等差数列的有多少个(三个数或以上)?
题解:设dp[i][j]表示以A[i]为结尾的且差为j的等差数列的个数(包括2个数的)。那么枚举上一个元素A[k],就可以了。由于差的范围很大,所以用unorder_map存储,保证O(1)的平均存取时间。
class Solution {
public:
    int numberOfArithmeticSlices(vector& A) {
        int n = A.size();
        if(n < 3) return 0;
        vector> dp(A.size());
        int ans = 0;
        
        for(int i = 0;i < n; ++i)
            for(int j = 0;j < i; ++j){
                if((long)A[i] - (long)A[j] > INT_MAX || (long)A[i] - (long)A[j] < INT_MIN) continue;
                int diff = A[i] - A[j];
                dp[i][diff] += 1;
                if(dp[j].find(diff) != dp[j].end()){
                    dp[i][diff] += dp[j][diff];
                    ans += dp[j][diff];
                }
            }
        return ans;
    }
};

466. Count The Repetitions


题意:定义一个串S=[s,n]表示S由n个s拼接。s2能由s1获得表示s1删除某些元素后能够得到数
。现给S1=[s1,n1],S2=[s2,n2],求一个最大的m使得S=[S1,m]能由S2获得。
题解:倍增dp。定义dp[i][k]表示从字符串s1的第i位开始匹配2^k个s2串需要的最短长度。
dp[i][k] = dp[ i, k - 1] + dp[i + dp[i][k - 1]) % len1,k - 1];
class Solution {
    
    string str1,str2;
    int len1,len2,n1,n2;
    
    long long dfs(vector>& dp,int i,int k){
        long long &res = dp[i][k];
        if(res) return res;
        return res = dfs(dp, i, k - 1) + dfs(dp,(i + dp[i][k - 1]) % len1,k - 1); 
    }
    void init(vector>& dp){
         for(int i = 0;i < len1; ++i){
            int l1 = i,l2 = 0;
             while(l2 < len2){
                 while(l1 < n1 * len1 && str1[l1 % len1] != str2[l2]) ++l1;
                 ++l1; ++l2;
            }
             dp[i][0] = l1 - i;
        }
    }
    
public:
    int getMaxRepetitions(string s1, int n1, string s2, int n2) {
        vector> dp(101,vector(31,0));
        
        str1 = s1;
        str2 = s2;
        len1 = s1.length(), len2 = s2.length();
        this->n1 = n1;
        this->n2 = n2;
        
        init(dp);
        dfs(dp,0,30);
        long long ans=0;
        long long begin = 0;
        
        for(int k = 29; k >= 0; --k)
             if((begin + dp[(begin % len1)][k]) <= n1 * len1){
                 ans += (1 << k);
                 begin += dp[(begin % len1)][k];
            }
        return ans / n2;
    }
};

leetcode中有人贴出答案如下,更加优雅。稍微解释下:
rest[i]表示i个s1最多能匹配到的s2的下一个未匹配位置。
rapport[i]表示i个s1能匹配多少个s2
b是产生第一个循环的开始,使用s1的个数
last是下个循环开始,使用s1的个数
interval是一个循环需要的s1个数
idea是找出匹配过程中产生的循环节。因为s2至多用s2.length()个s1就能完全匹配,所以s2.length() + 1个s1肯定能产生循环节。
class Solution {
public:
    int getMaxRepetitions(string s1, int n1, string s2, int n2) {
        vector rapport(102,-1);
        vector rest(102,-1);
        int b=-1;int posRest=0;int rap=0;
        int last=-1;
        rapport[0]=rest[0]=0;//case when n=0
        for(int i=1;i<=s2.size()+1;i++){
            int j;
            for(j=0;j=0)break;
        }
        int interval=last-b;
        if(b>=n1)return rapport[n1]/n2;
        return ((n1-b)/interval*(rapport[last]-rapport[b])+rapport[(n1-b)%interval+b])/n2;
    }
};

458. Poor Pigs

题意:假设有 n 只水桶,猪饮水中毒后会在 m 分钟内死亡,你需要多少猪(x)就能在 p 分钟内找出 “有毒” 水桶?这 n 只水桶里有且仅有一只有毒的桶。

题解:信息论解法,

一共有n个桶,哪个桶有有毒药,包含log(n)的信息量。每一只猪可以测试的次数是 floor(minutesToTest / minutesToDie)。

每只猪有t+1种状态,在某次死亡或存活到最后带信息量最多为log(t+1)。假设需要num只猪,则有num*log(t+1)>=log(n)
所以答案就是ceil(log(buckets) / log(minutesToTest / minutesToDie + 1))

class Solution {
public:
    int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        return ceil(log(buckets) / log(minutesToTest / minutesToDie + 1));
    }
};

460. LFU Cache

题意:设计一个数据结构,满足以下的需求( Least Frequently Used (LFU) )。容量为capacity。get(key)返回对应value(不存在返回-1)。put(key,value)插入key,value对,如果容量已满,删除最近最少使用次数的key和对应的数据,如果多个次数一样多,删除最后使用时间最早的那个。

题解:可以对不同频次使用不同的list存储数据,每个list维护一次先后顺序。由于频次次数可能很大,不能预先用数组存,所以可以用一个hashmap来存。hashmap将频次映射到一个list。因为还需要对key索引到val中取,所以还需要一个hashmap保存一下key到哪个list以及list中的哪个指针(我用一个hashmap多层嵌套模板产生了模板嵌套编译错误,暂时改不了,所以我采用了别人的代码,和我的想法基本一致的,它用了两个hashmap,一个用来存频数,一个用来存list中的指针位置)。注意到最小频次如果有新元素插入就是1,没有的话,可以随着每次更新跟着更新即可。

class LFUCache {
    int min_count;
    int capacity;
    //key 的频数
    unordered_map key_to_count;
    //每个频数对应的一个list(key,val)
    unordered_map>*> count_to_queue;
    //key对应的list中的指针位置
    unordered_map>::iterator> cache;
    
    
    //更新key的频次,和对应的list
    //也就是更新key_to_count中的频次
    //更新count_to_queue对应位置(存在则先删除然后在频次+1的位置中插入)
    //更新cache
    void update(int key, int value) { 
      int count;
      list>::iterator data_it;
      //key不存在
      if(cache.find(key) == cache.end()) {
           count = 1;
           min_count = 1;
      }
      else {
          count = key_to_count[key] ;
          auto it = cache[key];
          auto q = count_to_queue[count];
          q->erase(it);
          if(q->empty()) {
             delete q; 
             count_to_queue.erase(count);
             if(count == min_count)
               min_count++;
          }   
          count++;
      }

      list> *q;   
      if(count_to_queue.find(count) == count_to_queue.end()) 
          q = new list>; 
      else 
          q = count_to_queue[count];
      q->push_back({key,value});
      data_it  = --q->end();  
      count_to_queue[count] = q;
      
      key_to_count[key] = count;
      cache[key] = data_it;
   }
   //删除频次最低且访问最早的(最低频次的列表头)
   void evict() {
     auto q = count_to_queue[min_count];
     int victim_key = q->front().first;
     q->pop_front();
     if(q->empty()) {
        delete q; 
        count_to_queue.erase(min_count);
     }    
     cache.erase(victim_key);
     key_to_count.erase(victim_key);
   }

public:
    LFUCache(int cap) {
        capacity = cap;
    }
    
    int get(int key) {
      if(cache.find(key) == cache.end())
          return -1;
      int value = cache[key]->second;
      update(key, value);
      return value;
    }
    
    void put(int key, int value) {
        if(capacity <= 0)
            return;
        if(cache.find(key) == cache.end() && cache.size() == capacity)
           evict();
        update(key,value);
    }
};

 

472. Concatenated Words

题意:给出一些字符串,输出那些可以被其他串组成的串
题解:对每个串做动态规划。设dp[i]表示这个串从0到位置i - 1能不能由其他串组成。
class Solution {
public:
   vector findAllConcatenatedWordsInADict(vector& words) {
        unordered_set s(words.begin(), words.end());
        vector res;
        for (auto w : words) {
            int n = w.size();
            vector dp(n + 1,false);
            dp[0] = true;
            for (int i = 0; i < n; i++) {
                if (!dp[i]) continue;
                for (int j = i + 1; j <= n; j++) {
                    if (j - i < n && s.count(w.substr(i, j - i))) dp[j] = true;
                }
                if (dp[n]) { res.push_back(w); break; }
            }
        }
        return res;
    }
};

474. Ones and Zeroes

题意:给定一系列只有0和1的串(一个string数组),m,n表示有m个0n个1.用这么多个1和0可以最多组成这些串中多少个?每个0或1只能用一次
题解:动态规划dp[i][j][k]表示前i个串用j个0k个1最多组成多少个串,转移方程易得。然后用滚动数组就少了一维的内存。

class Solution {
public:


    int findMaxForm(vector& strs, int m, int n) {
        if(strs.size() == 0) return 0;
        int **dp = new int*[m + 1];
        for(int i = 0;i <= m; ++i){
            dp[i] = new int[n + 1];
           // memset(dp[i], (n + 1) * sizeof(int), 0);
           for(int j = 0;j <= n; ++j) dp[i][j] = 0;

        }

        int *oness = new int[strs.size()];
        int *zeros = new int[strs.size()];
        //memset(oness, strs.size() * sizeof(int), 0);
        //memset(zeros, strs.size() * sizeof(int), 0);
        for(int i = 0; i < strs.size(); ++i){
            oness[i] = zeros[i] = 0;
        }


        for(int i = 0;i < strs.size(); ++i){
            for(int j = 0;j < strs[i].length(); ++j){
                if(strs[i][j] == '0')
                    ++zeros[i];
                else
                    ++oness[i];
            }
        }

        int ans = 0;
        for(int i = 0;i < strs.size(); ++i){
            for(int j = m; j >= 0; --j){
                if(zeros[i] > j) break;
                for(int k = n;k >= 0; --k){
                    if(oness[i] > k) break;
                    dp[j][k] = max(dp[j - zeros[i]][k - oness[i]] + 1,dp[j][k]);
                    ans = max(ans, dp[j][k]);
                }
            }
        }
        for(int i = 0; i <= m; ++i)
            delete [] dp[i];
        delete [] dp;
        delete [] oness;
        delete [] zeros;
        return ans;    
    }
};

479. Largest Palindrome Product

题意:给定一个n。求两个n位数相乘得到的最大回文数 题解:枚举回文数的一半,然后判断是不是能分解成两个n位数相乘。或者直接打表即可


//可以先找到最大的回文数,再检查能否被分解,如果不能就再找仅次于这个回文数的小回文
或者打表
class Solution {
public:
    int largestPalindrome(int n) {
        //打表
        int ans[] = {9, 987, 123, 597, 677, 1218, 877, 475};
        return ans[n - 1];
        
        if(n==1) return 9;
        int upper = pow(10, n) - 1;
        int lower = pow(10, n - 1);
        for (int i = upper; i > lower; --i) {
            long long cand = creatPalindrome(i);
            for (int j = upper; cand / j < j; --j) {
                if (cand % j == 0) return cand % 1337;
            }
        }
        return -1;
    }

private:
    long long creatPalindrome(int n) {
        string lastHalf = to_string(n);
        reverse(lastHalf.begin(), lastHalf.end());
        return stoll(to_string(n) + lastHalf);
    }
};

480. Sliding Window Median

题意:求连续k个数的中值
题解:用multiset记录,因为它删除和插入,原迭代器不会失效,所以只要适当调整中间的迭代器即可。O(nlog(k)
class Solution {
public:
    vector medianSlidingWindow(vector& nums, int k) {
        multiset window(nums.begin(), nums.begin() + k);
        auto mid = next(window.begin(), k / 2);
        vector medians;
        
        for(int i = k;i < nums.size() ; ++i){
            medians.push_back((double(*mid) + *prev(mid, 1 - k % 2)) / 2);
            window.insert(nums[i]);
            if (nums[i] < *mid) --mid;
            if (nums[i - k] <= *mid) ++mid;

            window.erase(window.find(nums[i - k]));
        }
        medians.push_back((double(*mid) + *prev(mid, 1 - k % 2)) / 2);

        return medians;
        
    }
};

483. Smallest Good Base

题意:给一个数,问它能以最小的几进制表示成全是1。
题解:枚举位数,二分进制即可。
class Solution {
public:
    string smallestGoodBase(string n) {
        unsigned long long num = (unsigned long long)stoll(n);
        unsigned long long ans = num - 1,last = num - 1;
        if(num == 1uLL) return string("2");
        
        for(int i = 62;i >= 1; --i){
            if(1uLL << (i - 1) >= num) continue;
            unsigned long long left = 2,right = min(ans,(unsigned long long)(pow(num, 1.0 / i ) + 1));
            while(left <= right){
                unsigned long long mid = (right - left) / 2 + left;
                unsigned long long cur = 1,power = 1;
                for(int j = 0;j < i; ++j){
                    power *= mid;
                    cur += power;
                }
                if(cur == num){
                    ans = min(mid,ans);
                    break;
                }else if(cur < num){
                    left = mid + 1;
                }else{
                    right = mid - 1;
                }
            }
        }
        
        return to_string(ans);
    }
};

493 Reverse Pairs 

题意:给定一个 长度不超过5w的int数组。求其中(i,j)满足i < j 且 a[i] > 2 * a[j]的对的数目
题解:显然这个和逆序对差不多。所以容易想到分治的算法,这个比较容易,无需多说。除了采用分治,像这种结构的问题,还可以用树状树枝或者线段树解决。具体的,先增加n个数,分别是原来数字的两倍,然后对数组加索引标记(原来和加两倍的标记同一个),然后从大到小排序(两个合并一起排序)。然后逐一访问。如果遇到是原数字两倍的,则执行查询操作,否则进行插入操作,见下面代码(树状树枝)。
应该注意的是乘以2的时候溢出的问题。
class Solution {
public:
    int getans(int left, int right, vector& nums){
        if(right - left <= 1) return 0;
        size_t mid = (right + left) / 2;
        int ans = 0;
        ans = getans(left, mid, nums);
        ans += getans(mid, right, nums);
        size_t ind = mid;
        for(size_t i = left; i < mid; ++i){
            while(ind < right && nums[i] > 2LL * nums[ind]){
                ans += (mid - i) ;
                ++ind;
            }
        }
        size_t indL = left,indR = mid;
        vector tmp;
        for(size_t i = left; i < right; ++i){
            if(indL >= mid) tmp.push_back(nums[indR++]);
            else if(indR >= right) tmp.push_back(nums[indL++]);
            else if(nums[indL] < nums[indR]) tmp.push_back(nums[indL++]);
            else tmp.push_back(nums[indR++]);
        }
        for(size_t i = left; i < right; ++i){
            nums[i] = tmp[i - left];
        }
        return ans;
    }
      
    int reversePairs(vector& nums) {
        return getans(0, nums.size(), nums);
    }
};

树状数组的代码
class Solution {

public:
    int *tree;

    struct Node{

        long long value;

        int index;

        bool dflag;

        Node(){};

        Node(long long v, int id, bool f){


            value = v;

            index = id;

            dflag = f;

        }

        bool operator < (const Node &b){

            if(this->value == b.value){
                if(this->index == b.index) return this->dflag;
                return this->index > b.index;

            }
            else return this->value > b.value;
        }

    };

    Node *node;
    Solution(){
        tree = new int[200000];
        node = new Node[200001];
    }

    void init(size_t size){
        memset(tree,sizeof(tree),0);
        //memset(node,sizeof(node),0);
    }
    int lowbit(int x){
        return x&(-x);
    }

    void add(int k, int n)
    {
        while(k <= n)
        {
            tree[k] += 1;
            k += lowbit(k);
        }
    }

    int sums(int k)
    {
        int sum = 0;

        while(k)
        {
            sum += tree[k];
            k -= lowbit(k);
        }
        return sum;

    }

    int reversePairs(vector& nums) {

        if(nums.size() <= 1) return 0;

        init(nums.size());

        int total = 0;
        int ans = 0;
        for(size_t i = 0; i < nums.size(); ++i){
            ans -= nums[i] < 0;        
            node[total++] = Node(nums[i], i + 1, 0);
            node[total++] = Node(2LL * nums[i], i + 1, 1);

        }
        sort(node,node + total);


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

            if(node[i].dflag) ans += sums(node[i].index);

            else add(node[i].index,total);

        }
        return ans;
    }
};

 

你可能感兴趣的:(LeetCode)