代码随想录day2--双指针、滑动窗口

LeetCode977.有序数组的平方

题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例 1:

输入:nums = [-4,-1,0,3,10]
输出:[0,1,9,16,100]
解释:平方后,数组变为 [16,1,0,9,100]
排序后,数组变为 [0,1,9,16,100]

示例 2:

输入:nums = [-7,-3,2,3,11]
输出:[4,9,9,49,121]

解题思路

·看到题目的第一个想法是使用暴力排序法解决,将每个数据平方后,再进行排序

·这道题比较难想到使用双指针进行求解,笔者也是观看了视频之后才意识到可以使用双指针法进行求解

·观察数组发现,其实数组是有序的,而且负数的平方后也是正数,所以顺序需要进行调整。定义指针i指向起始位置,指针j指向终止位置。定义一个数组result,并且与nums数组大小一致,并且定义变量k使其指向result数组终止位置。

1.暴力求解法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0;i < nums.size();i++){
            nums[i] = nums[i]*nums[i];
        }
        sort(nums.begin(),nums.end());
        return nums;
    }
    
};

2.双指针法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int i = 0,j = nums.size()-1;//定义指针i指向数组起始位置,指针j指向数组终止位置
        int k = nums.size()-1;//定义变量k指向数组终止位置
        vector<int> result;//定义数组result
        result.resize(nums.size());//初始化数组result并且大小与nums数组大小一致
        while(i <= j){
            if(nums[i]*nums[i] > nums[j]*nums[j]){
                result[k] = nums[i]*nums[i];//若nums[i]^2大于nums[j]^2则将nums[i]^2值放入k中
                k--;//k向前移动
                i++;//ii向后移动
            }else{//同上
                result[k] = nums[j]*nums[j];
                k--;
                j--;
            }
        }
        return result;
    }
    
};

难点/易错点

·使用vector定义数组后,一定要初始化数组,否则无法正常使用数组

·while循环中一定是i<=j因为如果没有等于,会出现少比较一个数的情况

总结

笔者过往在遇到这一类问题的第一直觉是使用暴力求解,因为可以直接暴力破解。在未看解法前,从未思考过使用双指针法进行求解。通过书写这道题,加深了对双指针的印象,为解题增加了一个新的思路。


 LeetCode209.长度最小的子数组

题目描述:

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

解题思路

·看到题目的第一个念头是使用分治算法进行求解,但是思考后想起来,分治法求解的是最长子序列,并非求解长度最小的子数组

·依旧是使用暴力求解,使用两个for循环,将所有可能的子序列进行寻找,这样虽然可以写出来,但是时间复杂度是O(n^2),但是题目要求的时间复杂度是O (nlogn),所以暴力求解方法也不行

·在看了题解后最终决定使用滑动窗口进行求解

因为暴力求解不能通过,所以就不进行书写了

滑动窗口法

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;//将result定义成最大值,因为后面需要有min操作
        int i = 0,j = 0;//将初始指针与终止指针先都定义在起点
        int subL = 0;//最短子序列
        int sum = 0;//满足条件的集合
        for(j = 0;j < nums.size();j++){
            sum += nums[j];
            while(sum >= target){//如果集合中的数大于等于目标数
                subL = j-i+1;//最短子序列长度
                result = min(result,subL);//取最短序列

                //数组向右移动
                sum = sum - nums[i];
                i++;
            }
        }
        return result==INT32_MAX?0:result;//如果result的值等于INT32_MAX,说明灭有找到满足条件的子数组,此时返回result的值
    }
};

难点

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置。再暴力求解法中,一个for循环为起始位置,一个for循环为终止位置,用两个for循环完成了一个不断搜索区间的过程。滑动窗口只用一个for循环来表示终止位置。

实现滑动窗口主要需要确定以下三点:

·窗口内是什么

·如何移动起始位置

·如何移动结束位置

窗口就是满足窗口内和大于等于目标值的长度最小的连续子数组

窗口的起始位置如何移动:如果当前窗口的值大于目标值,窗口就需要向前移动(也就是缩小)。

窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。

总结

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而降低时间复杂度。本人从未用过滑动窗口的解法(可能用了也不知道),滑动窗口的代码较简单,但是理解过程比较难,我也是看了三遍题解才懂,学习了一种全新的解题方法。


LeetCode59.螺旋矩阵II

题目描述:

给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。

示例 1:

输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]

示例 2:

输入:n = 1
输出:[[1]]

解题思路:

·看到这道题,第一件事情是要确定好边界值,千万不能使用每条边都遍历一遍,再把重复元素删除,这是吃力不讨好的方法,应该使用左闭右开进行遍历。

·就以示例为例,第一次遍历12,第二次遍历34,第三次遍历56,第四次遍历78,这样可以做到不重不漏,如果遇到奇数个数,那个中间的数进行单独操作

以下是代码

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n,vector<int>(n,0));
        int half = n/2;//矩阵的中间位置
        int offset = 0;//定义初始位置
        int value = 1;//对矩阵内元素进行赋值
        for(int k = 1;k <= half;k++){
            for(int j = offset;j < n-1-offset;j++)
                res[offset][j] = value++;//上行从左到右(左闭右开)

            for(int i = offset;i < n-1-offset;i++)
                res[i][n-1-offset] = value++;//右列从上到下(左闭右开)

            for(int j = n-1-offset;j > offset;j--)
                res[n-1-offset][j] = value++;//下行从右到左(左闭右开)

            for(int i = n-1-offset;i > offset;i--)
                res[i][offset] = value++;//左列从下到上(左闭右开)

            offset++;
        }
        if(n%2 == 1)//若为奇数,则单独给矩阵中间赋值
            res[half][half] = n*n;

        return res;
        
    }

};

总结

加深了对循环不变量原则的印象,每一步循环的边界值一定要判断清楚,判断准确,这样循环才能做到不重不漏,正确的遍历。

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值