Leetcode Hard

Leetcode Hard Problems

23. Merge k Sorted Lists

将多个有序的链表合并成一个有序的链表。

用到的数据结构:

优先队列、小根堆

使用优先队列,对链表进行排序,使得在队首的链表的第一个结点的值最小。

这样,每次都直接从优先队列中获取第一个链表的第一个结点,再将剩下的链表放回优先队列中待用。这样每次都保证取出来的结点是最小的结点。

class Solution
{
  public:
    struct compare
    {
        bool operator()(const ListNode *l, const ListNode *r)
        {
            return l->val > r->val;
        }
    };
    ListNode *mergeKLists(vector<ListNode *> &lists)
    { //priority_queue
        priority_queue<ListNode *, vector<ListNode *>, compare> q;
        for (auto l : lists)
        {
            if (l)
                q.push(l);
        }
        if (q.empty())
            return NULL;

        ListNode *result = q.top();
        q.pop();
        if (result->next)
            q.push(result->next);
        ListNode *tail = result;
        while (!q.empty())
        {
            tail->next = q.top();
            q.pop();
            tail = tail->next;
            if (tail->next)
                q.push(tail->next);
        }
        return result;
    }
};

32. Longest Valid Parentheses

求最长合法匹配的括号子串。

37.Sudoku Solver

解数独。回溯算法,遍历整个数独,找到空白位置'.'

尝试将1-9分别填入该空中,判断填入后数独是否合法,如果合法就继续往下搜索,如果不合法,就继续尝试。如果1-9均不能满足,说明该层搜索不存在解,将其还原成'.',往上回溯。

class Solution
{
  public:
    bool isValid(vector<vector<char>> &board, int row, int col, char c)
    {
        for (int i = 0; i < 9; i++)
        {
            if (board[i][col] == c)
                return false; //check row
            if (board[row][i] == c)
                return false; //check column
            if (board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c)
                return false; //check 3*3 block
        }
        return true;
    }

    void solveSudoku(vector<vector<char>> &board)
    {
        if (board.size() == 0 || board[0].size() == 0)
            return;
        solve(board);
    }

    bool solve(vector<vector<char>> &board)
    {

        for (int i = 0; i < board.size(); i++)
        {
            for (int j = 0; j < board[0].size(); j++)
            {
                if (board[i][j] == '.')
                {
                    for (char ch = '1'; ch <= '9'; ch++)
                    {

                        if (isValid(board, i, j, ch))
                        {
                            board[i][j] = ch;
                            if (solve(board))
                                return true;
                            else
                                board[i][j] = '.';
                        }
                    }

                    return false;
                }
            }
        }
        return true;
    }
};

41.First Missing Positive

求出没有出现的最小正整数。

通过将对应的数交换到对应位置来判断。比如1应该被交换到nums[0],2为nums[1],这样比较nums[i]和i+1的关系就能得出是缺少哪一个数了。

对每一个处于[1,n]之间的数,都是需要做换位操作的。对其进行换位知道找到对应的位置,使得nums[i]==nums[nums[i]-1]。不在这个区间内的数可以忽略不管,没有影响。

class Solution
{
  public:
    int firstMissingPositive(vector<int> &nums)
    {

        for (int i = 0; i < nums.size(); i++)
        {
            while (nums[i] >= 1 && nums[i] <= nums.size() &&
             nums[i] != nums[nums[i] - 1])
                swap(nums[i], nums[nums[i] - 1]);
        }

        for (int i = 0; i < nums.size(); i++)
        {
            if (nums[i] != (i + 1))
                return i + 1;
        }

        return nums.size() + 1;
    }
};

42.Trapping Rain Water

数组给出一组高度,求去由此构成的形状能蓄多少水。如图所示。

对每一个位置分别计算。对于一个位置来说,其能蓄多少水取决于其左边最大值leftmax和右边最大值rightmax,值为min(leftmax,rightmax)-height。

用两个指针分别指向左右两侧,如果左边的值较小,那么此时min(leftmax,rightmax)必定是取决于左边,所以只需要考虑左边部分。比较当前指针指向的值height[left]和Leftmax的大小,如果leftmax较小,就更新其值,如果leftmax较大,则可以容纳leftmax-height[left]。对于右侧同理。

