当前位置: 首页 > news >正文

重温hot100-day2

11. 239 滑动窗口最大值

经典单调栈问题。一个deque维护单调栈,front都是最大值,递减存储,因此最大值就是front。但问题就是有个窗口大小,更需要更新最大值。k窗口往右移动,弹出左边的值。因此直接给deque中存储索引i,如果i-front>=k,说明front不在k窗口范围内,需要弹出。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> result;
        deque<int> q; //优先队列
        for(int i=0; i<nums.size(); i++){
            while(!q.empty() && nums[i]>=nums[q.back()]) //保持单调性,左往右是递减的
                q.pop_back();
            q.push_back(i);
            if(i - q.front()>=k) //保证队列右边值在窗口范围内
                q.pop_front();
            if(i+1>=k) //符合位置就可以读结果了
                result.emplace_back(nums[q.front()]);
        }
        return result;
    }
};

12. 76 最小覆盖字串

滑动窗口问题。整体思路就是两个map,一个mapt记录的是目标字符串,maps表示滑动窗口得map,每次对比两个map就好。滑动窗口时,先更新窗口右边界,左边界默认为0,不断更新右边,直到窗口满足条件。

  1. 窗口满足要求就每次更新结果,结果left和len。就可以得到字符串
  2. 满足条件基础上开始尝试缩小窗口,因为题目要求最小,因此移动左边界,每次要判断最左边元素是不是在目标mapt中,如果在,需要改变maps,因为会涉及移动窗口是否满足条件。如果不再,那就不用管,说明可以缩小的。
class Solution {
public:
    unordered_map<char, int> s_map, t_map;
    bool check(){
        for(const auto& m:t_map){
            if(s_map[m.first] < m.second)
                return false;
        }
        return true;
    }
    string minWindow(string s, string t) {
        for(const auto& m:t)
            t_map[m]++;
        int l=0, r=-1;
        int len = INT_MAX, anl=-1, anr = -1;
        cout<<s.size()<<endl;
        cout<<int(s.size())<<endl;
        while(r<int(s.size())){
            if(t_map.find(s[++r]) != t_map.end()) //滑动右边边界
                s_map[s[r]]++;
            while(check() && l<=r){ //满足条件的字符串,开始滑动左边边界
                if(r-l+1 < len){  //长度更小,更新即可
                    len = r-l+1;
                    anl = l;
                }
                if(t_map.find(s[l]) != t_map.end()) //左边字母在目标t中
                    s_map[s[l]]--; //s—map弹出
                l++;    //如果上一步左边字母不在t中,smap不需要改变,l++即可。
            }
        }
        return anl==-1 ? string() : s.substr(anl, len);
    }
};

13. 53 最大子数组和

解法一:遍历求和,因为要求得最大子数组和,因此如果目前和已经是负数,那就说最终的子数组一定不会有前面的,因为负数会影响后面的子数组之和。因此,需要每次遍历加到和为负。那就清空为0,重新开始求和,就表示更新了子数组的左边界。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int result = INT_MIN;
        int add = 0;
        for(int i=0; i<nums.size(); i++){
            add += nums[i];
            if(add>result)
                result = add;
            if(add<0) //目前子数组和为负,更新左边界。
                add = 0;
            
        }
        return result;
    }
};

解法二,动态规划,每次求和更新dp,dp就在于是求和之后的大,还是本身num大,其实本质也是看之前和是否为负数。

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int result = nums[0];
        int add;
        for(int i=0; i<nums.size(); i++){
            add = max(add+nums[i], nums[i]);
            result = max(add, result);
        }
        return result;
    }
};

14. 56 合并区间

