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

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

977.有序数组的平方

题目:给你一个按 非递减顺序 排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。要求时间复杂度O(N)。

链接:https://leetcode.cn/problems/squares-of-a-sorted-array/

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

基本思路

双指针:
  1. 新建一个和原来数组相同长度的数组,并且索引从末尾出发,每一次向左挪一步。
  2. 左指针从0出发,右指针从数组最末尾出发,比较两个指针的绝对值大小,取较大值放入新指针的位置。
  3. 若较大值是左指针,则左指针向右一步;若较大值是右指针,则右指针向左一步;若两大小一样,指定左指针向右一步,优先放左指针的数进入新数组。
  4. 左指针在遇到右指针时,循环结束(循环会在绝对值最小的地方结束)。
代码:
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        begin = 0
        end = len(nums)-1
        end_new = len(nums)-1
        nums_new = [0]*len(nums)
        while begin<=end: #确保没有0出现的情况下,最小的数值会被读入
            # if nums[begin]<0:
            if abs(nums[begin])>=abs(nums[end]):
                nums_new[end_new] = nums[begin]**2
                begin=begin+1
            else:
                nums_new[end_new] = nums[end]**2
                end=end-1
            end_new = end_new-1
        return nums_new
代码随想录:
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l, r, i = 0, len(nums)-1, len(nums)-1
        res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果
        while l <= r:
            if nums[l] ** 2 < nums[r] ** 2: # 左右边界进行对比,找出最大值
                res[i] = nums[r] ** 2
                r -= 1 # 右指针往左移动
            else:
                res[i] = nums[l] ** 2
                l += 1 # 左指针往右移动
            i -= 1 # 存放结果的指针需要往前平移一位
        return res

链接:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html#%E6%9A%B4%E5%8A%9B%E6%8E%92%E5%BA%8F

总结

刚开始做的时候思路不太清晰,使用左指针遇到0作为循环结束条件(此时会比上面的方法省略了正数部分的顺序比较),因此就额外添加了正数部分的顺序排列,但是这样就会导致一些特殊情况需要额外考虑,例如全为负数的话左指针会越界,可能速度会更快,但是debug和测试用例时间更长。
左右指针这类型的题目,真的极其容易出现越界以及不知道如何设定循环结束条件的问题!

209.长度最小的子数组

题目:给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
链接:https://leetcode.cn/problems/minimum-size-subarray-sum

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

基本思路

  1. 首先求出原数组的累积和,从0开始,因此会比原始数组长度加1。
  2. 找到第一个大于target的位置,并设定为右指针初始位置。
  3. 左指针从0出发,记录右指针位置减去左指针位置的数值差,判断是否>target(第一步肯定大于),然后左指针前移找到最小的长度。
  4. 随后右指针前移一步,左指针前移一步,判断是否>target。
  5. 如果大于target,则左指针继续前移至小于target的位置,存储当前结果。
  6. 右指针到数组末尾结束。
代码:
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        sum_num = 0
        sum_list = [0]
        min_num = []
        for i, list in enumerate(nums):
            sum_num = sum_num + list
            if (sum_num >= target) & (len(min_num) == 0):
                min_num.append(i)
            sum_list.append(sum_num)
        print(sum_list)
        if len(min_num) == 0:
            return 0
        else:
            left = 0
            right = min_num[0] + 1
            index = right - left
            while right <= len(nums):
                i = 0
                while sum_list[right] - sum_list[left] > target:
                    i = i + 1
                    left = left + 1
                if (i > 0) & (sum_list[right] - sum_list[left] < target):
                    right = right + 1
                    index = right - left
                    continue
                left = left + 1
                right = right + 1
                index = right - left
            return index
代码随想录
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        l = len(nums)
        left = 0
        right = 0
        min_len = float('inf')
        cur_sum = 0 #当前的累加值
        
        while right < l:
            cur_sum += nums[right]
            
            while cur_sum >= s: # 当前累加值大于目标值
                min_len = min(min_len, right - left + 1)
                cur_sum -= nums[left]
                left += 1
            
            right += 1
        
        return min_len if min_len != float('inf') else 0

链接:https://programmercarl.com/0209.%E9%95%BF%E5%BA%A6%E6%9C%80%E5%B0%8F%E7%9A%84%E5%AD%90%E6%95%B0%E7%BB%84.html#%E6%BB%91%E5%8A%A8%E7%AA%97%E5%8F%A3

