代码随想录算法训练营第二天|977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II

1.977.有序数组的平方

题目链接:有序数组的平方
文档讲解: 代码随想录

class Solution {
    public int[] sortedSquares(int[] nums) {
        int[] arr = new int[nums.length];
        int a = 0, b = nums.length - 1;
        for(int i = nums.length - 1; i >= 0; i--){
            if(nums[a] * nums[a] < nums[b] * nums[b]){
                arr[i] = nums[b] * nums[b];
                b--;
            }else{
                arr[i] = nums[a] * nums[a];
                a++;
            }
        }
        return arr;
    }
}

讲解用while循环,我用的是for,遇到的问题就是一开始把a++写成了i++,debug的时候发现i一直在3和4打转,发现了问题。

2.209.长度最小的子数组

题目链接:长度最小的子数组
文档讲解: 代码随想录

这道题用到的思想是滑动窗口,不断调节子序列的起始位置和终止位置。暴力解法利用两层for循环,第一层控制起始位置,第二层循环控制终止位置,遍历所有可能的子数组。

class Solution {
    public int minSubArrayLen(int target, int[] nums) {
       int length = Integer.MAX_VALUE;//将默认子序列长度设置为最大值
       int l = 0;//滑动窗口的左边界
       int sum = 0;
       for(int r = 0; r < nums.length; r++){
        sum += nums[r];
        while(sum >= target){
            length = length > (r - l + 1) ? (r - l + 1): length;
            sum -= nums[l++];
        }
       }
       return length == Integer.MAX_VALUE? 0 : length;
    }
}

两个注意点:
(1)对于子数组长度的默认长度得设置为超大整数:Integer.MAX.VALUE
(2)关于时间复杂度:看每个元素被操作的次数,每个元素在滑动窗后进来一次,出去一次,每个元素都是被操作两次,所以时间复杂度为O(n)

扩展题

904. 水果成篮

题目链接:904

class Solution {
    public int totalFruit(int[] fruits) {
        int kind = 0;
        int[] count = new int[fruits.length + 10];
        int l = 0, ans = 0;
        for(int r = 0; r < fruits.length; r++){     
            if(count[fruits[r]] == 0){
                kind++;
            }//判断水果种类
             count[fruits[r]]++;//摘水果放篮子
            while(kind > 2){
                //水果种类超过2了,需要右移l
                count[fruits[l]]--;
                if(count[fruits[l]] == 0){
                    //这是该种类唯一的水果
                    kind--;
                }
                l++;
            }
            ans = ans > (r - l + 1) ? ans : (r - l + 1);
        }
        return ans; 
    }
}

这道题的难点在于如何统计水果的种类。通过定义一个数组count,即各种水果的篮子,果子种类为3,那么放入该果子则count[3]自加一。
那么人站在一棵树前,摘果子的步骤为:
(1)判断该果子的种类,是否为新出现的水果

if(count[fruits[r]] == 0){
                kind++;
            }

(2)将该水果放入对应的篮子中

 count[fruits[r]]++;

摘水果的过程已经完成,那么接下来需要判断这蓝水果是否需要,则需要判断此时kind是否超过2,如果超过,则需要调整窗口使得起始位置。

76. 最小覆盖子串

题目链接:76

class Solution {
    public String minWindow(String s, String t) {
        //通过比较字符串长度将不可能的情况排除掉(这步忘记了)
        int sLen = s.length();
        int tLen = t.length();
        if(sLen == 0 || tLen == 0 || sLen < tLen){
            return "";
        }
        
        //先把字符串转变为字符数组
        char[] sAr = s.toCharArray();
        char[] tAr = t.toCharArray();

        int[] ss = new int[128];
        int[] tt = new int[128];

        //窗口滑动时发现,需要定义一个变量来让我知道在什么时候s的子串已经包含了t,先认为是t中字符种类
        //但是觉得种类这个变量不行,种类相同并不代表着完全包含
        //答案定义了一个变量,来反映s子串中包含t字符的个数
        int dis = 0;
 
        //统计字符出现的次数
        //不需要统计s中所有字符出现的次数
         for(int i = 0; i < tLen; i++){
            tt[tAr[i]]++;
            if(tt[tAr[i]] == 1){
            }
        }
        //需要返回最小覆盖字串,则需要记录符合条件的窗口的起始和子字符串长度
        int l = 0, lWin = 0, minLen = sLen + 1;//加1的目的是为了区分s是最小覆盖子串和找不到子串的情况        
   
        //开始滑动窗口
        for(int rWin = 0; rWin < sLen; rWin++){
            if(ss[sAr[rWin]] < tt[sAr[rWin]]){
                //说明sAr[rWin]在t中
                dis++;
            }
            ss[sAr[rWin]]++;

            //直到dis=tLen的时候可以移动窗口的左边界
            while(dis == tLen){
                已经得到最小覆盖子串
                if((rWin - lWin + 1) < minLen){
                    l = lWin;
                    minLen = rWin - lWin + 1;
                }
                if(ss[sAr[lWin]] == tt[sAr[lWin]]){
                    //说明sAr[lWin]在t中,删掉它不行
                    dis--;
                }
                ss[sAr[lWin++]]--;
            }
        }
        if(minLen != sLen + 1){
            return s.substring(l, l + minLen); 
        }else{
            return "";
        }
    }   
}

这道题的难点在于如何确定s的子串已经包含了t。这道题在理解了思路自己写的时候,一直报错,后来发现是对s.substring(l, l + minLen)存在误解,是不包括endIndex的。目前还是对常见字符串操作不熟悉。

难点总结

滑动窗口最难的地方就在于什么时候可以右移起始位置。

3.59.螺旋矩阵II

题目链接:螺旋矩阵II
文档讲解: 代码随想录
视频讲解: 代码随想录

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] nums = new int[n][n];
        int startX = 0, startY = 0;
        int loop = 1;//记录当前圈数
        int offset = 1;
        int count = 1;//矩阵中需要填写的数字;
        int i,j;
        while(loop <= n/2){
            //左闭右开
            //上行
            for(j = startY; j < n - offset; j++){
                nums[startX][j] = count++;
            }

            //右列
            for(i = startX; i < n - offset; i++){
                nums[i][j] = count++;
            }

            //下行
            for(; j> startY; j--){
                nums[i][j] = count++;
            }

            //左列
            for(; i > startX; i--){
                nums[i][j] = count++;
            }
            
            startX++;
            startY++;
            offset++;
            loop++;
        }

        //若n为奇数
        if(n % 2 == 1){
            nums[startX][startY] = count;
        }
        return nums;
    }
}

第一遍看是不会的,理解思路的话写起来是简单的,关键在于要坚持循环不变量原则,对于拐角处的处理要坚持左闭右开或者左开右闭。

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二天的算法训练主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的数组,使得数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二天的算法训练的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值