class Solution
{
  public:
    int trap(vector<int> &height)
    {

        int left = 0, right = height.size() - 1;
        int leftmax = 0, rightmax = 0;
        int ret = 0;
        while (left <= right)
        {
            if (height[left] < height[right])
            {
                if (height[left] > leftmax)
                    leftmax = height[left];
                else
                    ret += leftmax - height[left];
                left++;
            }
            else
            {
                if (height[right] > rightmax)
                    rightmax = height[right];
                else
                    ret += rightmax - height[right];
                right--;
            }
        }
        return ret;
    }
};

44.Wildcard Matching

类似正则表达式的字符串匹配。动态规划。dp[i][j]表示s的前i个字符和p前j个字符是否匹配。注意dp[i][j]和s[i-1][s[j-1]对应。

首先dp[0][0]=true,因为两个空串是匹配的。然后,给dp[0][j]赋值,当p[j-1]为'*'才能和空串匹配。而对于dp[i][0],s非空而p为空,必定不匹配。

处理了为0的下标后,不用考虑i-1、j-1越界的情况,方便编写代码。

如果p[j-1]是'*',那么可以匹配s的最后一个字符,所以dp[i][j] = dp[i-1][j]。或者*匹配空串,dp[i][j] = dp[i][j-1]。二者满足一个即可,所以用或运算。

如果p[j-1]是'?',则可以和s[i-1]匹配。或者s[i-1]==p[j-1],直接匹配。此时dp[i][j] = dp[i-1][j-1]

其他情况均不匹配,dp[i][j]保持false,不需要赋值。

class Solution
{
  public:
    bool isMatch(string s, string p)
    {

        vector<vector<int>> dp(s.length() + 1, vector<int>(p.length() + 1, false));
        dp[0][0] = true;

        for (int j = 1; j <= p.length(); j++)
        {
            dp[0][j] = dp[0][j - 1] && p[j - 1] == '*';
        }

        for (int i = 1; i <= s.length(); i++)
        {
            for (int j = 1; j <= p.length(); j++)
            {
                if (p[j - 1] == '*')
                    dp[i][j] = dp[i - 1][j] || dp[i][j - 1];
                else if (p[j - 1] == '?' || p[j - 1] == s[i - 1])
                    dp[i][j] = dp[i - 1][j - 1];
            }
        }
        return dp[s.length()][p.length()];
    }
};

还可以直接利用多指针直接解析,判断能否匹配。

i,j分别表示当前s、p的字符。先判断是否相同,p[j]是否是'?'。然后如果p[j]是'*',就用starindex来记录这个*的下标,用si来记录此时i指针的值,用于回溯。记录后j++,因为'*'可以当作空处理。如果之后不匹配再回退。当两个条件都不满足时,需要回退,重新匹配'*',用starindex找到上一个*的位置,将j仍然赋值为其后一位。并将i也赋值为si(即之前i值)的后一位,这样表示用'*'匹配了s中的第i个字符。

class Solution
{
  public:
    bool isMatch(string s, string p)
    {
        int i = 0, j = 0;
        int si;
        int starindex = -1;
        while (s[i])
        {
            if (s[i] == p[j] || p[j] == '?')
            {
                i++;
                j++;
            }
            else if (p[j] == '*')
            {
                starindex = j;
                j++;
                si = i;
            }
            else if (starindex != -1)
            {
                j = starindex + 1;
                i = ++si;
            }
            else
                return false;
        }

        while (p[j] == '*')
            j++;
        return p[j] == 0;
    }
};

45.Jump Game II

从0开始跳,最少多少步能跳到末尾。nums[i]表示能跳的步数。

BFS。每一层遍历时求出最大能跳到的地方,如果满足就直接返回。

class Solution
{
  public:
    int jump(vector<int> &nums)
    {

        if (nums.size() <= 1)
            return 0;
        int level = 0, currentMax = 0, i = 0, nextMax = 0;

        while (i < nums.size() && (currentMax - i + 1 > 0))
        {
            level++;
            for (; i <= currentMax; i++)
            {
                nextMax = max(nextMax, A[i] + i);
                if (nextMax >= n - 1)
                    return level;
            }
            currentMax = nextMax;
        }
        return 0;
    }
};

第一时间想到的解法是DP..

class Solution
{
  public:
    int jump(vector<int> &nums)
    {

        vector<int> dp(nums.size() + 1, INT_MAX);
        dp[0] = 0;
        for (int i = 1; i < nums.size(); i++)
        {
            for (int j = 0; j < i; j++)
            {
                if (j + nums[j] >= i)
                    dp[i] = min(dp[i], dp[j] + 1);
            }
        }
        return dp[nums.size() - 1];
    }
};

51.N-Queens

N皇后问题。求出所有解。

第一次做的时候,是将整个map全部遍历一次,但实际上因为每一行不能出现相同的皇后,所以每次只要安排第row行的皇后即可。这样只需要遍历n次,并安排合理的col值即可。

此外,在判断两条斜线方向是否有冲突的时候,只需要判断上半部分即可。因为从上往下遍历,下面在没有遍历到的情况下全是'.' 当row==n时表示完成遍历,得到一个解,将其放入ret中返回。

class Solution
{
  public:
    vector<vector<string>> ret;

    vector<vector<string>> solveNQueens(int n)
    {
        string temp(n, '.');
        vector<string> solution(n, temp);
        helper(solution, 0, n);
        return ret;
    }

    void helper(vector<string> &map, int row, int n)
    {
        if (row == n){
            ret.push_back(map);
            return ;
        }
        for (int col = 0; col < n; col++)
        {
            if (IsValid(map, row, col, n))
            {
                map[row][col] = 'Q';
                helper(map, row + 1, n);
                map[row][col] = '.';
            }
        }
    }

    bool IsValid(vector<string> &map, int row, int col, int n)
    {
        for (int i = 0; i <= row; i++)
        {
            if (map[i][col] == 'Q')
                return false;
        }

        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
            if (map[i][j] == 'Q')
                return false;
        for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++)
            if (map[i][j] == 'Q')
                return false;
        return true;
    }
};

