【代码随想录day2】977.有序数组的平方 ,209.长度最小的子数组 ,59.螺旋矩阵II

977.有序数组的平方

问题

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/
给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。

示例:

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

解答

法1:暴力解答
先平方再排序
代码:

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        result = []
        for num in nums:
            result.append(num**2)
        result.sort()
        return result

时间复杂度 O(n + nlogn)

法2:双指针法
由于原数组是有序的,而负数平方后变成正数,因此新数组最大值在原数组两端产生。此时可以考虑双指针法了,i指向起始位置,j指向终止位置。定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
如果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]:
        i = 0
        j = len(nums) - 1
        k = len(nums) - 1
        result = [0] * (k+1)
        while i <= j:
            if nums[i]**2 <= nums[j]**2:
                result[k] = nums[j]**2
                k -= 1
                j -= 1
            else:
                result[k] = nums[i]**2
                k -= 1
                i += 1
        return result

需要注意的是,循环结束后可能还剩下一个元素没有处理,因此在循环条件中使用了 i <= j。

209.长度最小的子数组

问题

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

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

解答

法1:暴力解法
两层循环,遍历所有可能的子数组
代码:

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

由于该方法会超时,因此略过

法2:滑动窗口
考虑使用一个循环完成。设定一个开头与结尾可以滑动的窗口,当窗口值的和小于target,则扩大窗口范围,即窗口结尾向前移动;反之,则缩小窗口范围,即窗口开头向前移动。在窗口移动过程中,记录窗口内元素的数量,找到最小值。
代码:

# 我自己的代码,但是最后会超时
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        i, j = 0, 0
        #定义一个大于len(nums)的值,一般可以设置为无限
        length = float('inf') 
        while j < len(nums):
            if sum(nums[i:j+1]) < target:
                j += 1
            else:
                length = min(j - i + 1, length)
                i += 1
        #若length没有被赋值则返回0
        return 0 if length == float('inf') else length 

每次循环都要计算一次sum,因此需要很久。

#提供的代码
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        res = float("inf")   # 定义一个无限大的数
        Sum = 0     # 滑动窗口数值之和
        i = 0      # 滑动窗口起始位置
        for j in range(len(nums)):
            Sum += nums[j]
            while Sum >= s:
                res = min(res, j-i+1)
                Sum -= nums[i]
                i += 1
        return 0 if res == float("inf") else res

整个过程只使用一个sum值不断更新。窗口结尾每移动一次,sum就加上这个值;窗口结尾移动到当sum>=target时,则减去窗口开始的值,并向前移动一次,直到sum<target,开始下一次的结尾移动。这样每次计算sum都可以利用上一轮的值,计算更快。
·时间复杂度:O(n)
·空间复杂度:O(1)
注:虽然有两层循环,但每个元素进出窗口各一次,故总操作量的2n,因此时间复杂度是O(n)。

59.螺旋矩阵II

问题

题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例:
在这里插入图片描述

解答

本体算法很简单,但是对边界条件控制要求很高,而求解本题依然是要坚持循环不变量原则。(类似一种对称性)

模拟顺时针画矩阵的过程:
填充上行从左到右
填充右列从上到下
填充下行从右到左
填充左列从下到上
例如,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
在这里插入图片描述
代码:

# 我自己的
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        loops = int((n + 1) / 2)
        num = 1
        matrix = [[0 for _ in range(n)] for _ in range(n)]
        for loop in range(loops):
            for i in range(n - 1 - loop * 2):
                matrix[loop][i + loop] = num
                num += 1
            for i in range(n - 1 - loop * 2):
                matrix[i + loop][n - loop - 1] = num
                num += 1
            for i in list(range(n - 1 - loop * 2, 0, -1)):
                matrix[n - loop - 1][i + loop] = num
                num += 1
            for i in list(range(n - 1 - loop * 2, 0, -1)):
                matrix[i + loop][loop] = num
                num += 1
        if n % 2 == 1:
            matrix[loops - 1][loops - 1] = n**2
        return matrix

注:
1.生成n*n的二维数组时应该使用循环嵌套生成matrix = [[0 for _ in range(n)] for _ in range(n)],而不能直接用乘法matrix = [[0]*3]*3,否则每次修改其中一行的时候,其他行也会改变。因为其创建的二维列表中的每一行其实是引用了同一个列表对象,而不是独立的列表对象。
2.使用loop来代表由外至内填充的层数(起始层为第0层),因此每一层开始填充的起点是[loop, loop],因此在每一行每一列开始填充的时候,i要加上loop,例如matrix[i + loop][n - loop - 1] = num
3.当n为奇数时,第0层每行每列实际填充n-1个数,增加一层需要填充的数减少两个,因此到最内层时,需要填充的数为0个,因此要单独处理中心值。

# 提供的code
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

·时间复杂度 O(n^2): 模拟遍历二维矩阵的时间
·空间复杂度 O(1)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值