数组 704.二分查找、59.螺旋矩阵II、27.移除元素、977.有序数组的平方、209.长度最小的子数组、904. 水果成篮、76. 最小覆盖子串、1004. 最大连续1的个数 III、3. 无重

704.二分查找

题目链接

(1)文字讲解:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html
(2)视频讲解:https://www.bilibili.com/video/BV1fA4y1o715
(3)题目链接:https://leetcode.cn/problems/binary-search/

有序数组+无重复元素,考虑二分

看到代码随想录之前的想法

二分只记得基本思路了,但是对于边界处理完全忘记了。主要困惑点在于:

  • r到底等于length-1还是length?
  • while中到底是<还是<=?

看到代码随想录之后的想法

有两种解法,但是思路是不变的,区间是不变量
(1)如果是[a,b]
那么r=length-1,毕竟你要取到r,让nums[r]有意义。while就是<=,因为这里l=r是有可能的。
此外,每次迭代,l=m+1或者r=m-1,因为如下图:
在这里插入图片描述
这里可以得知,nums[m]在上一轮就check过了,没必要check一遍了,所以为了还是保持左闭右闭,每一轮迭代,l=m+1或者r=m-1。
(2)如果是[a,b)
那么r=length,毕竟取不到r。while就是<,因为这里l=r是不可能的。
此外,每次迭代,l=m+1或者r=m-1,因为如下图:
在这里插入图片描述
nums[m]究竟行不行,其实上一轮就已经check过了。我们还是想要保持左闭右开的空间,那么r就要故意取大1位,这样所有这一半需要被check的都囊括进下一轮中

本题难点

想明白区间是不变量,推荐画图自己模拟一遍。

代码

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0;
        int r = nums.size()-1;
        while(l <= r){
            //int m = l + (r - l)/2
            int m = (l + r)/2;
            if(nums[m] == target){
                return m;
            }else if(nums[m] < target){
                l = m+1;
            }else if(nums[m] > target){
                r = m-1;
            }
        }
        return -1;
    }
};
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0;
        int r = nums.size();
        while(l < r){
            //int m = l + (r - l)/2
            int m = (l + r)>>1;
            if(nums[m] == target){
                return m;
            }else if(nums[m] < target){
                l = m+1;
            }else if(nums[m] > target){
                r = m;
            }
        }
        return -1;
    }
};

59.螺旋矩阵II

题目链接

(1)文字讲解:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html
(2)视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
(3)题目链接:https://leetcode.cn/problems/spiral-matrix-ii/

看到代码随想录之前的想法

模拟题,但是我想的很复杂,控制边界条件很麻烦。

看到代码随想录之后的想法

最重要的是领悟“循环不变量”的思想。
1.绕圈,先分成一圈一圈绕,这样就有了loop这个变量,loop=n/2,记录绕多少圈。
2.一个圈有四个方向,按照题目的说法,一个正方形,先从上边开始从左到右,再从右边开始从上到下,再从下边开始从右到左,最后左边开始从下到上。每一圈都是从左上角开始,那么每一圈就需要更新左上角的坐标。于是有了starx,stary这两个变量,每一圈都++;
3.每一圈,每个方向,都设定成左闭右开,这样for循环的终止条件就是< n-offset,offset是每一次圈缩小的变量,每圈++。
4.在注意一下n如果是奇数,那么最中间的空格需要单独设定,于是有了middle这个变量。

本题难点

规定每一圈左闭右开,严格遵守循环不变量。

代码

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n,vector<int>(n,0));
        int starx=0, stary=0;
        int loop = n/2;
        int middle = n/2;
        int offest = 1;
        int count = 1;
        while(loop--){
            int i = starx;
            int j = stary;

            for(; j < n-offest; j++){
                res[starx][j] = count;
                count++;
            }
            for(; i < n-offest; i++){
                res[i][j] = count;
                count++;
            }
            for(; j > stary; j--){
                res[i][j] = count;
                count++;
            }
            for(; i > starx; i--){
                res[i][j] = count;
                count++;
            }
            offest++;
            starx++;
            stary++;
        }
        if(n%2){
            res[middle][middle] = count;
        }
        return res;
    }
};

27.移除元素

题目链接

(1)文字讲解:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html
(2)视频讲解:https://www.bilibili.com/video/BV12A4y1Z7LP
(3)题目链接:https://leetcode.cn/problems/remove-element/

看到代码随想录之前的想法

暴力,开一个新数组然后复制和target不一样的。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        vector<int> res;
        for(int i = 0; i < nums.size(); i++){
            if(val != nums[i]){
                res.push_back(nums[i]);
            }else {
                continue;
            }
        }
        nums = res;
        return res.size();
    }
};

然后才看到不要另开数组,我就没办法了。

看到代码随想录之后的想法

