刷力扣中等题

题源:LeetCode

550道,每题重复10遍,我就不信记不住了还。

45.跳跃游戏II

贪心,通过局部最优解得到全局最优解。每次在上次能跳到的范围(end)内选择一个能跳的最远的位置(也就是能跳到max_far位置的点)(覆盖了跳得近的点)作为下次的起跳点 。

class Solution {
public:
    int jump(vector<int>& nums) {
        int step = 0;//跳跃次数
        int max_far = 0;//目前能跳到的最远位置
        int end = 0; //上次跳级可达范围右边界(下次的最右起跳点)
        for(int i = 0; i < nums.size() - 1; i++){
            max_far = max(max_far, i + nums[i]);
            if(i == end){
                //到达上次跳跃能到达的右边界了
                end = max_far; // 目前能跳到的最远位置变成了下次起跳位置的右边界,起跳点就是能跳到最远位置的点
                step++; // 进入下一次跳跃
            }
        }   
        return step;
    }
};

1190. 反转每对括号间的子串

对于括号序列相关的题目,通用的解法是使用递归或栈。本题中我们将使用栈解决。
下一层和上一层之间如何跳转。

class Solution {
public:
    string reverseParentheses(string s) {
        stack<string> stk;
        string  str;
        for(auto & ch : s){
        //上下层跳转时才会对栈操作,否则就对当前字符串操作
            if(ch == '('){
            	//下一层
                stk.push(str);
                str = "";
            }else if(ch == ')'){
            	//上一层
                reverse(str.begin(), str.end());
                str = stk.top() + str;
                stk.pop();
            }else {
            	//这一层
                str.push_back(ch);
            }
        }
        return str;
    }
};

5. 最长回文子串

动态规划

class Solution {
public:
    string longestPalindrome(string s) {
        int n = s.size();
        // dp[left][right]标记从i到j是否是回文串
        vector<vector<int> > dp(n, vector<int>(n));
        string ans;
        // length表示判断的字串的长度
        // left表示字串的左边起始位置
        // right表示字串的右边起始位置
        for(int length = 1; length <= n; length++){//length = 0 是非法的
            for(int left = 0; left + length-1 < n; left++){
                int right = left + length -1;
                // 即字符串长度为1时,矩阵对角线
                if(length == 1) dp[left][right] = 1; //边界1
                // 字符串长度为2的时候,只需判断两者是否相等
                else if(length == 2) dp[left][right] = (s[left] == s[right]); //边界2
                else{  // 字符串长度大于等于3之后
                       // 其是否是回文串取决于当前left和right及更小一号的字符串
                       // 更新参考值为矩阵表的左下方
                    dp[left][right] = (s[left] == s[right] && dp[left + 1][right - 1]);
                }
                // 如果当前left位置到right位置的字串能够构成回文串,并且现在长度大于之前记忆中的子回文串的长度,那么更新回文串!这里也可以优化成记录起始位置和长度的两个int,返回时再截取
                if(dp[left][right] && length > ans.size()){
                    ans = s.substr(left, length );
                }
            }
        }
        return ans;
    }
};

中心扩展法

class Solution {
public:
    pair<int, int> expandAroundCenter(const string& s, int left, int right){
        while(left >=0 && right < s.size() && s[left] == s[right]){
            left--;
            right++;
        }
        return {left + 1, right - 1};
    }
    string longestPalindrome(string s) {
        int start(0), end(0);
        for(int i = 0; i < s.size(); i++){
            auto [left1, right1] = expandAroundCenter(s, i, i);
            auto [left2, right2] = expandAroundCenter(s, i, i + 1);

            if(right1 - left1 > end - start){
                start = left1;
                end = right1;
            }
            if(right2 - left2 > end - start){
                start = left2;
                end = right2;
            }
        }
        return s.substr(start, end - start + 1);
    }
};

739. 每日温度

单调栈

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int> ans(n, 0);
        stack<int> s;
        for(int i = 0; i < n; i++){
            while(!s.empty() && temperatures[i] > temperatures[s.top()]){
                int previousIndex = s.top();
                ans[previousIndex] = i - previousIndex;
                s.pop();
            }
            s.push(i);
        }
        return ans;
    }
};

