LEETCODE 刷题记录 HARD类

版权声明:转载请附加出处 https://blog.csdn.net/tianhao199703/article/details/85223819

42. Trapping Rain Water

原题目

Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!

心得

我先记录自己的思路,首先我定义了一个结构体rain,用来存储每个位置的高度和是否访问过的bool,然后我先遍历一遍数组找到最高的作为参照,将数组存入优先队列中,每次弹出一个次高的元素,计算它到最高的之间的盛水量,如果计算过一次就将bool变量置为true。

struct rain
{
    int high;
    bool use;
};
class Solution {
public:
    int trap(vector<int>& height) {
        int size = height.size();
        if(size==0){return 0;}
        int count=0;
        rain r[size];
        priority_queue<pair<int,int>> pq;
        for(int i=0;i<size;i++)
        {
            r[i].high = height[i];
            r[i].use = false;
            pair<int, int> b(height[i],i);
            pq.push(b);
        }
        pair<int, int> highest,cur;
        highest = pq.top();
        pq.pop();
        while(!pq.empty())
        {
            cur = pq.top();
            pq.pop();
            if(r[cur.second].use==true){continue;}
            r[cur.second].use = true;
            if(cur.second < highest.second)
            {
                for(int i = cur.second;i < highest.second;i++)
                {
                    if(r[i].use==true){continue;}
                    r[i].use = true;
                    count += cur.first-r[i].high;
                }
            }
            else
            {
                for(int i = cur.second;i > highest.second;i--)
                {
                    if(r[i].use==true){continue;}
                    r[i].use = true;
                    count += cur.first-r[i].high;
                }
            }
        }
        return count;
    }
};

其实该问题还有别的解决思路,也就是积累法:分别维护一个maxleft和maxright,将整个池子想象成一个大的水池,不断抬高边界高度即可。

int trap(int A[], int n) {
        int left=0; int right=n-1;
        int res=0;
        int maxleft=0, maxright=0;
        while(left<=right){
            if(A[left]<=A[right]){
                if(A[left]>=maxleft) maxleft=A[left];
                else res+=maxleft-A[left];
                left++;
            }
            else{
                if(A[right]>=maxright) maxright= A[right];
                else res+=maxright-A[right];
                right--;
            }
        }
        return res;
    }

128. Longest Consecutive Sequence

原问题

Given an unsorted array of integers, find the length of the longest consecutive elements sequence.

Your algorithm should run in O(n) complexity.

心得

这个问题我想的没问题,但是写起来的时候空间用多了,导致修改了很久。核心的思想是:维护一个哈希表unordered_map,用来记录数组中的数和对应的最大连接长度。只需要维护最长连接数组的两端即可。

int longestConsecutive(vector<int>& nums) {
        unordered_map<int, int> numbers;
        int length = 0;
        for (int i : nums) {
            if (numbers[i]) {continue;}
            length = max(length, numbers[i]=numbers[i+numbers[i+1]]=numbers[i-numbers[i-1]]=numbers[i+1]+numbers[i-1]+1);
        }//这个地方就是维护数组中连续的两端的节点和长度
        return length;
    }

239. Sliding Window Maximum

原问题

Given an array nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position. Return the max sliding window.

心得

从讨论区中学到一个新的数据结构deque,双端队列,支持队列收尾的添加删除等操作。

vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;
        vector<int> ans;
        for (int i=0; i<nums.size(); i++) {
            if (!dq.empty() && dq.front() == i-k) dq.pop_front();
            while (!dq.empty() && nums[dq.back()] < nums[i])
                dq.pop_back();
            dq.push_back(i);
            if (i>=k-1) ans.push_back(nums[dq.front()]);
        }
        return ans;
    }

218. The Skyline Problem

心得