贪心问题,每次都取最优,先sort排序,就按照左边界从小到大排序了,因此结果得第一个区间左边界可以确定,就在于右边界。合并就是看区间有没有重叠,也就是第一个区间得右边界和第二个区间的左边界。现在结果已经放了第一个区间,那就从第二个区间(i=1)开始遍历,看看i区间的左边界和结果中back(最右边得区间)右边界大小,如果大于,那就说明没重叠,结果back不需要更新,可以确定,将i区间push即可。如果小于,说明有重叠,那么合并之后的右边界取决于,第两个右边界谁大!思路就清晰了。

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end());
        vector<vector<int>> result;
        result.emplace_back(intervals[0]);
        for(int i=1; i<intervals.size(); i++){
            if(intervals[i][0] <= result.back()[1])
                result.back()[1] = max(result.back()[1], intervals[i][1]);
            else{
                result.emplace_back(intervals[i]);
            }
        }
        return result;
    }
};

15. 238 除自身以外数组的乘积

典型数组前缀和问题,乘积包括两部分,前面乘积和后面乘积,例如,第i处的结果就是i-1之前的乘积和i+1之后的乘积(闭区间),因此求两个数组,一个前缀乘积,一个后缀乘积,就可以得到所有结果

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int len = nums.size();
        vector<int> Lnum(len), Rnum(len);
        vector<int> result(len);
        Lnum[0] = nums[0];
        for(int i=1;i<len-1; i++){
            Lnum[i] = Lnum[i-1]*nums[i]; //前缀乘积
        }
        Rnum[len-1] = nums[len-1];
        for(int i=len-2; i>=0; i--)
            Rnum[i] = Rnum[i+1]*nums[i]; //后缀乘积
        for(int i=1;i<len-1;i++){
            result[i] = Rnum[i+1] * Lnum[i-1]; //求结果
        }
        result[0] = Rnum[1]; //两个边界值需要特殊考虑
        result[len-1] = Lnum[len-2];
        return result;
    }
};

16. 41 缺失的第一个数

这题真抽象,非得n时间复杂度和1空间复杂度,第一次做的时候没考虑空间复杂度,零申请了n大小用于存储哈希,思路很简单,就是查每个数据的+1是否存在,不存在,那么i+1就是结果(缺失得第一个正数)

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        map<int,int> ma;
        int result = 0;
        for(const auto& n :nums){
            if(n>0 && ma.find(n)==ma.end())
                ma.insert(make_pair(n,1));
        }
        if(ma.find(1) == ma.end()){
                result = 1;
                return result;
        }
        for(const auto& m : ma){
            if(ma.find((m.first)+1) == ma.end()){ //找每个数的+1是否存在
                result = (m.first)+1;
                cout<<m.first<<endl;
                break;
            }
        }
        return result;
    }
};

虽然能通过,但不符合题目要求的常数空间复杂度,所以需要思考进化版,其实,一定是从正数1开始找,因此从第一个正数与1对比,第二个与2对比,哪个不满足,哪个就是result。不过记得去重。但这样需要先排序 ,时间复杂度又超了

class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int result = 1, i=0;
        sort(nums.begin(), nums.end());
        while(i<nums.size()){  //找到第一个正数
            if(nums[i]<=0){
                i++;
                continue;
            }
            break;
        }
        while(i<nums.size()){
            if(nums[i]==result){ //对比成功
                result++;
                while(i+1<nums.size() && nums[i] == nums[i+1]){ //去重剪枝
                    i++;
                }
                i++;
                continue;
            }
            return result; //返回没有对比成功的数
        }
        return result; //都对比成功,result变成+1,返回即可
    }
};

就这样吧,这才是正常时间和空间权衡的做法,leetcode要求的,太极限了。

17. 73 矩阵置零

这道题很简单了,如果某个为0,记录下来它的行列号,最后在遍历,如果是之前记录的行列号,那就把值变为0就好。用两个标志数组做记录,一个表示行,一个表示列

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        //vector<vector<bool>> flag(matrix.size(), vector<bool> (matrix[0].size(), false));
        vector<bool> row(matrix.size()), col(matrix[0].size());
        for(int i=0;i<matrix.size();i++)
            for(int j=0;j<matrix[0].size();j++)
                if(matrix[i][j] == 0)
                    row[i] = col[j] = true; //是0,记录两个标记数组
        for(int i=0;i<matrix.size();i++)
            for(int j=0;j<matrix[0].size();j++){
                if(row[i] ||  col[j]){ //如果满足某个标记数组
                   matrix[i][j] = 0;
                }
            }
        return;
    }
};

