leetcode 热题 100(部分)C/C++

news2024/11/19 6:22:15

leetcode 热题 100

双指针

盛最多水的容器 【mid】【双指针】

在这里插入图片描述
思路:
好久没写代码sb了,加上之前写的双指针并不多,以及有点思维定势了。我对双指针比较刻板的印象一直是两层for循环i,j,初始时i,j都位于左界附近,但是对于第i次的内层循环,j只需要从第i-1次内层循环停下时的j开始循环,即内层的循环变量j一直在增加,而不会减少,故双指针复杂度O(n)
然鹅,本题利用双指针l,r,初始分别位于左界和右界,之后++l--r,这样子移动。
至于如何移动,写出容器容量公式便很容易想出V=(r - l - 1) * min(height[l], height[r])。为取得Vmax,考虑无论移动l or r,都会使得宽d = r - l - 1 变小,故考虑如何使得min(height[l], height[r])变大,容易发现应该移动height小的那一个。
AC代码

class Solution {
public:
    int maxArea(vector<int>& height) {
        int n = height.size();
        int l = 0, r = n - 1;
        int s = (r - l) * min(height[l], height[r]);
        int res = s;
        while(l < r)
        {
            if(height[l] < height[r]) ++l;
            else --r;
            s = (r - l) * min(height[l], height[r]);
            res = max(res, s);
        }
        return res;
    }
};

三数之和 【mid】【双指针】

在这里插入图片描述
思路:
与上一题类似。
先排序,之后搜一遍。
对于nums[i],需要从右边找出两个数字使得和为-nums[i]
双指针l,r初始分别为左界和右界,据nums[l] + nums[r]-nums[i]的大小关系决定移动哪个指针即可。
另外对于去重,可直接通过set逃课。
AC代码

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        vector<vector<int>> res;
        set<vector<int>> st;
        sort(nums.begin(), nums.end());

        int a, sum, last = -5000000;
        int l, r;
        bool flag;
        for(int i = 0; i < n; ++i)
        {
            if(nums[i] == last) continue;
            else
            {
                last = nums[i];
                l = i + 1; 
                r = n - 1;
                a = -nums[i];
                while(l < r)
                {
                    sum = nums[l] + nums[r];
                    if(sum > a) --r;
                    else if(sum < a) ++l;
                    else 
                    {
                        vector<int> vt;
                        vt.push_back(nums[i]);
                        vt.push_back(nums[l]);
                        vt.push_back(nums[r]);
                        st.insert(vt);
                        ++l;
                    }
                }
            }
        }
        for(auto x : st) res.push_back(x);
        return res;
    }
};

接雨水【hard】【双指针/单调栈】

在这里插入图片描述
思路:
【解1】:预处理(对应官方题解dp解法)
考虑每个洼地可容纳的雨水,取决于这个洼地左侧和右侧的最高的高地的较小值。对于每个洼地左侧和右侧的最高的高地,预处理即可。
【解2】:双指针
思路同解1,只不过不做预处理,而是用双指针l,r初始分别为左界右界,移动过程中,分别维护扫过区域的最大值,每次移动最大值较小的那一侧,然后判断移动之后是洼地还是高地,洼地则计算接的雨水,高地则更新单侧最大值。
关于为什么移动最大值较小的那一侧,假设较高的一侧是r
移动r侧,由于移动之后可能是高地or洼地,对于高地,不计算贡献,其实无所谓,但是对于洼地,需要计算贡献,此时贡献取决于洼地两侧最高的高地的较小值,然而左侧最高的高地其实是不确定的,故无法计算。倘若移动是l的一侧,虽然右侧最高的高地也是不确定的,但至少可以确定的是右侧的高地一定比左侧的高,故可以计算正确的贡献。
【解3】:单调栈
构造一个单减栈(栈底>栈顶)。对于单调栈,其实每个元素都会进栈一次。
对于遍历到的当前元素,若<栈顶元素,入栈
否则,说明存在可以积水的洼地,此时需要弹出栈顶元素,可积的雨水取决于洼地的宽度(据两侧高地的距离)及可积雨水的高地,循环处理,直至当前元素可以入栈。(此过程官方视频题解动画容易理解)。
AC代码:
预处理:

class Solution {
public:
    int trap(vector<int>& height) {
        
        int n = height.size();
        int res = 0;
        int lmx[n + 5], rmx[n + 5];
        lmx[0] = height[0], rmx[n - 1] = height[n - 1];

        for(int i = 1; i < n; ++i) lmx[i] = max(lmx[i - 1], height[i]);
        for(int i = n - 2; i >= 0; --i) rmx[i] = max(rmx[i + 1], height[i]); 

        int mx1, mx2, h;
        for(int i = 0; i < n; ++i)
        {
            mx1 = lmx[i], mx2 = rmx[i];
            h = min(mx1, mx2);
            res += (h - height[i]);
        }

        return res;
    }
};

双指针:

class Solution {
public:
    int trap(vector<int>& height) {
        
        int n = height.size();
        int res = 0;
        int l = 0, r = n - 1;
        int lmx = height[0], rmx = height[n - 1];
        
        while(l < r)
        {
            if(lmx < rmx)
            {
                ++l;
                if(lmx > height[l]) res += lmx - height[l];
                else lmx = height[l];
            }
            else
            {
                --r;
                if(rmx > height[r]) res += rmx - height[r];
                else rmx = height[r];
            }
        }

        return res;
    }
};

单调栈:

class Solution {
public:
    int trap(vector<int>& height) {
        
        stack<int> st;
        int n = height.size();
        int res = 0;
        
        for(int i = 0; i < n; ++i)
        {
            while(!st.empty() && height[i] > height[st.top()])
            {
                int tp = st.top();
                st.pop();
                if(st.empty()) break;
                int l = st.top();
                int d = i - l - 1;
                int h = min(height[l], height[i]) - height[tp];
                res += d * h;
            }
            st.push(i);
        }

        return res;
    }
};

子串

最小覆盖字串【hard】【双指针/滑动窗口】

在这里插入图片描述
思路:
先找出左边界为字符串s的左边界且能覆盖t的最小子串。
之后,利用双指针/滑动窗口的思想,交替移动左右指针l,r
具体规则如下:
若当前子串能够覆盖,则++l,否则++r,每次移动后判断新的子串是否能够覆盖并且是否变小,保存最小能覆盖的子串长度以及其左右边界l,r
关于如何判断,只需在移动的过程中维护好如下容器or变量便显而易见。

map<char, int> mp; 			  //hash,'a'~'z'对应1~26,'A'~'Z'对应27~52
int cnt_s[64]				  //cnt_s[i]表示当前子串s[l~r]中i对应的字母个数,cnt_t[i]表示
int cnt_t[64];				  //cnt_t[i]表示t串中i对应的字母一共有几个
int num = 0, cnt = 0;		  //num表示t串一共有多少个不同的字母,cnt表示当前子串s[l~r]已经覆盖了t中的字母个数
int l, r;					  //当前正在处理的子串s[l~r]
int min_l, min_r, mi = inf;   //目前所有处理的子串中能覆盖的最小子串的左右边界及长度

AC代码:

class Solution {
public:
    string minWindow(string s, string t) {

        const int inf = 0x3f3f3f3f;
        int len1 = s.size(), len2 = t.size();
        map<char, int> mp;
        int cnt_s[64], cnt_t[64];
        int num = 0, cnt = 0;
        int l, r, min_l = 0, min_r = 0, mi = inf; 
        string str;
        
        for(char ch = 'a'; ch <= 'z'; ++ch) mp[ch] = ch - 'a' + 1;
        for(char ch = 'A'; ch <= 'Z'; ++ch) mp[ch] = ch - 'A' + 1 + 26;

        for(int i = 0; i < len2; ++i) ++cnt_t[mp[t[i]]];
        for(int i = 1; i < 55; ++i) if(cnt_t[i]) ++num;
        
        l = 0, r = -1;
        while(r < len1)
        {
            if(cnt == num)
            {
                if(cnt_s[mp[s[l]]] == cnt_t[mp[s[l]]]) --cnt;
                --cnt_s[mp[s[l++]]];
            }
            else
            {
                ++cnt_s[mp[s[++r]]];
                if(cnt_s[mp[s[r]]] == cnt_t[mp[s[r]]]) ++cnt;
            }

            if(cnt == num && r - l + 1 < mi)
            {
                mi = r - l + 1;
                min_l = l, min_r = r;
            } 
        }

        if(mi == inf) return "";
        else 
        {
            str = s.substr(min_l, mi);
            return str;
        }
    }
};

普通数组

轮转数组【mid】【数论/gcd】

在这里插入图片描述
思路
给出空间O(1),且不用reverse的方法。(官方题解推导比较清楚)。
维护两个变量postmp,分别表示现在的位置和对应位置上的值,由此,更新只需pos = (pos + k) % n,swap(nums[pos], tmp),当pos回到最开始的位置时,我们称这是完成了一趟修改,此时应该停下,然鹅有的元素并没有遍历到。(只将下标为gcd(k,n)的元素遍历了,可证,另见类似题目,2015ICPC沈阳,跳青蛙🐸容斥),故需要将pos+1,再依次为开始,继续上述操作,直至回到开始位置并遍历全部位置。
接下来考虑需要几趟(根据上面结论,其实可知需要gcd(n,k)趟)。假设转了a圈,遍历了b个元素,则an=bk,故an一定是n,k的倍数,又因为我们在第一次回到起点时便结束了,故a应尽可能的小,由此an=lcm(n,k),故b=lcm(n,k)/k,即需要遍历的趟数cnt=n/b=gcd(n,k)
AC代码:

class Solution {
public:

    int gcd(int a, int b)
    {
        return b == 0 ? a : gcd(b, a % b);
    }

    void rotate(vector<int>& nums, int k) {
    
        int n = nums.size();
        k %= n;
        int cnt = gcd(n, k);

        for(int i = 0; i < cnt; ++i)
        {
            int pos = i;
            int tmp = nums[i];
            while(true)
            {
                pos = (pos + k) % n;
                swap(nums[pos], tmp);
                if(pos == i) break;
            }
        }
    }
};

缺失的第一个正数 【Hard】【置换/原地哈希】

在这里插入图片描述
思路:
【方法1】:置换(类似于上一个题目)

  • nums[i]<=0||nums[i]>n的元素统一处理。(设置为较大值或数组中出现过的某个1~n的值)。
  • 对于x = nums[i],考虑swap(nums[i], nums[x-1]),即将nums[i]放到他应该在的位置上,如此循环下去,但是注意到当nums[i] == nums[x-1]时会进入死循环。
  • 所有1~n之间的数字都应当被放到对应的位置上,故最后遍历一遍数组,找到第一个不在对应位置之上的元素,便可知答案。
    【方法2】:改进开bool型数组,需要空间复杂度O(n),原始数组nums同时充当bool数组(原地哈希)
    首先,给出开bool数组的方法,开一个大小为n+5bool数组flag,flag[i]=true表示i出现,为此只需要找第一个flag[i]==falsei即可。
    改进方法如下:
  • 同方法1。
  • 通过第一步nums数组中应当只有正数。此时,对于一个1~n之间的数字x,我们就将nums[x - 1]中的元素修改为-nums[x - 1](主要不要多次修改,以防负负得正)。即nums数组中元素的正负代表上述bool型数组的truefalse,绝对值代表对应元素的值。
  • 与方法1类似。

AC代码:
【方法1】:置换

class Solution {
public:
    int firstMissingPositive(vector<int>& nums)
    {
        int n = nums.size();
        
        for(int i = 0; i < n; ++i) 
            while(nums[i] > 0 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) 
                swap(nums[i], nums[nums[i] - 1]);
       
       for(int i = 0; i < n; ++i) if(nums[i] != i + 1) return i + 1;
       
       return n + 1;
    }
};