200. 岛屿数量

class UnionFind{
    vector<int> parent;
    int n;
public:
    UnionFind(int _n) : n(_n), parent(_n){
        iota(parent.begin(), parent.end(), 0);
    }

    int find(int x){
        return x == parent[x] ? x : parent[x] = find(parent[x]);
    }

    void merge(int x, int y){
        int rootx = find(x);
        int rooty = find(y);
        if(rootx == rooty) return;
        parent[rooty] = rootx;//把y合并到x上
        n--;//合并后集合数量减一
    }

    bool isConnected(int x, int y){
        return find(x) == find(y);
    }

    int getSize(){
        return n;//最开始是m*n个集合
    }
};
class Solution {
public:
    int dx[4] = {-1, 1, 0, 0};
    int dy[4] = {0, 0, 1, -1};
    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size();//行
        int n = (m == 0 ? 0 : grid[0].size());//列
        if(m == 0 || n == 0) return 0;
        vector<int> cnt(2, 0);//计数君,grid[i][j]的值是1或0
        for(int i = 0; i < m; i++)
            for(int j = 0; j < n; j++)
                cnt[grid[i][j]-'0']++;//cnt[0]是grid[][]中0出现的次数。cnt[1]是grid[][]中1出现的次数。
        UnionFind uf(m * n);
        for(int i = 0; i < m; i++){//行
            for(int j = 0; j < n; j++){//列
                if(grid[i][j] == '0')
                    continue;
                for(int k = 0; k < 4; k++){
                    int xx = i + dx[k], yy = j + dy[k];
                    if(xx < 0 || xx >= m || yy < 0 || yy >= n)
                        continue;
                    if(grid[xx][yy] == '1'){
                        uf.merge(i * n + j, xx * n + yy);
                    }
                }
            }
        }
        return uf.getSize() - cnt[0];
    }
};

3. 无重复字符的最长子串

滑动窗口

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int start(0), end(0), length(0), result(0);
        int size = s.size();
        while(end < size){//每次挪动end一个长度,可以覆盖全部子字符串
            for(int idx = start; idx < end; idx++){
                //start之前的肯定有重复字符,所以不考虑
                if(s[idx] == s[end]){
                    start = idx + 1;
                    length = end - start;
                    break;
                }
            }
            length++;
            result = max(result, length);
            end++;
        }
        return result;
    }
};

2104. 子数组范围和

滑动窗口,遍历子数组。

class Solution {
public:
    long long subArrayRanges(vector<int>& nums) {
        int n = nums.size();
        long long ret = 0;
        for(int i = 0; i < n; i++){
            int minVal = nums[i], maxVal = nums[i];
            for(int j = i; j < n; j++){
                minVal = min(minVal, nums[j]);
                maxVal = max(maxVal, nums[j]);
                ret += maxVal - minVal;
            }
        }
        return ret;
    }
};

单调栈

84. 柱状图中最大的矩形

暴力解法(超时):
首先我们枚举某一根柱子 i 作为高h=heights[i];
随后我们需要进行向左右两边扩展,使得扩展到的柱子的高度均不小于 hh。换句话说,我们需要找到左右两侧最近的高度小于 h 的柱子,这样这两根柱子之间(不包括其本身)的所有柱子高度均不小于 h,并且就是 i 能够扩展到的最远范围。