18. 54螺旋矩阵

纯纯遍历边界考验,上下左右四个边界值,按照顺时针方向遍历,每次遍历完一个方向,判断是否越界,比如。刚开始从左往右的遍历,说明上面一行遍历完了,因此上界就要往下移动一格,比如从0到1.如果移动后的拆超过了下界,就说明遍历完了。break即可

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> result;
        if(matrix.empty())
            return result;
        int u=0, d=matrix.size()-1, l=0, r=matrix[0].size()-1;
        while(true){
            for(int i=l;i<=r;i++)
                result.push_back(matrix[u][i]);
            if(++u>d)
                break;
            for(int i=u;i<=d;i++)
                result.push_back(matrix[i][r]);
            if(--r<l)
                break;
            for(int i=r;i>=l;i--)
                result.push_back(matrix[d][i]);
            if(--d<u)
                break;
            for(int i=d;i>=u;i--)
                result.push_back(matrix[i][l]);
            if(++l>r)
                break;
        }
        return result;
    }
};

19. 48 旋转图像

顺时针旋转90°,两种做法。首先就是基本的映射,列过去是行,行对应到n-i-1列.这个自己手推举个例子就明白了。
第二种做法就是变换,先行对称变换,再主对角线变换。代码只写了第一个方法的,因为简单

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        // C++ 这里的 = 拷贝是值拷贝,会得到一个新的数组
        auto matrix_new = matrix;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                matrix_new[j][n - i - 1] = matrix[i][j];
            }
        }
        // 这里也是值拷贝
        matrix = matrix_new;

    }
};

20. 240 搜索二维矩阵

基本遍历时间复杂度m*n,优化的话可以每一行采用二分

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        for(const auto& row:matrix){
            auto it = lower_bound(row.begin(), row.end(), target); //二分查找
            if(it != row.end() && *it==target)
                return true;
        }
        return false;
    }
};

这样就马马虎虎,直到我看到了评论一个神仙做法
在这里插入图片描述
于是尝试,只能说,自古网络出大神

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int i = 0, j = matrix[0].size()-1;
        while(true){
            if(matrix[i][j] == target)
                return true;
            else if(matrix[i][j]> target){ //遍历“左子树”
                j--;
                if(j<0) //越界就break
                    break;
            }else{ // 遍历“右子树”
                i++;
                if(i>=matrix.size()) 
                    break;
            }
        }
        return false;
    }
};

相关文章:

  • dav_1_MySQL数据库排查cpu消耗高的sql
  • C语言for循环嵌套if相关题目
  • Flink框架:批处理和流式处理与有界数据和无界数据之间的关系
  • 数据库视图讲解(view)
  • 8.3.1 MenuStrip(菜单)控件
  • 基于window11安装NACOS2.5.1的简介
  • Spark Core
  • 【Axure元件分享】移动端滑动拨盘地区级联选择器
  • 【c++11】c++11新特性(上)(列表初始化、右值引用和移动语义、类的新默认成员函数、lambda表达式)
  • 在Windows上安装Ubuntu24.04虚拟机
  • 全局端对端问题及应对方案
  • STM32F103VET6 芯片的主Flash 内存空间大小计算
  • 虚幻5的C++调试踩坑
  • 危险化学品标志速查手册(27个完整版)
  • 文件备份程序中的线程池管理:为何限制子线程数量?
  • Linux Makefile-概述、语句格式、编写规则、多文件编程、Makefile变量分类:自定义变量、预定义变量
  • feign 调用返回值为void接口,抛出了异常也不报错问题处理
  • 【C++】模板进阶
  • 编程中,!! 双感叹号的理解
  • 华为数字芯片机考2025合集1已校正
  • 有哪些建设工程类网站/福州seo排名公司
  • 如何做行业网站/动态网站建设
  • 网站 改域名/怎样建立网站免费的
  • 成人家装设计培训学校/专业优化网站排名
  • 网站建设滕州信息港/seo自媒体运营技巧
  • 招聘网页制作人员/深圳seo公司排名