代码训练营 DAY2 打卡

 本文由GarfieldTheOldCat原创,转载请标明dekkyandlappy-CSDN博客

今天是打卡第二天,继续研究数组内容

继续双指针

昨天的打卡里研究了快慢指针的用法,两个指针起点相同(以数组左侧为例),以不同的速度向终点(数组右侧)移动,当快指针移动到数组右侧时结束循环。

今天有另外一种双指针的写法:两个指针分别从数组两端向中间逼近,当指针相遇(确切的说,时两个指针迎面错过)时中止循环。

我对这两钟方法的理解:快慢指针两个指针的权重不同,担负不同功能,而双指针的两个指针地位是相同的。

话不多说,上题目:

LC977: 977. 有序数组的平方

上来先暴力解法:遍历数组,求平方,再排序(刚好我以前对java的内置排序并不清楚,借了这个机会也了解了一下)Java8 Stream 之sorted方法 排序讲解_stream().sorted-CSDN博客

    // 暴力解法
    public int[] sortedSquares_0(int[] nums) {
        int[] sqrt=new int[nums.length];
        for(int i=0;i<nums.length;i++){
            sqrt[i]=nums[i]*nums[i];
        }
        sqrt= Arrays.stream(sqrt).sorted().toArray();
        return sqrt;
    }

对于这道题而言时间复杂度有O(nlogn),力扣上也可以跑通。

考虑到输入的数组是有序的,最大值会在输入数组的左侧元素或右侧元素上取到,可以使用双指针。创建一个新数组用于保存结果,两个指针读取对应下标的数求平方再比较,最后完成指针操作和保存数据。

java版本

// 双指针法
    public int[] sortedSquares(int[] nums) {
        int[] sqrt=new int[nums.length];
        int left=0;
        int right=nums.length-1;
        int pos=nums.length-1;
        while(left<=right){
            int ls=nums[left]*nums[left];
            int rs=nums[right]*nums[right];
            if(ls < rs){
                sqrt[pos]=rs;
                right--;
            }
            else {
                sqrt[pos]=ls;
                left++;
            }
            pos--;
        }
        return sqrt;
    }

python版本