【方法2】:原地哈希

class Solution {
public:
    int firstMissingPositive(vector<int>& nums)
    {
        int n = nums.size();
        int x;

        bool flag = false;
        for(auto x: nums) if(x == 1) flag = true;
        if(!flag) return 1;

        for(int i = 0; i < n; ++i) if(nums[i] <= 0 || nums[i] > n) nums[i] = 1;

        for(int i = 0; i < n; ++i)
        {
            x = abs(nums[i]) - 1;
            nums[x] = -abs(nums[x]);
        }

        for(int i = 0; i < n; ++i) if(nums[i] > 0) return i + 1;

        return n + 1;
    }

};

矩阵

矩阵置零【mid】【原地/随机】

在这里插入图片描述
思路:
【方法1】:原地
比较容易想到,开一个长度为n和长度为mbool型数组,分别表示第i行和第j列是否有0。这样空间复杂度O(n+m)。优化方法是用原数组的第一行和第一列充当这个bool数组。
用两个bool型变量colrow表示第1列,第1行是否有0,之后对于matrix[i][j]==0,将matrix[i][0]matrix[0][i]标记为0
【方法2】:随机
这个问题的关键是,只会把一开始就有的0所在的行和列全部置为0,后来的出现0不会操作。容易想到的一个思路就是先把有0的行和列中不是0的数字置为一个奇怪的数字(比如inf或是-inf),最后再把这些数改为0,但是发现取值为int的最小值到最大值,所以不行。但是n,m最大为200,随机生成的一个int在矩阵中出现的概率极低,故我们随机生成一个数字作为上述中的奇怪数字即可。(当然是需要判断的,如果运气不好,随机生成的数字正好出现了,就再随机生成一个)

AC代码:
【方法1】:原地

class Solution {
public:

    void setZeroes(vector<vector<int>>& matrix) {

        int n = matrix.size();
        int m = matrix[0].size();
        bool col = false, row = false; //column = 列,row = 行
        
        for(int i = 0; i < n; ++i)
        {
            if(!matrix[i][0])
            {
                col = true;
                break;
            }
        }

        for(int i = 0; i < m; ++i)
        {
            if(!matrix[0][i])
            {
                row = true;
                break;
            }
        }

        for(int i = 1; i < n; ++i)
        {
            for(int j = 1; j < m; ++j)
            {
                if(!matrix[i][j]) matrix[i][0] = matrix[0][j] = 0;
            }
        }

        for(int i = 1; i < n; ++i)
        {
            for(int j = 1; j < m; ++j)
            {
                if(!matrix[i][0] || !matrix[0][j]) matrix[i][j] = 0; 
            }
        }

        if(col) for(int i = 0; i < n; ++i) matrix[i][0] = 0;
        if(row) for(int i = 0; i < m; ++i) matrix[0][i] = 0;
    }
};

【方法2】:随机

class Solution {
public:

    int st_rand(vector<vector<int>>& matrix)
    {
        int xx = matrix.size();
        int yy = matrix[0].size();
        srand(time(NULL));
        int st;
        bool flag;
        while(true)
        {
            st = rand();
            flag = false;
            for(int i = 0; i < xx; ++i)
            {
                for(int j = 0; j < yy; ++j)
                {
                    if(matrix[i][j] == st) 
                    {
                        flag = true;
                        break;
                    }
                }
                if(flag) break;
            }
            return st;
        }
    }

    void work(vector<vector<int>>& matrix, int x, int y, int st)
    {
        int xx = matrix.size();
        int yy = matrix[0].size();
        for(int i = 0; i < xx; ++i) if(matrix[i][y] != 0) matrix[i][y] = st;
        for(int i = 0; i < yy; ++i) if(matrix[x][i] != 0) matrix[x][i] = st;
    }