单调栈(栈中存放的元素具有单调性)解法:

  • 重点在求什么: 需要求出每一根柱子的左侧且最近的小于其高度的柱子
  • 初始时的栈为空。
  • 栈中存放了 j 值。从栈底到栈顶,j 的值严格单调递增,同时对应的高度值也严格单调递增;
  • 当我们枚举到第 i 根柱子时,我们从栈顶不断地移除 height[j]≥height[i] 的 j 值。在移除完毕后,栈顶的 j 值就一定满足 height[j]j 就是 i 左侧且最近的小于其高度的柱子。
    这里会有一种特殊情况。如果我们移除了栈中所有的 j 值,那就说明 i 左侧所有柱子的高度都大于 height[i],那么我们可以认为 ii 左侧且最近的小于其高度的柱子在位置 j=−1,它是一根「虚拟」的、高度无限低的柱子。这样的定义不会对我们的答案产生任何的影响,我们也称这根「虚拟」的柱子为「哨兵」。
  • 我们再将 i 放入栈顶。
  • 我们得到它们左侧的柱子编号。用相同的方法,我们从右向左进行遍历,也可以得到它们右侧的柱子编号。在得到了左右两侧的柱子之后,我们就可以计算出每根柱子对应的左右边界,并求出答案了。
    因此当我们从左向右/总右向左遍历数组时,对栈的操作的次数就为 O(N)。所以单调栈的总时间复杂度为 O(N)。
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        vector<int> left(n, -1), right(n, n);
        stack<int> s;//单调栈
        for (int i = 0; i < n; i++) {
            while (!s.empty() && heights[s.top()] >= heights[i]){
                //栈不为空并且栈顶元素大于现在这个元素
                right[s.top()] = i;//出栈前得到要出栈的元素的位置对应右边界
                s.pop();
            }
            //停止循环后,栈中的元素都是单调递增的
            left[i] = s.empty() ? -1 : s.top();//要入栈的元素的左边界是-1或者当前栈顶元素的位置
            s.push(i);
        }
        int ans = 0;
        for(int i = 0; i < n; i++){
            ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
        } 
        return ans;
    }
};

85. 最大矩形

class Solution {
public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        int m = matrix.size();
        if(m == 0) return 0;
        int n = matrix[0].size();
        vector<vector<int> > left(m, vector<int> (n, 0));

        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(matrix[i][j] == '1'){
                    left[i][j] = (j == 0 ? 0 : left[i][j - 1]) + 1;
                }
            }
        }

        int ret = 0;
        for(int j = 0; j < n; j++){
            vector<int> up(m, 0), down(m, m);
            stack<int> stk;

            for(int i = 0; i < m; i++){
                while(!stk.empty() && left[stk.top()][j] >= left[i][j]){
                    down[stk.top()] = i;
                    stk.pop();
                }
                up[i] = stk.empty() ? -1 : stk.top();
                stk.push(i);
            }

            for(int i = 0; i < m; i++){
                int height = down[i] - up[i] - 1;
                int area = height * left[i][j];
                ret = max(ret, area);
            }

        }
        return ret;
    }
};

42. 接雨水

781. 森林中的兔子

class Solution {
public:
    int numRabbits(vector<int>& answers) {
        unordered_map<int, int> rabbits;
        int res = 0;
        for(int i = 0; i < answers.size(); i++){
            if(rabbits[answers[i]] == 0){
                //之前回答某个颜色的兔子还有n个的兔子已经被数完了,现在回答还有n个兔子的兔子是另外的颜色
                res = res + answers[i] +1;//回答n个的兔子有n+1个
                rabbits[answers[i]] = answers[i];//回答n个的兔子还有n个
            }else{
                rabbits[answers[i]]--;
            }
        }
        return res;
    }
};

20. 有效的括号

栈,不合符流程就是出错。
unordered_map的size_type count(int key);函数如果Map中存在具有给定键的值,则此函数返回1,否则返回0。

class Solution {
public:
    bool isValid(string s) {
        int n = s.size();
        if(n % 2 == 1) return false;

        unordered_map<char, char> pairs ={{')', '('}, {']', '['}, {'}','{'}};
        stack<char> stk;
        for(char ch : s){
            if(pairs.count(ch)){
                //遇到右括号
                if(stk.empty() || stk.top() != pairs[ch]){
                    return false;
                }
                stk.pop();
            }else{
                //遇到左括号
                stk.push(ch);
            }
        }
        return stk.empty();
    }
};

221. 最大正方形

动态规划。从左上角到右下角。

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if(matrix.size() == 0 || matrix[0].size() == 0){
            return 0;
        }
        int maxSide = 0;
        int rows = matrix.size(), columns = matrix[0].size();
        vector<vector<int> > dp(rows, vector<int>(columns));
        for(int i = 0; i < rows; i++){
            for(int j = 0; j < columns; j++){
                if(matrix[i][j] == '1'){
                    if(i == 0 || j == 0){
                        dp[i][j] = 1;
                    }else{
                        dp[i][j] = min({dp[i-1][j], dp[i][j - 1], dp[i - 1][j - 1]}) + 1;//三个方向中选一个最小值+1
                    }
                    maxSide = max(maxSide, dp[i][j]);
                }
            }
        }
        return maxSide * maxSide;
    }
};