57.Insert Interval

合并区间。

class Solution
{
  public:
    vector<Interval> insert(vector<Interval> &intervals, Interval newInterval)
    {
        auto comp = [](Interval &I1, Interval &I2) {
            return I1.start < I2.start;
        };
        intervals.push_back(newInterval);
        sort(intervals.begin(), intervals.end(), comp);
        vector<Interval> ret;
        int pre = 0;
        ret.push_back(intervals[pre]);
        for (int i = 1; i < intervals.size(); i++)
        {

            if (ret[pre].end < intervals[i].start)
            {
                ret.push_back(intervals[i]);
                pre++;
            }
            else if (ret[pre].end < intervals[i].end)
                ret[pre].end = intervals[i].end;
        }

        return ret;
    }
};

72.Edit Distance

字符串编辑距离。word1可以通过三种操作:插入一个字符、删除一个字符、替换一个字符来变成word2。求从word1到word2的最小操作数。

动态规划,用dp[i][j]表示字符串word1[0,i)到word2[0,j)的编辑距离。注意,dp[i][j]对应word1[i-1]和word2[j-1]

如果word1[i-1]==word2[j-1]那么不需要进行修改,仍能保持相同,所以此时dp[i][j] = dp[i-1][j-1]。

如果word1[i-1]!=word2[j-1],此时可能需要三种操作。

1:直接将word2[j-1]替换为word1[i-1],此时dp[i][j] = dp[i-1][j-1]+1

2: word1[0,i-1)能编辑成word2[0,j),那么word1[0,i)只需要删除word1[i-1]即可,dp[i][j] = dp[i-1][j]+1

3: word2直接添加一个字符,此时dp[i][j] = dp[i][j-1]+1

以上三种情况取最小值即可。

class Solution
{
  public:
    int minDistance(string word1, string word2)
    {
        int m = word1.size(), n = word2.size();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1, 0));
        for (int i = 1; i <= m; i++)
        {
            dp[i][0] = i;
        }
        for (int j = 1; j <= n; j++)
        {
            dp[0][j] = j;
        }
        for (int i = 1; i <= m; i++)
        {
            for (int j = 1; j <= n; j++)
            {
                if (word1[i - 1] == word2[j - 1])
                {
                    dp[i][j] = dp[i - 1][j - 1];
                }
                else
                {
                    dp[i][j] = min(dp[i - 1][j - 1], min(dp[i][j - 1], dp[i - 1][j])) + 1;
                }
            }
        }
        return dp[m][n];
    }
};

