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

今日碎碎念:坚持打卡第二天,争取多刷一些题。


977.有序数组的平方

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

文章讲解:代码随想录

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

关键在于理解双指针的思想

题目:

给你一个按 非递减顺序 排序的整数数组 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]

思路:

    1. 根据题意,可以先在原数组的基础上直接进行平方,平方后的数值存储在原数组的位置得到一个新数组,然后再将新数组进行排序。(暴力解法)

    2. 利用双指针的思想。根据题意,该数组是有序的,因此其平方后,最大值和最小值存在于数组的两端。因此可以定义一个对撞指针i,j。i指向开始的位置,j指向种植位置。随后,重新再定义一个新数组,比较两个指针所指位置的平方值较大的放入新数组中。

      即:

      如果A[i] * A[i] < A[j] * A[j] 那么result[k--] = A[j] * A[j];

      如果A[i] * A[i] >= A[j] * A[j] 那么result[k--] = A[i] * A[i];

算法:对撞指针法(见第一天的第二个算法介绍)

题解:

  • 暴力解法:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        for i in range(len(nums)):
            nums[i] *= nums[i]
        
        nums.sort()
        return nums
  • 对撞双指针法:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 对撞指针法
        # 第一步:定义指针。这里需要三个,分别指向开始(i)、结尾(j)以及新数组(k),新数组从大到小排列
        i, j, k = 0, len(nums)-1, len(nums)-1 #新数组的长度和现在长度一样
        # 定义一个新数组,长度和原来相等的空数组
        result = [float("inf")] * len(nums)  
        #result =[]  #list结合append()方法不管用,输出空值
        while i <= j:
            if nums[i] ** 2 < nums[j] ** 2:  # 如果从头开始的数平方 小于从尾端开始的数平方,则尾端数平方放进新数组,尾端的指针继续向左移,开始的指针不动。让新的数值平方继续和开始的指针指向数值比较。
                result[k] = nums[j] ** 2
                j -= 1
            else:
                result[k] = nums[i] ** 2  #如果开始的大,则将大的数值继续填入新数组。原则:哪边指针指的数组结果大,就移动哪一边。开始的向右移动,尾端的向左移动。
                i += 1
        # 控制新数组的指针移动。新数组从小到大排列,因此先得到的大结果放最右边。新数组的指针保持向左移。
            k -= 1  
        return result
  • 列表推导式的方法

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted(x*x for x in nums)

总结:

这道题还是比较容易的,不管是暴力解法、列表推导式,还是双指针方法,只要想清楚实现步骤以及需要的东西,很容易就解出来了。


209.长度最小的子数组

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

文章讲解:代码随想录

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

关键在于理解滑动窗口

 

题目:

给定一个含有 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

思路:

首先寻找所有组合,然后在所有的组合中寻找符合题意条件的组合,并计算符合条件的组合数量。可以使用滑动窗口的思想来编写代码。

那什么是滑动窗口思想呢?

算法:滑动窗口思想

关键:如何移动起始位置。简单来说就是,从起始位置0开始固定一个指针,然后另外一个指针遍历剩余的所有数来寻找符合条件的数组集合。后面以此类推,当一个位置的固定指针和剩下的滑动指针遍历完成后,才能固定下一个指针继续遍历。

可以简单用小学数学中寻找一组元素中有多少种组合方式,并且从这些组合方式中寻找出符合条件的组合的思路来理解。

在具体编写代码的过程中,需要注意如下几点:

  • 根据题意确定窗口内是什么内容

  • 如何移动窗口的起始指针位置

  • 如何移动窗口的结束位置

