代码随想录打卡—day2—【数组】——2. 滑动窗口和模拟

1. 双指针的变种

1.1 双指针

977. 有序数组的平方

最暴力的:平方之后,再sort一下,O(n+nLog(n))不符合要求。

期待一个O(n)解法,一开始想了十分钟一直想着原地升序,我的思路1:首先平方,变成一个降序(S1部分)又升序(S2部分)的数组,对每个左边S1递减的部分,确保其每一个元素后面的所有比它大,S2不用管,但明显思路错误。

思路2: S1先升序,之后L指针1从S1打头,R指针2从S2打头,凡是L指向的大于R就交换,L++,R++。毫无理论依据,乱想的。事实证明错误!

看了视频之后,可以开一个新数组!!!顿悟!那就很简单了

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) 
    {
        vector<int> out;
        int l = 0;
        int r = nums.size() - 1;
        while(l <= r)
        {
            if(abs(nums[l]) > abs(nums[r]))
            {
                out.push_back(nums[l]*nums[l]);
                l++;
            }
            else 
            {
                out.push_back(nums[r]*nums[r]);
                r--;
            }
        }
        l = 0;
        r = nums.size() - 1;
        while(l < r)
        {
           int tmp = out[l];
           out[l] = out[r];
           out[r] = tmp;
           l++;
           r--;
        }
        return out;
    }
};


2 双指针----滑动窗口

209. 长度最小的子数组

我的一开始写法:前缀和+暴力遍历窗口大小(从1...到nums.size()),过了测试样例,但TLE了,时间复杂度是O(n^2)。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) 
    {
        // 来一个前缀和
        vector<int> sum(nums.size()+1);
        sum[0] = 0;
        for(int i = 1; i <= nums.size();i++)
            sum[i] = sum[i - 1] + nums[i - 1];
        
        // 开始处理
        for(int len = 0; len < nums.size(); len++)  //遍历不同的长度
        {
            for(int l = 1; l + len <= nums.size();l++ ) // 一个len 遍历这个nums
            {
                cout << len << ' ' << l << endl;
                int now_sum = sum[l+len] - sum[l-1];
                if(now_sum >= target)
                    return len + 1;
            }
        }
        return 0;
    }
};

看了视频,学习了同向双指针的另一种用法——滑动窗口,NB!(虽然好像以前见过,又忘记了)根据两个指针i、j表示这个窗口的起始和结束位置,重点是最外层的 j 表示窗口的结束位置。时间复杂度是O(n)的。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) 
    {
        int i = 0; //i表示这个滑动窗口的起始位置
        int sum = 0;
        int uu = INT32_MAX;
        for(int j = 0; j < nums.size();j++) //j表示这个滑动窗口的结束位置
        {
            sum += nums[j];
            while(sum >= target)  //左缩窗口
            {
                uu = min(uu,j - i + 1);
                sum -= nums[i++];
            }
        }
        if(uu != INT32_MAX)return uu;
        else return 0;
    }
};

2.1 滑动窗口的拓展

904. 水果成篮 【中等】

呜呜呜,每一个变量的定义一定要想清楚,不然乱试尊的会死,such as 本题的 int left。我的写法: 这个left怎么调都过不了......痛苦,看了题解 是要用哈希表代替 left 这一块的作用?

class Solution {
public:
    int totalFruit(vector<int>& fruits) 
    {
        // 还是双指针-滑动窗口的思想
        // j表示窗口结束位置 i表示窗口开始位置 维护[i,j]是永远满足条件的一个区间
        // left 表示对于结束位置 比如是5 前一个不等于5 的第一个下标 比如 3335 left 期待是0
         
        int result = 1;
        int i = 0;
        vector<int> now;
        now.push_back(fruits[0]);
        int left = 0;
        for(int j = 1; j < fruits.size();j++)
        {
            auto it = find(now.begin(),now.end(),fruits[j]);
            if(it == now.end())  //没找到
            {
                if(now.size() == 2)
                {
                    // cout << i <<  " " << j << endl;
                    result = max(result,j - i );
                    i = left;
                    int tmp = now[0];
                    now[0] = now[1];
                    now[1] = tmp;
                    now.pop_back();
                    now.push_back(fruits[j]);
                    // left = j;
                    cout << "1   " << result << endl; 
                }
                else
                {
                    now.push_back(fruits[j]);
                    result = max(result,j - i + 1 );
                    cout << "2   " << result << endl; 
                    // left = j;
                }
            }
            else  //找到了
            {
                result = max(result,j - i + 1 );
                cout << fruits[j] <<"3   " << result  << ' '; 
                cout << i <<  " " << j << endl;
            }
            if(fruits[j] != fruits[left])left = j;
        }
        return result;
    }
};

 todo

76. 最小覆盖子串 【困难】

 todo

3 一个模拟题

59. 螺旋矩阵 II

下面是我按照初步思路写就AC的代码,思路很简单,确保四个方向依次走到头,头的标准是:以右边走到头为例子就是(右边是边界 || 右边有数了)。按着这个逻辑写完,才发现这个写法正好没有让x,y出out的边界,而且产生了任一个方向左闭右开的效果,和正解不谋而合,并且比carl写的最外层while循环简单,他写的还要算出loop次数+中心额外处理。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) 
    {
        // 初步思路————向 右 下 左 上 四个方向依次走到尽头 直到无路可走?

        vector<vector<int>> out(n,vector<int>(n,0));  //二维数组的初始化大小
        int x = 0;  //当前坐标
        int y = 0;

        int k = 1;
        // 当四周都填了(数字或者无路)
        while((out[x][y] == 0) || ( !(x == 0 || out[x - 1][y] != 0) || !(x == n-1 || out[x+1][y] != 0) || !(y == 0 || out[x][y-1] != 0) || !(y == n-1 || out[x][y+1] != 0)))  
        {
            // 往右边走
            while( !(y == n-1 || out[x][y+1] != 0) && out[x][y] == 0 )
            {
                out[x][y] = k++;
                y++;
            }
            // 往下走
            while( !(x == n-1 || out[x+1][y] != 0) && out[x][y] == 0  )
            {
                out[x][y] = k++;
                x++;
            }
            // 往左边走
            while(!(y == 0 || out[x][y - 1] != 0)&& out[x][y] == 0  )
            {
                out[x][y] = k++;
                y--;
            }
            // 往上走
            while(!(x == 0 || out[x - 1][y] != 0)&& out[x][y] == 0 )
            {
                out[x][y] = k++;
                x--;
            }
            if(out[x][y] == 0 &&  ( (x == 0 || out[x - 1][y] != 0) && (x == n-1 || out[x+1][y] != 0) && (y == 0 || out[x][y-1] != 0) && (y == n-1 || out[x][y+1] != 0)))
            {
                out[x][y] = k;
            }
            
        }
        // cout << '[';
        // for(int i = 0; i < n; i++)
        // {
        //     cout << '[';
        //     for(int j = 0; j < n; j++)
        //         if(j != n-1) cout << out[i][j] << ',';
        //         else cout << out[i][j];  
        //     cout << "],";
        // }
        // cout << ']';
        return out;
    }
};

总结数组:

题型:

【1】二分

【2】双指针(同向和双向)

【3】滑动窗口

【4】模拟,考察代码实现能力


 

todo

还有1道半的滑动窗口拓展

还有2题螺旋矩阵类似的模拟题

感受

滑动窗口的拓展-水果成篮调的很辛苦,其他还可以。大概总共用时4小时

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值