76.Minimum Window Substring

s包含t的最小子串。

滑动窗口。用一个map来记录t中字符出现次数。count用来表示出现次数复合要求的个数,当count==t.size()的时候,s中的字符串包含t

设定begin和end两个指针,先移动end指针,将map对应的值+1,当s[end]在t中时,判断此时map[s[end]]的值是否小于等于0,如果满足,就将count++。当counter==t.size()时,先判断此时的字符串长度d,并更新d和head(用来记录子串起始位置)。然后移动head指针,如果map[s[begin]]<0,说明此时的窗口内的字符串已经不能包含t了,count--,跳出循环,继续往后移动end。

class Solution
{
  public:
    string minWindow(string s, string t)
    {
        vector<int> map(128, 0);
        for (auto c : t)
            map[c]--;
        int counter = 0, begin = 0, end = 0, d = INT_MAX, head = 0;
        while (end < s.size())
        {
            map[s[end]]++;
            if (map[s[end]] <= 0)
                counter++;
            end++;

            while (counter == t.size())
            {
                if (end - begin < d)
                {
                    d = end - begin;
                    head = begin;
                }

                map[s[begin]]--;
                if (map[s[begin]] < 0)
                    counter--;

                begin++;
            }
        }

        return d == INT_MAX ? "" : s.substr(head, d);
    }
};

84.Largest Rectangle in Histogram

求面积最大的长方形。

用一个栈来存储高度的下标,当栈顶的高度比当前高度小的时候,直接进栈,所以栈内的高度是从底向顶递增的。如果栈顶比当前高度高,那么出栈,作为长方形的高。然后计算宽,通过当前下标i和最大高度的下标计算出长度。

  class Solution {
    public:
        int largestRectangleArea(vector<int> &height) {
            
            int ret = 0;
            height.push_back(0);
            vector<int> index;
            
            for(int i = 0; i < height.size(); i++)
            {
                while(index.size() > 0 && height[index.back()] >= height[i])
                {
                    int h = height[index.back()];
                    index.pop_back();
                    
                    int sidx = index.size() > 0 ? index.back() : -1;
                    if(h * (i-sidx-1) > ret)
                        ret = h * (i-sidx-1);
                }
                index.push_back(i);
            }
            
            return ret;
        }
    };

85.Maximal Rectangle

97. Interleaving String

判断字符串s1,s2能否组成s3。

dp

class Solution
{
  public:
    bool isInterleave(string s1, string s2, string s3)
    {
        if (s3.length() != s1.length() + s2.length())
            return false;

        bool table[s1.length() + 1][s2.length() + 1];

        for (int i = 0; i < s1.length() + 1; i++)
            for (int j = 0; j < s2.length() + 1; j++)
            {
                if (i == 0 && j == 0)
                    table[i][j] = true;
                else if (i == 0)
                    table[i][j] = (table[i][j - 1] && 
                    s2[j - 1] == s3[i + j - 1]);
                else if (j == 0)
                    table[i][j] = (table[i - 1][j] && 
                    s1[i - 1] == s3[i + j - 1]);
                else
                    table[i][j] = (table[i - 1][j] && 
                    s1[i - 1] == s3[i + j - 1]) ||
                     (table[i][j - 1] && s2[j - 1] == s3[i + j - 1]);
            }

        return table[s1.length()][s2.length()];
    }
};

132. Palindrome Partitioning II

求一个字符串最少分成多少个子串,使得所有子串都是回文序列。

动态规划,对于0-i子串,假设需要dp[i]次分割。那么对于dp[i+1],考虑以s[i]作为中心的回文序列,求min(dp[i+j+1],dp[i-j]+1),其中j为到字符s[i]的距离。

class Solution
{
  public:
    int minCut(string s)
    {
        int n = s.size();
        vector<int> dp( n+1,0);

        //s的(0,i)子串最大切分数目为i-1,即全部分成单个字符,注意,要将字符串为0赋值为-1
        for(int i = 0;i<dp.size();i++)
            dp[i]=i-1;
        for(int i = 0;i<n;i++)
        {
            //分奇偶两种情况。j表示到i的距离。如果从i-j到i+j都是回文串,
            //那么对 dp[i+j+1],可以将[i-j]到[i+j]作为一个分割。
            //其值是dp[i-j]+1,与原值比较取较小值即可
            for(int j = 0;j<=i && i+j<n && s[i-j]==s[i+j];j++)
                dp[i+j+1] = min(dp[i+j+1],dp[i-j]+1);
            for(int j = 1;j<=i+1 && i+j<n && s[i+1-j]==s[i+j];j++)
                dp[i+j+1] = min(dp[i+j+1],dp[i+1-j]+1);


        }
        return dp[n];
    }
};