题解:

  • 滑动窗口

    • 在本题中,窗口中的内容是满足>=target的长度最小的连续子数组

    • 窗口移动的起始指针:当窗口中的值大于target,则起始固定指针需要移动

    • 窗口移动的结束位置:遍历起始指针后的所有位置。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 1.获取数组的长度
        l = len(nums)

        # 2.确定固定指针和滑动指针的初始位置
        left = 0  # 固定的指针
        right = 0 # 滑动的指针

        # 3.确定符合条件子数组的初始长度和当前子数组的累加和
        min_len = float("inf")
        cur_sum = 0 #当前子数组的累加和

        # 4.滑动窗口思想寻找符合条件的子数组
        while right < l: # 不能滑动出数组
            cur_sum += nums[right]  # 当前累加和等于所有滑动指针走过的值相加

            while cur_sum >= target:
                min_len = min(min_len, right-left + 1)  # right-left + 1 防止运算出界。和之前的最小长度比较,直到寻找到最小的
                cur_sum -=nums[left]  # 当前的和减去固定指针之前的,寻找出最小子数组的和
                left += 1

            right += 1

        return min_len if min_len != float("inf") else 0
  • 暴力解法:两个for循环(leetcode超出时间限制)

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 1.获取数组的长度
        l = len(nums)

        # 2.确定符合条件子数组的初始长度
        min_len = float("inf")
        

        # 3.暴力求解
        for i in range(l): #在不超过数组长度的地方遍历
            cur_sum = 0 #当前子数组的累加和
            
            for j in range(i,l):
                cur_sum += nums[j]
                if cur_sum >= target:
                    min_len = min(min_len, j-i+1)
                    break 

        return min_len if min_len != float("inf") else 0

总结:

很有意思的一种代码书写方法,需要多理解。

相似练习题&题解

904.水果成篮
  • 思路: 寻找最长的子数组,并且计算子数组中的数值种类,返回最长的子数组。

  • 具体的代码书写有点绕

  • 题解:

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        
        # 确定指针
        left = 0

        # 确定返回的值:最大的长度和数值顶多两类(放在两个box里面)
        max_len = 0
        num = Counter() # 计算子数组中的种类

        # 使用滑动窗口来判定
        for right, k in enumerate(fruits):
            num[k] += 1
            while len(num) > 2:
                num[fruits[left]] -= 1  #如果大于2,就左指针fruits[left]减掉,不采摘
                if num[fruits[left]] == 0: #如果 fruits[left]在哈希表中的出现次数减少为0,需要将对应的键值从num中移除。
                    num.pop(fruits[left])
                    
                left += 1
            
            max_len = max(max_len, right - left + 1)

        return max_len 
76.最小覆盖子串

我暂时还理解不了,等二刷的时候再做吧~


59.螺旋矩阵Ⅱ

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

文章讲解:代码随想录

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

关键理解转圈的逻辑,运用在二分搜索中提到区间定义

 

题目:

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

示例 1:

img

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

思路:

  • 注意旋转的方向:从左至右,从上至下,从右至左,从下至上

  • 保持循环不变量原则:左闭右开,每一层设置一个offset量,用于控制余一个位置做开区间。

题解:

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]
        startx,starty = 0, 0  # x,y 方向的起始点
        loop, mid = n // 2, n // 2  #迭代次数,当n为奇数时,矩阵的中心点
        count = 1

        for offset in range(1, loop+1):  #每循环一层偏移量加1,偏移量从1开始
            for i in range(starty, n-offset): #从左至右,左开右闭
                nums[startx][i] = count
                count += 1
            for i in range(startx, n - offset):  #从上至下 
                nums[i][n-offset] = count
                count += 1

            for i in range(n-offset, starty, -1): #从右至左
                nums[n-offset][i] = count
                count += 1
            for i in range(n-offset, startx, -1): #从下至上
                nums[i][starty] = count
                count += 1
            # 更新起始点
            startx += 1
            starty += 1

        if n % 2 !=0:  # 当n为奇数时,填充中心点
            nums[mid][mid] = count

        return nums

数组章节总结

  • 循环不变量原则

  • 双指针思想:快慢指针,对撞指针

  • 特殊的双指针思想:滑动窗口思想

  • 理解清楚螺旋矩阵的打印方法,注意确定好区间,遵守循环不变量的原则。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值