    void setZeroes(vector<vector<int>>& matrix) {

        int xx = matrix.size();
        int yy = matrix[0].size();
  
        int st = st_rand(matrix);
        for(int i = 0; i < xx; ++i)
        {
            for(int j = 0; j < yy; ++j)
            {
                if(matrix[i][j] == 0) work(matrix, i, j, st);
            }
        }

        for(int i = 0; i < xx; ++i)
        {
            for(int j = 0; j < yy; ++j)
            {
                if(matrix[i][j] == st) matrix[i][j] = 0;
            }
        }

    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1570741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL数据库 数据库基本操作(三):表的增删查改(中)

1. 数据库的约束 1.1 约束类型(一般发生于表的创建中) NOT NULL - 指示某列不能存储 NULL 值。UNIQUE - 保证某列的每行必须有唯一的值。DEFAULT - 规定没有给列赋值时的默认值。PRIMARY KEY - NOT NULL 和 UNIQUE 的结合。确保某列&#xff08;或两个列多个列的结合&#xf…

【CSS】浮动笔记及案例

CSS浮动 1. 认识浮动 float属性可以指定一个元素沿着左侧或者是右侧放置&#xff0c;允许文本和内联元素环绕它 float属性最初只使用文字环绕图片但却是早起CSS最好用的左右布局方案 绝对定位、浮动都会让元素脱标&#xff0c;以达到灵活布局的目的可以通过float属性让元素脱…

FTP,NFS,SSH服务安装和应用,遇到的问题

文章目录 一、FTP是什么&#xff1f;二、NFS是什么&#xff1f;三、SSH是什么&#xff1f;四、FTP&#xff0c;NFS&#xff0c;SSH安装(Ubuntu 操作系统)1.FTP安装2.NFS安装3.SSH服务安装 五、FTP&#xff0c;NFS&#xff0c;SSH应用1.FTP应用12.FTP应用23.NFS应用4.SSH应用 六…

【快速解决】python缺少了PyQt5模块的QtMultimedia子模块

目录 问题描述 问题原因 解决方法 成功示范 问题描述 Traceback (most recent call last): File "d:\桌面\python项目\DesktopWords-master\main.py", line 4, in <module> from PyQt5.QtMultimedia import QMediaPlayer, QMediaContent ModuleNotFoundEr…

动态规划刷题(算法竞赛、蓝桥杯)--线段(线性DP)

1、题目链接&#xff1a;P3842 [TJOI2007] 线段 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include <bits/stdc.h> using namespace std; const int N20010; int a[N][2],f[N][2]; //a[i][0]表示l[i],a[i][1]表示r[i] int dis(int a,int b){return abs(a-b); } int…

计算机网络-HTTP相关知识-基础

HTTP基础 基本概念&#xff1a;HTTP是一种计算机之间交流通信的规范&#xff0c;它允许数据在两点之间传输&#xff0c;这个过程可以包括中转或接力。HTTP不仅仅包括文本&#xff0c;还可以包括图片、音频等超文本。状态码&#xff1a;HTTP状态码分为五类&#xff1a; 2xx&…

12-项目部署_持续集成

项目部署_持续集成 1 今日内容介绍 1.1 什么是持续集成 持续集成&#xff08; Continuous integration &#xff0c; 简称 CI &#xff09;指的是&#xff0c;频繁地&#xff08;一天多次&#xff09;将代码集成到主干 持续集成的组成要素 一个自动构建过程&#xff0c; 从…

BLE协议—HID

BLE协议—HID HID设备HOGP&#xff08;HID Over GATT Profile&#xff09;HID服务HID infoHID ModeHID ReportHID MAP HID设备 HID&#xff08;Human Interface Device&#xff0c;人机接口设备&#xff09;是USB设备中常用的设备类型&#xff0c;是直接与人交互的USB设备&…

PostgreSQL 文章下架 与 热更新和填充可以提升数据库性能

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;…

keycloak - 鉴权VUE

目录 一、前言 1、背景 2、实验版本 二、开始干活 1、keycloak配置 a、创建领域(realms) b、创建客户端 c、创建用户、角色 2、vue代码 a、依赖 b、main.js 三、未解决的问题 目录 一、前言 1、背景 2、实验版本 二、开始干活 1、keycloak配置 a、创建领域(r…

常见的数据库操作

一、查看数据库及表 1.查看当前 DMBS 中有哪些数据库 show databases; (首先进入数据库) 2.查看当前数据库中有哪些表 show tables; 3.查看表的结构&#xff08;信息&#xff09; describe 表名;如&#xff1a;describe orders; 二、创建删除数据库 1.创建数据库 create databa…

三栏布局——面试/笔试题

目录 三栏布局(两端指定宽度&#xff0c;中间自适应)三栏布局(平均分布) 三栏布局(两端指定宽度&#xff0c;中间自适应) 只介绍简单的写法&#xff0c;圣杯布局之类的比较复杂&#xff0c;实际上越简单越好&#xff0c;所以复杂的就不介绍了 flex布局 <!DOCTYPE html>…

博客搭建(hexo+github)

简介 搭建完成网站的如下所示 https://polarday.top/ 使用github托管博客&#xff0c;完全免费不需要购买服务器 博客框架&#xff1a;hexo hexo主题&#xff1a;ICARUS 图床&#xff1a;githubPicGo 编辑&#xff1a;vscode 为什么使用hexo框架&#xff1f;因为hexo是静态框…

阿里云99元服务器性能测评_CPU内存_带宽_系统盘

阿里云服务器99元一年配置为云服务器ECS经济型e实例&#xff0c;2核2G配置、3M固定带宽和40G ESSD Entry系统盘&#xff0c;新用户和老用户均可买&#xff0c;续费不涨价依旧是99元一年&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说下阿里云99元服务器性能测评&#xff…

opencv使用问题记录一二

opencv介绍 opencv是一个计算机视觉处理软件库&#xff0c;拥有强大的功能和高效的性能。 但是由于早期版本的原因&#xff0c;存在一些与目前主流使用不兼容的问题 问题与解决 RGB通道顺序 一般图片处理类库的通道顺序就是RGB&#xff0c;但是opencv的是反过来的&#xf…

OpenCV入门例程:裁剪图片、模糊检测、黑屏检测

初级代码游戏的专栏介绍与文章目录-CSDN博客 我的github&#xff1a;codetoys&#xff0c;所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。 这些代码大部分以Linux为目标但部分代码是纯C的&#xff0c;可以在任何平台上使用。 本例程运行环境为CentOS7&…

vLLM介绍

vLLM是伯克利大学LMSYS组织开源的大语言模型高速推理框架&#xff0c;旨在极大地提升实时场景下的语言模型服务的吞吐与内存使用效率。vLLM是一个快速且易于使用的库&#xff0c;用于 LLM 推理和服务&#xff0c;可以和HuggingFace 无缝集成。vLLM利用了全新的注意力算法「Page…

SimpleMind Pro 2.3.4中文激活版 思维导图

SimpleMind是一款设计精美、功能齐全的跨平台思维导图软件。它是一个功能强大的优秀的软件工具&#xff0c;可用于头脑风暴的跨平台思维导图。当你和你的朋友坐在一起讨论不同的想法时&#xff0c;你可以很容易地记录下它们&#xff0c;并随时修改它们。思维导图可以帮助你思考…

win10电脑无线网卡优化

近期win10会频繁断网&#xff0c;无任何规律。目前整理搜索后使用以下两种方法优化网卡&#xff0c;更改配置后断网问题得到有效改善。 方法一&#xff1a;在【电源管理】中取消勾选【允许计算机关闭此设备以节约电源】 方法二&#xff1a;【Preferred enable】修改为prefer 5…

c# wpf style 简单试验

1.概要 wpf style 用来控制控件的样式 2.代码 <Window x:Class"WpfApp2.Window5"xmlns"http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x"http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d"http://schemas.…