146. LRU Cache

实现一个LRU,使get()获取一个页面put()添加一个页面的时间复杂度均为O(1)

使用哈希表+双向链表。

Cachelist是一个list,用来实现O(1)时间内删除结点。当一个结点被touch(put或者get时),将其从原链表中删除,再插入表头。如果发生缺页,没有命中,那么直接删除表尾,将新的页面加入表头。

class LRUCache
{
  public:
    int cap;
    list<int> Cachelist;
    unordered_map<int, list<int>::iterator> pos_dict;
    unordered_map<int, int> value_dict;

    LRUCache(int capacity)
    {
        cap = capacity;
    }

    int get(int key)
    {
        if (value_dict.find(key) == value_dict.end())
            return -1;
        touch(key);
        return value_dict[key];
    }
    int touch(int key)
    {

        auto pos = pos_dict.find(key);
        if (pos == pos_dict.end() && Cachelist.size() == cap)
        {
            int old = Cachelist.back();
            Cachelist.pop_back();
            value_dict.erase(old);
            pos_dict.erase(old);
        }
        else if (pos != pos_dict.end())
        {
            Cachelist.erase(pos[key]);
        }

        Cachelist.push_front(key);
        pos_dict[key] = Cachelist.begin();

        return 0;
    }
    void put(int key, int value)
    {

        touch(key);
        cache[key] = value;
    }
};

164. Maximum Gap

一个未排序的数组,求当数组有序后,相邻两个数的最大差值。要求时间复杂度O(n)

最简单就是直接排序,但是时间复杂度不满足。需要使用桶排序。

将数组分组。因为最大的gap不会小于 (maxV - minV) / (nums.size() - 1),所以将数组分成(maxV - minV) / bucketsize + 1个桶,每个桶内的两个数必定不会复合要求,因为其差值小于bucketsize。所以gap的值一定是在两个桶之间取得的。用下一个桶的最小值减去上一个桶的最大值,就能获得一个gap,取最大值即可。

class Solution
{
  public:
    int maximumGap(vector<int> &nums)
    {

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

        int minV = INT_MAX;
        int maxV = INT_MIN;
        for (int i = 0; i < nums.size(); i++)
        {
            if (nums[i] > maxV)
                maxV = nums[i];
            if (nums[i] < minV)
                minV = nums[i];
        }

        int bucketsize = (maxV - minV) / (nums.size() - 1);

        if (bucketsize < 1)
            bucketsize = 1;
        int bucketnum = (maxV - minV) / bucketsize + 1;

        vector<int> minbucket(bucketnum, INT_MAX);
        vector<int> maxbucket(bucketnum, INT_MIN);

        for (auto num : nums)
        {
            int pos = (num - minV) / bucketsize;
            minbucket[pos] = min(num, minbucket[pos]);
            maxbucket[pos] = max(num, maxbucket[pos]);
        }

        int i = 0, j;
        int gap = maxbucket[0] - minbucket[0];
        while (i < bucketnum)
        {
            j = i + 1;
            while (j < bucketnum && minbucket[j] == INT_MAX && maxbucket[j] == INT_MIN)
                j++;
            if (j == bucketnum)
                break;
            gap = max(gap, minbucket[j] - maxbucket[i]);
            i = j;
        }
        return gap;
    }
};

174. Dungeon Game

打怪游戏。最少需要多少初始血量才能救到公主。主要难点是必须保证每一步的血量都不低于1。

依然是动态规划,不过是在底部和右侧添加dummy,并从右下角往左上角遍历。当需要的血量>0时,记为1.