vector<pair<int, int>> getSkyline(vector<vector<int>>& buildings) {

        // Step 1:
		multimap<int, int> coords;
		for (const vector<int> & building : buildings) {
			coords.emplace(building[0], building[2]);
			coords.emplace(building[1], -building[2]);
		}

        // Step 2:
		multiset<int> heights = { 0 };
		map<int, int> corners;
		for (const pair<int, int> & p : coords) {
			if (p.second > 0) {
				heights.insert(p.second);
			}
			else {
				heights.erase(heights.find(-p.second));
			}
			int cur_y = *heights.rbegin();
			corners[p.first] = cur_y;
		}

        // Step 3:
		function<bool(pair<int, int> l, pair<int, int> r)> eq2nd = [](pair<int, int> l, pair<int, int> r){ return l.second == r.second;  };
		vector<pair<int, int>> results;
		unique_copy(corners.begin(), corners.end(), back_insert_iterator<vector<pair<int, int>>>(results), eq2nd);
		return results;
	}

212. Word Search II

原问题

Given a 2D board and a list of words from the dictionary, find all words in the board.

Each word must be constructed from letters of sequentially adjacent cell, where “adjacent” cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once in a word.

心得

这个问题很巧,在之前的字节跳动的面试中是作为一道“easy”题出现的(服了)。我当时的直白想法就是,无非就是对每个词组中的词进行一次dfs搜索,也就是说O( N 3 N^3 )的时间复杂度。我在看了讨论区之后,发现大家都用到了一个数据结构trie

Trie,又称字典树、单词查找树或键树,是一种树形结构,是一种哈希树的变种。典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计。它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高。

  • 首先是trie结构的实现:
    trienode 类是trie的节点,包含的属性是,is_end:判断是否是叶节点;children:初始化26个字母的子节点。
    trie类实现了三个功能:getRoot():获得根节点;addWord():将一个给定的string添加到树中;Trie():用一个string的vector初始化一个trie树。
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;
};
  • 其次是实际的代码实现部分:
    这个就比较简单了,trie的应用是为了能够让拥有相同前缀的string在判断到达根节点以后同时加入到set中。而为了避免重复的方式则是将访问过的元素暂时标记为‘ ’。这个相比暴力的搜索法在拥有更多相同前缀的string是,效率要高得多。
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;        
        }
    }
};

140. Word Break II

原问题

Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add spaces in s to construct a sentence where each word is a valid dictionary word. Return all such possible sentences.

Note:

The same word in the dictionary may be reused multiple times in the segmentation.
You may assume the dictionary does not contain duplicate words.

心得

我之前准备采用的还是分割法,但是分割法在碰到某些比较特殊的例子时,会出现time exceed limit。在讨论区中发现这样一种方法,思路是:首先用一个hash表储存给定的字典,构造一个hashmap用来储存当前字符串长度和输出字符串。构造一个内联函数sentence。核心思想是通过动态储存来减少重复计算的浪费。

vector<string> wordBreak(string s, vector<string>& wordDict) {
        unordered_set<string> wordD;
        for(auto &i:wordDict){wordD.insert(i);}
        unordered_map<int, vector<string>> memo {{s.size(), {""}}};
        function<vector<string>(int)> sentences = [&](int i) {
            if (!memo.count(i))
                for (int j=i+1; j<=s.size(); j++)
                    if (wordD.count(s.substr(i, j-i)))
                        for (string tail : sentences(j))
                            memo[i].push_back(s.substr(i, j-i) + (tail=="" ? "" : ' ' + tail));
            return memo[i];
        };
        return sentences(0);
    }

10. Regular Expression Matching

原问题

Given an input string (s) and a pattern §, implement regular expression matching with support for ‘.’ and ‘*’.

  • ‘.’ Matches any single character.
  • ‘*’ Matches zero or more of the preceding element.

The matching should cover the entire input string (not partial).

心得