394.字符串解码

class Solution {
public:
    string decodeString(string s) {
        string res = "";
        stack<int> nums;
        stack<string> strs;
        int num = 0;
        int len = s.size();
        for(int i = 0; i < len; i++){
            if(s[i] >= '0' && s[i] <= '9'){
                num = num * 10 + s[i] - '0'; 
            }else if(s[i] >= 'a' && s[i] <= 'z' || (s[i] >= 'A' && s[i] <= 'Z')){
                //这一层
                res = res + s[i];
            }else if(s[i] == '['){
                //进入下一层
                nums.push(num);
                num = 0;
                strs.push(res);
                res = "";
            }else if(s[i] == ']'){
                //这一层
                int times = nums.top();
                nums.pop();
                for(int j = 0; j < times; j++){
                    strs.top() += res;
                }
                //回到上一层
                res = strs.top();
                strs.pop();
            }
        }
        return res;
    }
};

820. 单词的压缩编码

存储后缀

如果单词 X 是 Y 的后缀,那么单词 X 就不需要考虑了,因为编码 Y 的时候就同时将 X 编码了。例如,如果 words 中同时有 “me” 和 “time”,我们就可以在不改变答案的情况下不考虑 “me”。
如果单词 Y 不在任何别的单词 X 的后缀中出现,那么 Y 一定是编码字符串的一部分。
因此,目标就是保留所有不是其他单词后缀的单词,最后的结果就是这些单词长度加一的总和,因为每个单词编码后后面还需要跟一个 # 符号。
由数据范围可知一个单词最多含有 7 个后缀,所以我们可以枚举单词所有的后缀。对于每个后缀,如果其存在 words 列表中,我们就将其从列表中删除。为了高效删除,我们将 words 用哈希集合来存储。

class Solution {
public:
    int minimumLengthEncoding(vector<string>& words) {
        unordered_set<string> good(words.begin(), words.end());

        for(auto & word : words){
            for(int k = 1; k < word.size(); k++){
                good.erase(word.substr(k));//从第k个位置开始的后缀,substr()还可以加一个length参数,这里没必要
            }
        }

        int ans = 0;
        for(auto & word : good){
            ans += word.size() + 1;//+1 是加了#的长度
        }
        return ans;
    }
};

32. 最长有效括号

动态规划。

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxans = 0, n = s.length();
        vector<int> dp(n, 0);
        for(int i = 1; i < n; i++){
            if(s[i] == ')' && i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '('){
                //dp[i - 1]是已经配对括号的长度
                //i - dp[i - 1]- 1 是向前最近跳过一串已配对括号长度的未配对括号下标
                //如果s[i - dp[i - 1] - 1] == '(',那么说明可以和现在的s[i] == ')'配对,
                //dp[i]就会在dp[i-1]的基础上+ dp[i - dp[i - 1] - 2] + 2
                dp[i] = dp[i - 1] + ((i - dp[i - 1] ) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
                maxans = max(maxans, dp[i]);
            }
        }
        return maxans;
    }
};

栈。

class Solution {
public:
    int longestValidParentheses(string s) {
        int maxans = 0;
        stack<int> stk;
        stk.push(-1);//从栈中下标的下一个下标作为起点,算长度要-1所以正好i - stk.top() 是长度
        for(int i = 0; i < s.length(); i++){
            if(s[i] == '('){
                stk.push(i);
            } else {
                stk.pop();
                if(stk.empty()){
                    stk.push(i);//从这个无法配对的右括号开始算后面的
                }else{
                    maxans = max(maxans, i - stk.top());
                }
            }
        }
        //最后栈是空的或者全是右括号或者全是左括号或者一些右括号+一些左括号
        return maxans;
    }
};

43. 字符串相乘