class Solution
{
  public:
    int calculateMinimumHP(vector<vector<int>> &dungeon)
    {
        int M = dungeon.size();
        int N = dungeon[0].size();
        vector<vector<int>> hp(M + 1, vector<int>(N + 1, INT_MAX));
        hp[M][N - 1] = 1;
        for (int i = M - 1; i >= 0; i--)
        {
            for (int j = N - 1; j >= 0; j--)
            {
                int need = min(hp[i + 1][j], hp[i][j + 1]) - dungeon[i][j];
                hp[i][j] = need <= 0 ? 1 : need;
            }
        }
        return hp[0][0];
    }
};

188. Best Time to Buy and Sell Stock IV

买卖股票的时机。当有至多k次交易机会时,最大能够获利多少?

当k的值比较大的时候,可以简单计算,因为交易次数充足。

当k较小时,使用动态规划,dp[k][len]表示股票[0,len]序列在最多k次交易的情况下能够获得的最大利润。

动态规划基本公式:

    dp[k, i] = max(dp[k, i-1], prices[i] - prices[j] + dp[k-1, j-1]), j=[0..i-1]

先对k循环,再对len循环。tmpmin用来记录当前最小的买入值。用price[j]-tmpmin能够获取当前的最大利润。对于股票prices[j],如果没有使用,那么dp[i][j]=dp[i][j-1],如果使用了,那么dp[i][j] = prices[j]-tmpmin。

tmpmin则可以prices[j] - dp[i - 1][j - 1]计算得来。即在prices[j]的基础上减去之前计算的结果,求一个最小值。

class Solution
{
  public:
    int maxProfit(int k, vector<int> &prices)
    {
        int len = prices.size();
        if (k >= len / 2)
        {
            int ret = 0;
            for (int i = 1; i < len; i++)
                //当交易次数充足时,只要是递增的相邻两数,都可以获利
                if (prices[i] > prices[i - 1])
                    ret += prices[i] - prices[i - 1];
            return ret;
        }

        vector<vector<int>> dp(k + 1, vector<int>(len, 0));
        for (int i = 1; i <= k; i++)
        {
            int tmpmin = prices[0];
            for (int j = 1; j < len; j++)
            {
                tmpmin = min(tmpmin, prices[j] - dp[i - 1][j - 1]);
                dp[i][j] = max(dp[i][j - 1], prices[j] - tmpmin);
            }
        }

        return dp[k][len - 1];
    }
};

212. Word Search II

先建立字典树存储所有的单词,并用is_end来表示单词的结尾。

然后使用DFS来遍历整个board,当遍历到is_end的单词节点时,将该单词添加到result中。

class TrieNode
{
  public:
    bool is_end;
    vector<TrieNode *> children;
    TrieNode()
    {
        is_end = false;
        children = vector<TrieNode *>(26, NULL);
    }
};

class Trie
{
  public:
    TrieNode *getRoot() { return root; }
    Trie(vector<string> &words)
    {
        root = new TrieNode();
        for (int i = 0; i < words.size(); ++i)
            addWord(words[i]);
    }
    void addWord(const string &word)
    {
        TrieNode *cur = root;
        for (int i = 0; i < word.size(); ++i)
        {
            int index = word[i] - 'a';
            if (cur->children[index] == NULL)
                cur->children[index] = new TrieNode();
            cur = cur->children[index];
        }
        cur->is_end = true;
    }

  private:
    TrieNode *root;
};

class Solution
{
  public:
    vector<string> findWords(vector<vector<char>> &board, vector<string> &words)
    {
        Trie *trie = new Trie(words);
        TrieNode *root = trie->getRoot();
        set<string> result_set;
        for (int x = 0; x < board.size(); ++x)
            for (int y = 0; y < board[0].size(); ++y)
                findWords(board, x, y, root, "", result_set);

        vector<string> result;
        for (auto it : result_set)
            result.push_back(it);
        return result;
    }

  private:
    void findWords(vector<vector<char>> &board, int x, int y, TrieNode *root, string word, set<string> &result)
    {
        if (x < 0 || x >= board.size() || y < 0 || y >= board[0].size() || board[x][y] == ' ')
            return;

        if (root->children[board[x][y] - 'a'] != NULL)
        {
            word = word + board[x][y];
            root = root->children[board[x][y] - 'a'];
            if (root->is_end)
                result.insert(word);
            char c = board[x][y];
            board[x][y] = ' ';
            findWords(board, x + 1, y, root, word, result);
            findWords(board, x - 1, y, root, word, result);
            findWords(board, x, y + 1, root, word, result);
            findWords(board, x, y - 1, root, word, result);
            board[x][y] = c;
        }
    }
};