总结:

这里需要注意,由于题目要求找最小的连续数组,当第一次查找的数组index确定之后,会左指针右指针各往前平移,这里在做判定的时候需要区分=target和=target,所以理论上左指针要回撤一步,但是如果他回撤超过原来index了,就无效,而要求找最小连续数组,所以需要继续向前平移去查找,因此在这边我加了一个i来指示这里是否回撤超过原来的Index。
其次是这个题目还有一个方法可以思维更清晰,但是没时间写了(这个方法浪费了一些时间复杂度,因为如果index(n+1)>index(n)的话其实这个index就可以放弃,但是由于这个问题中需要确保找到每个左指针所对应的第一个大于target的位置是右指针,所以无法省略这个步骤),简单记录一下思路(也是代码随想录的思路):

  1. 左指针从0开始,右指针位于第一个大于target的位置,然后左指针右移到最小长度,记录此时的Index。
  2. 然后,左指针前移一步,判断此时右指针减去左指针是否大于target,如果大于的话,右指针不变,记录此时的Index;加入小于的话,右指针右移,找到第一个大于target的位置,记录Index。
  3. 最后获得Index的最小值。

59 螺旋矩阵II

题目:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
链接:https://leetcode.cn/problems/spiral-matrix-ii/

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

基本思路

方法1:
采用树的思维:做一个n2大小的循环
先设定一个nxn的矩阵为0,每次寻找网格点,第一步从(0,0)出发,优先向右移动;如果右移碰到网格边界或者已被填充,开始向下移动;如果下移碰到网格边界或者已被填充,开始向左移动;如果左移碰到网格边界或者已被填充,开始向上移动;如果上移碰到网格边界或者已被填充,开始向右移动…for循环:不设定截止边界,0来判断是否被填充
但是这是一个时间复杂度O(n2)的算法,可以优化(代码随想录的方法)
方法2:
优化的思维:首先是重复操作,例如5的网格,除了外层内部是3,最后是1,而4的网格除了外层内部是1,因此可以考虑定义函数调用n, n-2, n-4直到1或者0,需要注意此时的索引需要改变;其次是在外层的时候不用使用遍历的手法,因为内部的数值都是可以计算的,例如[:,0]=[1:n],而[:,n-1]=[3n-2:2n-1],[1:(n-1),0]=[4n-4:3n-1],[1:(n-1),n-1]=[n+1:2n-2],实际上因为list无法跨行操作其实时间复杂度还是会比较高,但是行操作还是节省复杂度的。

代码:
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix = [[0 for j in range(n)] for i in range(n)]

        def shell(i, n_a, delta):
            matrix[i][i:(n_a + i)] = list(range(1 + delta, n_a + 1 + delta, 1))
            matrix[n_a + i - 1][i:(n_a + i)] = list(range(3 * n_a - 2 + delta, 2 * n_a - 2 + delta, -1))

            if n_a > 2:
                for j, row in enumerate(matrix[(i + 1):(n_a + i - 1)]):
                    row[i] = list(range(4 * n_a - 4 + delta, 3 * n_a - 2 + delta, -1))[j]
                    row[n_a + i - 1] = list(range(n_a + 1 + delta, 2 * n_a - 1 + delta, 1))[j]

        sum = 0
        i = 0
        while n >= 0:
            shell(i, n, sum)
            n = n - 2
            i = i + 1
            sum = sum + 4 * n + 4
        return matrix
代码随想录
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]
        startx, starty = 0, 0               # 起始点
        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

链接:https://programmercarl.com/0059.%E8%9E%BA%E6%97%8B%E7%9F%A9%E9%98%B5II.html#%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC

今天问题总结

  1. 一个题目不及时总结的话,基本上写完就会忘记思路和方法。
  2. 学会写伪代码很重要,可以在判断边界的时候思路清晰。
  3. 可以先从暴力解法开始尝试,然后逐步思考可以优化的地方。
  4. 注意python中的list如果使用[[0]*3]*3的方式创建可能会出现某一行赋值影响下一行的情况。
  5. 有空补一下后两个题没写的方法。

你可能感兴趣的:(算法,矩阵,leetcode)