class Solution {
public:
    string multiply(string num1, string num2) {
        int m = num1.size(), n = num2.size();
        string s(m + n, '0');//初始化大小m+n
        for(int i = m - 1; i >= 0; i--){ //从后向前
            for(int j = n - 1 ; j >= 0; j--){
                int cur = (num1[i] - '0') * (num2[j] - '0') + (s[i + j + 1] - '0');
                //注意累加之前的进位,j单调递减,s[i + j + 1]是上一层的s[i + j]
                s[i + j + 1] = cur % 10 + '0';//记录本位
                s[i + j] += cur / 10;//记录进位
            }
        }
        for(int i = 0; i < m + n; i++)
            if(s[i] != '0')
                return s.substr(i);
        return "0";
    }
};

2. 两数相加

模拟。

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        
        ListNode* prehead = new ListNode(0), *cur = prehead;
        
        int t = 0;
        while (l1 || l2 || t)  // 对于最后一位的判定可以合成到while循环里也可以单独放在while循环结束后
        {
            if (l1) t += l1->val, l1 = l1->next;
            if (l2) t += l2->val, l2 = l2->next;  // 一般情况下 t += (A[i] + B[i])后是一个0-19大的数字,个位push到当前位,而十位只有0和1作为进位继续后面的加法
            cur->next = new ListNode(t % 10);     // t % 10 是 t的个位
            cur = cur->next;
            t /= 10;                              // t/=10,计算是否有进位,并更新t, 在下一轮继续 t += (A[i] + B[i])
        }
        //if (t) cur->next = new ListNode(t);       // 别忘了如果最后一位加法完成后,还得考虑进位,现在把进位放到了循环里
        return prehead->next;
    }
};

递归。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        return add(l1, l2, 0);
    }
    ListNode* add(ListNode* l1, ListNode* l2, int c){
        if(l1 == NULL && l2 == NULL && !c) return NULL;

        int val = c;
        if(l1 != NULL){
            val += l1->val;
            l1 = l1->next;
        } 
        if(l2 != NULL){
            val += l2->val;
            l2 = l2->next;
        }
        ListNode* node = new ListNode(val % 10);
        node->next = add(l1, l2, val/10);
        return node;
    }
};

406. 根据身高重建队列

排序,插入。

class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        sort(people.begin(), people.end(), [](const vector<int>&u, const vector<int>& v){
            return u[0] > v[0] || (u[0] == v[0] && u[1] < v[1]); 
        });
        vector<vector<int> > ans;
        //people现在是从大到小的
        for(const vector<int> & person : people){
            ans.insert(ans.begin() + person[1], person);
        }
        return ans;
    }
};

554. 砖墙

巧妙地转换。
问题可以转换成求垂线穿过的砖块边缘数量的最大值。用砖墙的高度减去该最大值即为答案。

我们遍历砖墙的每一行,对于当前行,我们从左到右地扫描每一块砖,使用一个累加器记录当前砖的右侧边缘到砖墙的左边缘的距离,将除了最右侧的砖块以外的其他砖块的右边缘到砖墙的左边缘的距离加入到哈希表中。最后我们遍历该哈希表,找到出现次数最多的砖块边缘,这就是垂线经过的砖块边缘,而该垂线经过的砖块数量即为砖墙的高度减去该垂线经过的砖块边缘的数量。

class Solution {
public:
    int leastBricks(vector<vector<int>>& wall) {
        unordered_map<int, int> cnt;
        for(auto & widths : wall){
            int n = widths.size();
            int sum = 0;
            for(int i = 0; i < n - 1; i++){
                sum += widths[i];
                cnt[sum]++;
            }
        }
        int maxLength = 0;
        for(auto [_, c] : cnt){
            maxLength = max(c, maxLength);
        }
        return wall.size() - maxLength;
    }
};

135. 分发糖果

我们遍历该数组两次,处理出每一个学生分别满足左规则或右规则时,最少需要被分得的糖果数量。每个人最终分得的糖果数量即为这两个数量的最大值。

