前言

看了一下,今天要写的这道滑动窗口是hard的,只能自己模仿试试了,加深滑动窗口的理解,另外我发现让ChatGPT读代码真的好方便,而且比较准确,也能举一反三,小白狂喜

76. 最小覆盖子串

  • 官方题解给的动图,也是滑动窗口的思路,右边界先,左边界后
  • 重点在于t可能有重复字符,需要有判断字符频次的哈希表
  • class Solution {
    public:
        string minWindow(string s, string t) {
            // 声明两个哈希表,tmap和smap,用于存储t和s中出现的字符以及对应的数量
            unordered_map<char, int> smap, tmap;
            int left = 0, correct = 0; // left指向当前窗口的左端点,correct表示在当前窗口内已经匹配上的字符数量
            // 初始化res为s串加一个最大字符串,用于记录当前满足条件的最小子串
            string res = s + "initialize a max string";
            // 对于t制作字符出现数的字典
            for (const auto &item: t)
                ++tmap[item]; // 遍历t串中的所有字符,将其加入到tmap哈希表中,并记录其出现次数
    
            // 遍历s串中的每个字符
            for (int right = 0; right < s.size(); ++right) {
                // 将当前字符添加到smap中,即记录当前窗口内字符的出现次数
                ++smap[s[right]];
                // 如果当前字符是t串中存在的字符,并且在smap中的数量不超过tmap中的数量,则匹配成功
                if (smap[s[right]] <= tmap[s[right]])
                    ++correct;
                // 当前窗口内已经包含了t中所有字符,需要缩短左边界
                while (left < right && smap[s[left]] > tmap[s[left]])
                    --smap[s[left++]]; // 将左边界对应的字符移出窗口
                // 当前窗口内已经满足t串的所有字符,更新res
                if (correct == t.size()){
                    if (right - left + 1 < res.size())
                        res = s.substr(left, right - left + 1); // 更新最小子串
                }
            }
            // 如果res没有被更新,则说明没有找到符合条件的子串
            return res == s + "initialize a max string" ? "" : res;
        }
    };
    

59. 螺旋矩阵 II

  • 创建矩阵赋值,左闭右开,不难但要求边界逻辑清楚

 

 

 

  • class Solution {
    public:
        vector<vector<int>> generateMatrix(int n) {
            vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
            int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
            int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
            int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
            int count = 1; // 用来给矩阵中每一个空格赋值
            int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
            int i,j;
            while (loop --) {
                i = startx;
                j = starty;
    
                // 下面开始的四个for就是模拟转了一圈
                // 模拟填充上行从左到右(左闭右开)
                for (j = starty; j < n - offset; j++) {
                    res[startx][j] = count++;
                }
                // 模拟填充右列从上到下(左闭右开)
                for (i = startx; i < n - offset; i++) {
                    res[i][j] = count++;
                }
                // 模拟填充下行从右到左(左闭右开)
                for (; j > starty; j--) {
                    res[i][j] = count++;
                }
                // 模拟填充左列从下到上(左闭右开)
                for (; i > startx; i--) {
                    res[i][j] = count++;
                }
    
                // 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
                startx++;
                starty++;
    
                // offset 控制每一圈里每一条边遍历的长度
                offset += 1;
            }
    
            // 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
            if (n % 2) {
                res[mid][mid] = count;
            }
            return res;
        }
    };

 54. 螺旋矩阵

  •  题解里大佬设定边界的写法,真的如同“疏通了我多年的便秘”
  • class Solution {
    public:
        vector<int> spiralOrder(vector<vector<int>>& matrix) {
            vector <int> ans;
            if(matrix.empty()) return ans; //若数组为空,直接返回答案
            int u = 0; //赋值上下左右边界
            int d = matrix.size() - 1;
            int l = 0;
            int r = matrix[0].size() - 1;
            while(true)
            {
                for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
                if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
                for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
                if(-- r < l) break; //重新设定有边界
                for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
                if(-- d < u) break; //重新设定下边界
                for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
                if(++ l > r) break; //重新设定左边界
            }
            return ans;
        }
    };

 数组总结

  • 二分法:O(n) → O(logn),循环不变量原则,常考题
  • 双指针法:O(n2) → O(n),一个快指针和慢指针在一个for循环下完成两个for循环的工作
  • 滑动窗口:O(n2) → O(n),根据当前子序列和大小的情况,不断调节子序列的两端位置
  • 模拟行为:单纯模拟,循环不变量原则,常见题,代码尽量简洁

 后言

今天整整刷了一下午了!明天开链表啦~数据结构这边还没撕过代码呢,希望能学清楚吧。

更多推荐

【代码随想录】刷题笔记Day6