双指针思想。慢指针用来记录需要被更新的target的位置,快指针寻找能够更新的后面的元素。一开始快慢指针同时出发,一旦碰到target,慢指针留下,快指针继续。

本题难点

一个双指针思想。还有一个其实是if(val != nums[fast])。这里我一开始也想到了双指针,但是我会让fast++,slow++,如果nums[i] == target,就只让fast++,nums[i] != target, nums[slow] = nums[target],slow++,fast++.
但是fast不管想不想等都要++,还不如直接让fast顶替i,如果不等于就让nums[slow] = nums[target] slow++,如果相等那就continue。总之这个代码在这个处理上很精巧,我想不到。

代码

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;
        for(int fast = 0; fast < nums.size(); fast++){
            if(val != nums[fast]){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
};

977.有序数组的平方

题目链接

(1)文字讲解:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html
(2)视频讲解:https://www.bilibili.com/video/BV1QB4y1D7ep
(3)题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

看到代码随想录之前的想法

最直观的想法肯定是每个平方之后使用sort函数,但是要求使用O(n)的方法,这样就不行。

看到代码随想录之后的想法

双指针思想。其实如果数组有负数的话,最大的数只可能出现在数组的两边,中间的一定是最小的。所以可以设置两个指针,在数组两端,两个指针都往中间走。开一个新数组存平方后的结果。每次都比较指针的数的平方,哪个大就存在新数组里(倒序),然后往中间移动一位。直到两个指针相遇。

本题难点

最大的数只可能出现在两边,由此想到双指针思想。

代码

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> res(nums.size(),0);
        int k = nums.size()-1;
        int i=0,j=nums.size()-1;
        while(k>=0){
            if(nums[i]*nums[i] >= nums[j]*nums[j]){
                res[k] =  nums[i]*nums[i];
                i++;
            }else{
                res[k] =  nums[j]*nums[j];
                j--;
            }
            k--;
        }
        return res;
    }
};

滑动窗口

以下题目都是滑动窗口,需要区分是最大滑动窗口和最小滑动窗口。最大的区别就是,最小滑动窗口跟随i更新,while的check条件是满足题目条件(也就是在while内更新),最大滑动窗口跟随j更新,while的check条件是不满足题目条件(也就是在while外更新),具体请看这个总结:
https://leetcode.cn/problems/fruit-into-baskets/solutions/1437444/shen-du-jie-xi-zhe-dao-ti-he-by-linzeyin-6crr/

209.长度最小的子数组

题目链接

(1)文字讲解:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html
(2)视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
(3)题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/

看到代码随想录之前的想法

1.理解错题目意思了,以为不仅仅元素位置要是连续的,大小也必须是严格递增1或者递减1,所以添加了flag这个判断条件。res这个vector也根本没有存在的必要,不需要存找到的子序列,只需要存长度就行。
2.其实使用了双指针,我设置i就是为了指出子序列开始的位置,但是因为我少考虑了一种情况:我这里设置的其实是从i开始,一直到有满足条件的子序列出现,j结束,然后i=j,开始重复。但是,实际上这样忽略了一个问题,假设这么一个子序列:【2,3,4】,target=6,那么i=0,j=2的时候满足了,i=j,其实答案不是【2,3,4】,而是【3,4】,所以i应该慢慢+1,而不是直接跳到j的地方。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int flag = 0;//0 没有状态 1 递增序列 2递减序列
        int sum = nums[0];
        int len = 1, reslen = 100001;
        vector<int> res;
        res.push_back(nums[0]);
        for(int i= 0, j = 1; j < nums.size(); j++){
            if(nums[j] == nums[j-1] + 1){
                if(flag == 0 || flag == 1){
                    flag = 1;
                    sum += nums[j]
                    res.push_back(nums[j]);
                    len++;
                    if(sum >= target){
                        if(len < reslen){
                            reslen = len;
                        }
                    }
                    res.clear();
                    res.push_back(nums[j]);
                    len = 1;
                    sum = nums[j];
                    flag = 0;
                }else if(flag == 2){
                    if(sum >= target){
                        if(len < reslen){
                            reslen = len;
                        }
                    }
                    res.clear();
                    res.push_back(nums[j]);
                    len = 1;
                    sum = nums[j];
                    flag = 0;
                }
            }else if(nums[j] == nums[j-1] -1){
                if(flag == 0 || flag == 2){
                    flag = 2;
                    sum += nums[j]
                    res.push_back(nums[j]);
                    len++;
                }else if(flag == 1){
                    if(sum >= target){
                        if(len < reslen){
                            reslen = len;
                        }
                    }
                    res.clear();
                    res.push_back(nums[j]);
                    len = 1;
                    sum = nums[j];
                    flag = 0;
                }

            }else{
                if(sum >= target){
                        if(len < reslen){
                            reslen = len;
                        }
                    }
                res.clear();
                res.push_back(nums[j]);
                len = 1;
                sum = nums[j];
                flag = 0;
            }
        }
    }
};