class Solution {
public:
    int candy(vector<int>& ratings) {
        int n = ratings.size();
        vector<int> left(n, 1);
        for(int i = 0; i < n; i++){
            if(i > 0 && ratings[i] > ratings[i - 1]){
                left[i] = left[i - 1] + 1;
            }
        }
        int right = 0, ret = 0;
        for(int i = n - 1; i >= 0; i--){
            if(i < n - 1 && ratings[i] > ratings[i + 1]){
                right++;
            }else{
                right = 1;
            }
            ret += max(left[i], right);//同时满足左规则和右规则
        }
        return ret;
    }
};

475. 供暖器

class Solution {
public:
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(houses.begin(), houses.end());
        sort(heaters.begin(), heaters.end());
        int ans = 0;
        for(int i = 0, j = 0; i < houses.size(); i++){
            int curDistance = abs(houses[i] - heaters[j]);
            while(j < heaters.size() - 1 && abs(houses[i] - heaters[j]) >= abs(houses[i] - heaters[j + 1])){
                curDistance = abs(houses[i] - heaters[++j]);
            }
            ans = max(ans, curDistance);
        }
        return ans;
    }
};

37. 解数独

class Solution {
private:
    bool line[9][9];
    bool column[9][9];
    bool block[3][3][9];
    bool valid;
    vector<pair<int, int>> spaces;

    void dfs(vector<vector<char> >& board, int pos){
        if(pos == spaces.size()){
            valid = true;
            return;
        }
        auto[i, j] = spaces[pos];
        for(int digit = 0; digit < 9 && !valid; digit++){
            if(!line[i][digit] && !column[j][digit] && !block[i / 3][j / 3][digit]){
                line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
                board[i][j] = digit + 1 +'0';
                dfs(board, pos + 1);
                line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = false;
            }
        }
    }

public:
    void solveSudoku(vector<vector<char>>& board) {
        
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.'){
                    spaces.emplace_back(i, j);
                }else{
                    int digit = board[i][j] - 1 - '0' ;
                    line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
                }
            }
        }
        dfs(board, 0);

    }
};

179. 最大数

    第一步:定义比较函数,把最大的放左边,排序
    第二步:拼接
    第三步:返回结果
class Solution {
public:
    string largestNumber(vector<int>& nums) {
        sort(nums.begin(), nums.end(), [](int a, int b){
            string aa = to_string(a);
            string bb = to_string(b);
            return aa + bb > bb + aa; 
        });
        string ret;
        for(int i : nums)
            ret += to_string(i);
        return ret[0] == '0' ? "0" : ret;
    }
};

46. 全排列

class Solution {
public:
    vector<vector<int>> res;
    unordered_map<int, bool> flag;
    void dfs(vector<int>& nums,int layer, int n, vector<int>&ans){
        if(layer == n){
            res.emplace_back(ans);
            return;
        }
        for(int i = 0; i < n; i++){
            if(!flag[nums[i]]){
                ans.emplace_back(nums[i]);
                flag[nums[i]] = true;
                dfs(nums, layer+1, n, ans);
                ans.pop_back();
                flag[nums[i]] =false;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans;
        dfs(nums, 0, n, ans);
        return res;
    }
};

55. 跳跃游戏

一层一层的感觉。

class Solution {
public:
    bool canJump(vector<int>& nums) {
        bool flag = false;
        int nowLayer = 0;
        int n = nums.size();
        for (int i = 0; i < n; i++){
            if (nowLayer < i) { 
                break;
            }
            if (nowLayer >= n - 1) {
                flag = true;
            }
            nowLayer = max(nowLayer, i + nums[i]);//更新最远范围
        }
        return flag;
    }
};

72. 编辑距离

动态规划。

class Solution {
public:
    int minDistance(string word1, string word2) {
        int dp[501][501];
        //dp[i][j]表示word1的前0到i-1共i个字符转换成word2的前0到j-1共j个字符需要的最少操作
        int n = word1.size(), m = word2.size();
        for(int i = 0; i <= n; i++) dp[i][0] = i;
        for(int j = 0; j <= m; j++) dp[0][j] = j;
        
        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], dp[i][j - 1], dp[i - 1][j]});
            }
        }
        return dp[n][m];
    }
};

93. 复原 IP 地址