class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """

        left=0;
        right=len(nums)-1
        pos=len(nums)-1
        sqrt=[0 for i in range(len(nums))]

        while left<=right:
            if nums[left]**2 >nums[right]**2:
                sqrt[pos]=nums[left]**2
                left=left+1
            else:
                sqrt[pos]=nums[right]**2
                right=right-1
            pos=pos-1
        return sqrt

双指针只要一次遍历即可完成,不需要额外的排序操作,时间复杂度是O(N),比暴力解法还是更有优势的。

滑动窗口

基础

滑动窗口是双指针法的一个变种:双指针法(包括快慢指针)研究的都是指针对应的元素;而滑动窗口研究的是两个指针之间(包含这两个指针)的所有元素。

滑动窗口只需要一个if循环就可以完成,其思路如下:

  1. left和right都为0(数组最左侧),滑动窗口算法开始
  2. left不动,right右移一位,滑动窗口增大,直到left与right构成的窗口满足题目的要求
  3. right不动,left右移一位,滑动窗口缩小,直到窗口不满足要求
  4. 重复2和3,直到right到达数组最右侧

注意:使用for循环控制的应当是窗口的结尾right

如果用for控制left,则right还是需要遍历寻找,失去了滑动窗口的价值。

LC209:209. 长度最小的子数组

首先先写暴力解法:for循环遍历每个元素,再套一个for循环寻找满足要求的最短长度(即类似上文的用for控制left)。对于这道题,此方法会超时。

 // 暴力解法,超时了
    public int minSubArrayLen_0(int target, int[] nums) {
        int result=-1;
        for (int left = 0; left < nums.length; left++) {
            int right;
            int sum = 0;
            for (right = left; right < nums.length; right++) {
                sum = sum + nums[right];
                if (sum >= target){
                    if (result == -1) result=right-left+1;
                    else if(right-left+1<result) result=right-left+1;
                    break;
                }
            }
        }
        if(result==-1) result=0;
        return result;
    }

再使用滑动窗口,开辟出一个变量sum存储窗口内数据的和,一个变量result记录窗口长度的最小值。

java版本

// 滑动窗口
    public int minSubArrayLen(int target, int[] nums) {
        int sum=0;
        int result=Integer.MAX_VALUE;
        int left=0;
        for(int right =0; right<nums.length;right++){
            sum=sum+nums[right];
            while(sum>=target){
                result=Integer.min(result,right-left+1);
                sum=sum-nums[left];
                left++;
            }

        }
        if (result==Integer.MAX_VALUE) result=0;
        return result;
    }

注意到这里在初始化result时使用了最大的整数Integer.MAX_VALUE,从而可以直接与窗口长度比较,取最小值。而对于python,这个值需要调库实现,为了稳妥起见,我没有在力扣上调sys库(我也不知道是否可行),而是选取了输入数组nums长度+1。

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        left = 0
        result = len(nums)+1
        sum = 0
        for right in range(len(nums)):
            sum = sum + nums[right]
            while sum >= target:
                result = min(result, right - left + 1)
                sum = sum - nums[left]
                left = left + 1

        if result == len(nums)+1:
            result = 0

        return result

螺旋矩阵

这类问题主要考察对题述过程的实现,但是困难在于循环嵌套时的边界判断问题。

解决方法:抓住不变量:对各个边的处理规则(全部坚持左闭右开,包括起点但不包括终点)

LC59:59. 螺旋矩阵 II

先上代码

 public int[][] generateMatrix(int n) {
        int[][] result=new int[n][n];
        int y=0; //纵坐标
        int x=-1; //横坐标
        int val=1;//需要赋的值
        int move=0;//移动方向
        int lenth=n-1;//移动步长

        if (lenth==0) lenth=1;// 处理n=1
        while(val <=n*n){
//            System.out.println(x+" "+y+" "+lenth+" "+val+" "+move);
            int move_count=0;//步数控制
            //上边:左到右
            if (move % 4==0){
                x++;
                while (move_count<lenth){
                    result[y][x]=val;
                    x++;
                    val++;
                    move_count++;
                }
                move++;

            }
            //右边:上到下
            else if (move%4==1) {
                while (move_count<lenth){
                    result[y][x]=val;
                    y++;
                    val++;
                    move_count++;
                }
                move++;
            }
            //下边:右到左
            else if (move%4==2){
                while (move_count<lenth){
                    result[y][x]=val;
                    x--;
                    val++;
                    move_count++;
                }
                move++;
            }
            //左边,下到上
            else {
                while (move_count<lenth){
                    result[y][x]=val;
                    y--;
                    val++;
                    move_count++;
                }
                move++;
                lenth=lenth-2;
                if (lenth<=0) lenth=1;
                y++;
            }
//            System.out.println(Arrays.deepToString(result));

        }
        return result;
    }

我看题解里每圈循环里的4条边的处理是用for循环实现的,为了避免后续for循环条件搞不清楚,我引入了一个变量move_count用于控制循环次数。

在写的过程中,发现有以下错误: 本文由GarfieldTheOldCat原创,转载请标明

  1. 每圈循环结束后,结束点在下一轮起点左上方(1,1),需要手动移到对应的位置
  2. 每圈步长减少2而不是1,若最后的步长<=0,需要补偿为1
  3. 初始步长若为0需要补偿为1以处理n=0

总结

今天学习了另一种双指针法和滑动窗口法,并写了螺旋矩阵

双指针法从两头向中间靠拢

滑动窗口for循环控制right

TODO

lc904,lc76

 本文由GarfieldTheOldCat原创,转载请标明dekkyandlappy-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值