然后经过反思我写出了第二版,但是依然错了:

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0;
        int len = 100001;
        for(int i=0,j=0; j < nums.size(); j++){
            sum+=nums[j];
            if(sum >= target){
                while(i <= j){
                    if(sum >= target){
                        len = (j-i+1) < len? (j-i+1) : len;
                    }
                    sum -= nums[i];
                    i++;
                }
            }
        }
        return len == 100001? 0 : len;
    }
};

这一版的问题其实和上一版差不多。这一版的想法是i先不动,j增加,直到substr的和已经大于target,j固定住,再来移动i,看看能不能把这个substr缩短。但是我的while条件是i<=j,也就是说,i一直要移动到j的位置上,但是忽略了一种情况就是,i可能移动到一半,substr的和就已经小于target了,如果这题的substr刚好出在此时的i到j+n这一段里面,那么就错了。如下图的案例:
在这里插入图片描述
总之,这样不行。

看到代码随想录之后的想法

滑动窗口的窗口里的条件一定是sum>=target,i,j在哪不重要。

本题难点

理解滑动窗口的判别条件。最小滑动窗口。

代码

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int sum = 0;
        int len = 100001;
        for(int i=0,j=0; j < nums.size(); j++){
            sum+=nums[j];
            while(sum >= target){
                len = (j-i+1) < len? (j-i+1) : len;
                sum -= nums[i];
                i++;
            }
        }
        return len == 100001? 0 : len;
    }
};

904. 水果成篮

题目链接

https://leetcode.cn/problems/fruit-into-baskets/description/

本题难点

最大滑动窗口。

代码

class Solution {
public:
    int totalFruit(vector<int>& fruits) {
        unordered_map<int,int> lz;
        int len = 0;
        for(int i = 0, j= 0; j < fruits.size(); j++){
            lz[fruits[j]]++;
            while(lz.size() > 2){
                auto it = lz.find(fruits[i]);
                it->second--;
                if (it->second == 0) {
                    lz.erase(it);
                }
                i++;
            }
            len = len < (j-i+1) ? j-i+1 : len; 
        }
        return len;
    }
};

76. 最小覆盖子串

题目链接

https://leetcode.cn/problems/minimum-window-substring/

本题难点

最小滑动窗口

代码

class Solution {
public:
    unordered_map<char,int> cnts,cntt;

    bool check() {
        for(auto it=cntt.begin();it!=cntt.end();it++){
            auto its = cnts.find(it->first);
            if(its != cnts.end()){
                if(its->second < it->second){
                    return false;
                }
            }else{
                return false;
            }
        }
        return true;
    }

    string minWindow(string s, string t) {
        int left = 0;
        int len = 100001;
        for(int k = 0; k < t.length(); k++){
            cntt[t[k]]++;
        }
        for(int i = 0, j = 0; j < s.length(); j++){
            cnts[s[j]]++;
            while(check()){
                if(len > j-i+1){
                    len = j-i+1;
                    left = i;
                }
                auto itt = cnts.find(s[i]);
                itt->second--;
                if(itt->second == 0){
                    cnts.erase(s[i]);
                }
                i++;
            }
        }
        if(len != 100001)
        {
            return s.substr(left,len);
        }else{
            return "";
        }
    }
};

1004. 最大连续1的个数 III

题目链接

https://leetcode.cn/problems/max-consecutive-ones-iii/description/

本题难点

最大滑动窗口

代码

class Solution {
public:
    int longestOnes(vector<int>& nums, int k) {
        int sum = 0;
        int len = 0;
        for(int i=0,j=0; j < nums.size(); j++){
            if(nums[j] == 0){
                sum++;
            }
            while(sum > k){
                if(nums[i] == 0){
                    sum--;
                }
                i++;
            }
            len = len > (j-i+1)?len:j-i+1;
        }
        return len;
    }
};

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

题目链接

https://leetcode.cn/problems/longest-substring-without-repeating-characters/

本题难点

最大滑动窗口

代码

class Solution {
public:
    unordered_map<int,int> cnt;
    bool check(){
        for(auto it = cnt.begin(); it != cnt.end(); it++){
            if(it->second >1){
                return false;
            }
        }
        return true;
    }
    int lengthOfLongestSubstring(string s) {
        int len = 0;
        for(int i =0, j =0; j < s.length(); j++){
            cnt[s[j]]++;
            while(!check()){
                auto its = cnt.find(s[i]);
                its->second--;
                if(its->second == 0){
                    cnt.erase(s[i]);
                }
                i++;
            }
            len = len > (j-i+1)?len:(j-i+1);
        }
        return len;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值