二、滑动窗口 模拟

有序数组的平方--双指针

 题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章链接:代码随想录

视频链接:双指针法经典题目 | LeetCode:977.有序数组的平方_哔哩哔哩_bilibili

暴力解法:

将所有元素平方,然后排序,即可得出

时间复杂度:O(nlogn)

public int[] sortedSquares(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        nums[i] *= nums[i];
    }
    Arrays.sort(nums);
    return nums;
}

双指针解法:

因为是一个递增数组且含有负数,平方后的数最大值不是在最左边就是在最右边,可以使用对撞双指针
可以创建一个相同大小的新数组,左右指针相互比较,较大的数放入数组空闲位置的最后一位,且下标往内收,不断重复该过程,直至left>right

时间复杂度:O(n)

public int[] sortedSquares(int[] nums) {
    int left = 0;
    int right = nums.length - 1;
    int index = nums.length - 1;
    int[] res = new int[nums.length];
    while (left <= right) {
        if (nums[left] * nums[left] >= nums[right] * nums[right]) {
            res[index--] = lnum;
            left++;
        } else {
            res[index--] = rnum; 
            right--;
        } 
    }
    return res;
}

长度最小的子数组--滑动窗口

 题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章链接:代码随想录

视频链接:拿下滑动窗口! | LeetCode 209 长度最小的子数组_哔哩哔哩_bilibili

滑动窗口:本质是满足了单调性,即左右指针只会往一个方向走且不会回头,收缩的本质即去掉不需要的元素。也就是做题我们可以先固定移动右指针,判断条件是否可以收缩左指针计算范围

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)

暴力解法:

观察题目给出的数据范围:1<=nums.length<=10^5,如果用暴力解法两个for循环(O(n^2))可以得出题解,但是会超出数据范围达到10^10 (>10^8就算超时)

双指针--滑动窗口解法:

自己的想法

同向双指针,模仿两个for循环使得时间复杂度降低,快指针不断移动,直至左右范围内的和>=target时,记录当前窗口的长度,让右指针等于左指针,重复以上的操作,相当于每个下标都作为一次窗口的左端

时间复杂度:O(n)

public int minSubArrayLen(int target, int[] nums) {
    int slow = 0;
    int length = Integer.MAX_VALUE;
    int sum = 0;
    for (int fast = 0; fast < nums.length; fast++) {
        sum += nums[fast];
        if (sum >= target) {
            length = Math.min(fast - slow + 1, length);
            slow++;
            sum = 0;
            fast = slow - 1;
        }
    }
    return length == Integer.MAX_VALUE ? 0 : length;
}
题解:

和上式的区别:不是尝试每个下标的区间最优解,而是让窗口保持在>=target的范围内不断收缩,以此得到最优解

时间复杂度:O(n)

public int minSubArrayLen(int target, int[] nums) {
    int slow = 0;
    int sum = 0;
    int length = Integer.MAX_VALUE;
    for (int fast = 0; fast < nums.length; fast++) {
        sum+= nums[fast];
        while (sum >= target) {
            length = Math.min(length, fast - slow + 1);
            sum -= nums[slow++];
        }
    }
    return length == Integer.MAX_VALUE ? 0 : length;
}

疑惑

for里面嵌套着while,为什么时间复杂度却是O(n),而不是O(nlogn)?

看每一个元素被操作的次数,每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被操作两次,所以时间复杂度是 2 × n 也就是O(n)

我自己写的算双指针吗?我的右指针都会=左指针,就相当于每个下标都遍历一次,相当于重新来,直至左右指针范围里的和大于target,我不确定这个算不算

录友回答

什么是窗口,其实就是[slow,fast]这个闭区间,并对这个闭区间做计算,你这个其实就是窗口,只是每次都以slow为基准往前找,虽然也可以实现,但是走回头路了,因为每次都重新从slow往前叠加,你求区间和的动作不就重复了吗。

其实找到了一个比target大的区间,你只需要保持fast在原地,让slow往前找,slow每移动一步就把值从窗口中拿掉,这样就不用走回头路了。简单来说就是fast前面探路,slow在后面追。两个指针都不走回头路。

螺旋矩阵II

 题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

文章链接:代码随想录

视频链接:一入循环深似海 | LeetCode:59.螺旋矩阵II_哔哩哔哩_bilibili

模拟题:

坚持循环不变量原则,左闭右开,模拟转圈的过程

时间复杂度:O(n^2)

public int[][] generateMatrix(int n) {
    int loop = 0;	//控制循环次数(圈数)
    int start = 0;	//每次循环的开始点(start,start)
    int count = 1;	//填充数字
    int[][] res = new int[n][n];
    int i,j;
    while (loop++ < n / 2) {	//判断边界后,loop从1开始
        //模拟上侧从左到右
        for (j = start; j < n - loop; j++) {
            res[start][j] = count++;
        }
        //模拟右侧从上到下
        for (i = start; i < n - loop; i++) {
            res[i][j] = count++;
        }
        //模拟下侧从右到左
        for (; j >= loop; j--) {
            res[i][j] = count++;
        }
        //模拟左侧从下到上
        for (; i >= loop; i--) {
            res[i][j] = count++;
        }
        start++;
    }
    //如果n为奇数,填充中间缺少的位置
    if (n % 2 == 1) res[start][start] = count;
	return res;
}

总结体悟

今天的数组题用到了双指针,其中双指针又分为对撞指针和同向指针,同向指针其中的一个应用方法就是滑动指针,我自己的思路虽然也算上滑动窗口,但是错在了每次都让fast从以slow为基准值往前叠加,反复去求区间和,走了回头路...刷到模拟题的时候错在了没有准守循环不变量原则,这也让我对这个原则有了明确的认识...今天是第二天,虽然平时也有事要干,但每天抽出点时间来学算法也算是我的一点乐趣,什么时候学都不算晚,坚持才是关键✊!!!

  • 22
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值