这个题老实说我一点思路都没有,只能去学习一下评论区的高赞答案。别人给出了两种解决方法:

  1. 递归法:分两种情况讨论,一种是当p中第二位不是‘*’时,此时我们只需要判断s[0]和p[0]是否是相同的,以及s[1…]和p[1…]是否是相同的;另一种是p的第二位是‘*’的情况,此时除了上面的判断之外,另外需要判断排除*字符 之后是否匹配,代码如下
bool isMatch(string s, string p) {
        if (p.empty())    return s.empty();
        
        if ('*' == p[1])
            // x* matches empty string or at least one character: x* -> xx*
            // *s is to ensure s is non-empty
            return (isMatch(s, p.substr(2)) || !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p));
        else
            return !s.empty() && (s[0] == p[0] || '.' == p[0]) && isMatch(s.substr(1), p.substr(1));
    }
  1. 另一种方法是DP法,核心思想是用一个二维数组储存s和p给定长度是否匹配。参考视频:https://www.youtube.com/watch?v=l3hda49XcDE&list=PLrmLmBdmIlpuE5GEMDXWf0PWbBD9Ga1lO
    代码如下:代码如下:
bool isMatch(string s, string p) {
        /**
         * f[i][j]: if s[0..i-1] matches p[0..j-1]
         * if p[j - 1] != '*'
         *      f[i][j] = f[i - 1][j - 1] && s[i - 1] == p[j - 1]
         * if p[j - 1] == '*', denote p[j - 2] with x
         *      f[i][j] is true iff any of the following is true
         *      1) "x*" repeats 0 time and matches empty: f[i][j - 2]
         *      2) "x*" repeats >= 1 times and matches "x*x": s[i - 1] == x && f[i - 1][j]
         * '.' matches any single character
         */
        int m = s.size(), n = p.size();
        vector<vector<bool>> f(m + 1, vector<bool>(n + 1, false));
        
        f[0][0] = true;
        for (int i = 1; i <= m; i++)
            f[i][0] = false;
        // p[0.., j - 3, j - 2, j - 1] matches empty iff p[j - 1] is '*' and p[0..j - 3] matches empty
        for (int j = 1; j <= n; j++)
            f[0][j] = j > 1 && '*' == p[j - 1] && f[0][j - 2];
        
        for (int i = 1; i <= m; i++)
            for (int j = 1; j <= n; j++)
                if (p[j - 1] != '*')
                    f[i][j] = f[i - 1][j - 1] && (s[i - 1] == p[j - 1] || '.' == p[j - 1]);
                else
                    // p[0] cannot be '*' so no need to check "j > 1" here
                    f[i][j] = f[i][j - 2] || (s[i - 1] == p[j - 2] || '.' == p[j - 2]) && f[i - 1][j];
        
        return f[m][n];
    }

76. Minimum Window Substring

原题目

Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n).

心得

类似问题,需要一个数组记录目标的变化情况,并将每次达到要求的子串长度和其实位置记录下来,这个count就是精髓

string minWindow(string S, string T) {
        if (S.empty() || T.empty()){return "";}
        int count = T.size();//待处理字符数
        int require[128] = {0};//记录T中的字符,和出现次数
        bool chSet[128] = {false};//记录是否出现过该字符
        for (int i = 0; i < count; ++i){
            require[T[i]]++;
            chSet[T[i]] = true;
        }
        int i = -1;
        int j = 0;
        int minLen = INT_MAX;
        int minIdx = 0;
        while (i < (int)S.size() && j < (int)S.size()){
            if (count){
                i++;
                require[S[i]]--;
                if (chSet[S[i]] && require[S[i]] >= 0){count--;}
            }
            else{
                if (minLen > i - j + 1){
                    minLen = i - j + 1;
                    minIdx = j;
                }
                require[S[j]]++;
                if (chSet[S[j]] && require[S[j]] > 0){count++;}
                j++;
            }
        }
        if (minLen == INT_MAX){return "";}
        return S.substr(minIdx, minLen);
    }

猜你喜欢

转载自blog.csdn.net/tianhao199703/article/details/85223819