224. Basic Calculator

实现一个简单的加减括号计算器。

class Solution
{
  public:
    int calculate(string s)
    {
        long long  total = 0;
        vector<int> signs(2, 1);
        for (int i = 0; i < s.size(); i++)
        {
            char c = s[i];
            if (c >= '0')
            {
                long long number = 0;
                while (i < s.size() && s[i] >= '0')
                    number = 10 * number + s[i++] - '0';
                total += signs.back() * number;
                signs.pop_back();
                i--;
            }
            else if (c == ')')
                signs.pop_back();
            else if (c != ' ')
                signs.push_back(signs.back() * (c == '-' ? -1 : 1));
        }
        return total;
    }
};

239. Sliding Window Maximum

给定一个数组,求所有长度为k的每一个滑动窗口中的最大值。

使用deque(双向队列),队列中的数保持递减、个数小于k。这样既能满足滑动窗口大小的要求,又能在需要求最大值的时候,直接取队头。而且当窗口大小到达k时,可以直接把队头pop掉。

class Solution
{
  public:
    vector<int> maxSlidingWindow(vector<int> &nums, int k)
    {

        deque<int> dq;
        vector<int> ret;
        for (int i = 0; i < nums.size(); i++)
        {
            //当滑动窗口的大小大于k时,将第一个数出队列。
            if (!dq.empty() && dq.front() == i - k)
                dq.pop_front();
            //比较队尾和Nums[i]的关系,如果比nums[i]小,
            //这些值一定不能成为最终选用的值,不考虑,直接出队即可。
            while (!dq.empty() && nums[dq.back()] < nums[i])
                dq.pop_back();

            dq.push_back(i);

            //从k-1下表开始,开始写入答案,队头即最大值。
            if (i >= k - 1)
                ret.push_back(nums[dq.front()]);
        }
        return ret;
    }
};

297. Serialize and Deserialize Binary Tree

如何序列化一个二叉树并恢复。

给出 先序(递归)、层序(队列)两种方法。

class Codec
{
  public:
    string serialize(TreeNode *root)
    {
        ostringstream out;
        serialize(root, out);
        return out.str();
    }

    TreeNode *deserialize(string data)
    {
        istringstream in(data);
        return deserialize(in);
    }

  private:
    void serialize(TreeNode *root, ostringstream &out)
    {
        if (root)
        {
            out << root->val << ' ';
            serialize(root->left, out);
            serialize(root->right, out);
        }
        else
        {
            out << "# ";
        }
    }

    TreeNode *deserialize(istringstream &in)
    {
        string val;
        in >> val;
        if (val == "#")
            return nullptr;
        TreeNode *root = new TreeNode(stoi(val));
        root->left = deserialize(in);
        root->right = deserialize(in);
        return root;
    }
};

class Codec
{
  public:
    // Encodes a tree to a single string.
    string serialize(TreeNode *root) const
    {
        if (!root)
            return "[]";

        ostringstream os;
        os << "[" << root->val;
        int null_count = 0;
        queue<TreeNode *> q({root->left, root->right});
        while (!q.empty())
        {
            auto node = q.front();
            q.pop();
            if (node)
            {
                os << ',' << node->val;
                if (null_count > 0)
                {
                    os << ":" << null_count;
                    null_count = 0;
                }
                q.push(node->left);
                q.push(node->right);
            }
            else
            {
                ++null_count;
            }
        }

        os << "]";
        return os.str();
    }

    // Decodes your encoded data to tree.
    TreeNode *deserialize(string data) const
    {
        istringstream is(data);
        TreeNode *root = nullptr;
        queue<TreeNode **> q({&root});

        int value, null_count;
        char sep;
        is >> sep >> value;
        while (is)
        {
            if (is.peek() == ':')
            {
                is >> sep >> null_count;
                for (int i = 0; i < null_count; ++i)
                    q.pop();
            }

            TreeNode *node = new TreeNode(value);
            q.push(&node->left);
            q.push(&node->right);
            *(q.front()) = node;
            q.pop();

            is >> sep >> value;
        }
        return root;
    }
};
© 皖ICP备20011981号