std::move函数可以以非常简单的方式将左值引用转换为右值引用

class Solution {
private:
    vector<string> ans;
    vector<int> segments;
    const int SEG_COUNT = 4;
    void dfs(const string& s, int segId, int segStart){
        if(segId == SEG_COUNT){
        // 如果找到了 4 段 IP 地址并且遍历完了字符串,那么就是一种答案
            if(segStart == s.size()){
                string ipAddr;
                for(int i = 0; i < SEG_COUNT; i++){
                    ipAddr += to_string(segments[i]);
                    if(i != SEG_COUNT - 1)
                        ipAddr += ".";
                }
                ans.emplace_back(move(ipAddr));
            }
            return;
        }
        
        //如果剩余的字符比剩下的空位多,说明这种情况一定不行,剪枝,回溯
        if(s.size() - segStart  > (SEG_COUNT-segId) * 3)
            return;
            
 		// 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
        if(segStart == s.size())
            return;
            
        // 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
        if(s[segStart] == '0'){
            segments[segId] = 0;
            dfs(s, segId + 1, segStart + 1);
        }

		// 一般情况,枚举每一种可能性并递归
        int addr = 0;
        for(int segEnd = segStart; segEnd < s.size(); segEnd++){
            addr = addr * 10 + (s[segEnd] - '0');
            if(addr > 0 && addr <= 0xFF){
                segments[segId] = addr;
                dfs(s, segId + 1, segEnd + 1);
            }else{
                break;
            }
        }
        
    }
public:
    vector<string> restoreIpAddresses(string s) {
        segments.resize(SEG_COUNT);
        dfs(s, 0, 0);
        return ans;
    }
};

15. 三数之和

利用排序避免重复答案
「双指针」:当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2) 减少至 O(N)。
一种优化循环的方法。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int> > ans;
        for(int first = 0; first < n; first++){
            if(first > 0 && nums[first] == nums[first - 1]){
                //需要和上次的数不同
                continue;
            }
            int third = n - 1;
            int target = - nums[first];
            for(int second = first + 1; second < n; second++){
                if(second > first + 1 && nums[second] == nums[second - 1]){
                    //需要和上次的数不同
                    continue;
                }
                while(second < third && nums [second] + nums[third] > target){
                    third--;
                }
                if(second == third)
                    break;
                if(nums[second] + nums[third] == target){
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

798. 得分最高的最小轮调

预处理+动态规划

发现每次移动的时候,值对应下标-1,首位到末位; 本来比下标小的依旧得分,本来等于下标的失分,本来大于下标的依旧不得分,本来在下标0的移到n-1处得分; 状态转移为f[k] = f[k-1] - (k-1步时值与下标相等的元素个数) + 1; 要得到每个步数时值与下标相等的元素个数,可由每个元素移到与值相等的下标的步数O(N)得到;

class Solution {
public:
    int bestRotation(vector<int>& nums) {
        int n = nums.size(), val = 0, ans = 0;
        
        vector<int> step(n, 0);
        for(int i = 0; i < n; i++){
            if(nums[i] <= i) {
                step[i - nums[i]]++;//每个k对应多少下标相等的
                val++;//初始值
            }else step[i + n - nums[i]]++;//每个k对应多少下标相等的
        }

        int maxval = val;
        for(int i = 1; i < n; i++){
            //状态转移,多移一位,上次的分数要减去上次刚好值与下标相等的元素(多移一位就丧失分数)
            //首位移动到末位必多一分
            val = val - step[i - 1] + 1;
            if(val > maxval){
                ans = i;
                maxval = val;
            }
        }
        return ans;
    }
};

35. 搜索插入位置

二分

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1, mid;
        while(left <= right){
            mid = (right - left) / 2 + left;
            if(nums[mid] == target){
                return mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        //循环结束后,在left和right之间画一条竖线,恰好可以把数组分为两部分:left左边的部分和right右边的部分,而且left左边的部分全部小于target,并以right结尾;right右边的部分全部大于等于target,并以left为首。所以最终答案一定在left的位置。
        return left;
    }
};

你可能感兴趣的:(LeetCode刷题,c++,LeetCode一刷,链表,leetcode,数据结构)