算法刷题打卡002 | 有序数组的平方,长度最小子数组,螺旋矩阵II

977.有序数组的平方

题目链接:977. 有序数组的平方 - 力扣(Leetcode)

前几天刚好又做了一遍这道题,主要思路是找到排序数组的分割点,将数组分为正、负两个部分(0或者第一个正数),然后从分割点开始向两边进行双指针遍历,较小的数优先放入结果数组。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 原做法:找到排序数组分界点,从分界点开始向两边双指针,每次选择较小的值加入结果数组
        n = len(nums)
        if nums[0] >= 0:
            return [nums[i] ** 2 for i in range(n)]
        elif nums[-1] <= 0:
            return [nums[i] ** 2 for i in range(n-1, -1, -1)]
        # 找到正负的分界点
        for k in range(n-1):
            if nums[k] * nums[k+1] <= 0:
                i, j = k, k+1
                break
        ans = []
        while i >= 0 and j < n:
            if abs(nums[i]) <= nums[j]:
                ans.append(nums[i] ** 2)
                i -= 1
            else:
                ans.append(nums[j] ** 2)
                j += 1
        while i >= 0:
            ans.append(nums[i] ** 2)
            i -= 1
        while j < n:
            ans.append(nums[j] ** 2)
            j += 1
        return ans

上述解法最多要遍历两次数组,时间复杂度是O(n)。看题解发现自己蠢了,为什么非要从中间开始双指针,从首尾进行双指针异曲同工,只不过结果数组会是降序排列,返回时需要反转。当然也可以预定义结果数组,数组指针index从后往前移动。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        # 做法二:原数组有序,双指针从两端向中间遍历,先处理绝对值较大的数
        # 可以按这个顺序构成结果数组(非递增)以后反转顺序,也可以预先构建结果数组,倒序填入
        n = len(nums)
        ans, index = [0] * n, n - 1
        i, j = 0, n - 1
        while i <= j:
            if abs(nums[i]) >= abs(nums[j]):
                ans[index] = nums[i] ** 2
                i += 1
            else:
                ans[index] = nums[j] ** 2
                j -= 1
            index -= 1
        return ans
第二种解法遍历了一遍数组,时间复杂度同样是O(n)。如果是暴力解法,就是遍历一遍数组得到每个数的平方再排序,时间复杂度是O(n+nlogn)。

209.长度最小的子数组

题目链接:209. 长度最小的子数组 - 力扣(Leetcode)

最早解题接触的就是双指针方法:

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if sum(nums) < target:
            return 0
        # 代码随想录,双指针解法,i遍历区间起始位置,j遍历区间终止位置
        i , n = 0, len(nums)
        result = float('inf')
        s = 0  # 动态的区间之和
        for j in range(n):
            s += nums[j]
            # while循环找区间起始位置
            while s >= target:
                # 满足条件时,记录当前区间长度
                subL = j - i + 1
                result = min(result, subL)
                # 起始位置移动
                s -= nums[i]
                i += 1
        return 0 if result == float('inf') else result
这次回顾题目并练习一下暴力解法和进阶部分的O(nlogn)解法。
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        # 暴力解法
        result = float('inf')
        for i in range(n):
            Sum = 0
            for j in range(i, n):
                Sum += nums[j]
                if Sum >= target:
                    result = min(result, j - i + 1)
                    break
        return 0 if result == float('inf') else result
        
        # 进阶解法-> 前缀和+二分查找(数组若存在负数就不能用二分了)
        n = len(nums)
        result = float('inf')
        preSum = [0]
        for i in range(n):
            preSum.append(preSum[-1] + nums[i])
        # i, j之间的区间和可以表示为preSum[j] - preSum[i-1]
        for i in range(n):
            t = preSum[i] + target
            idx = bisect.bisect_left(preSum, t)
            if idx == len(preSum):
                continue
            result = min(result, idx - i)
        return 0 if result == float('inf') else result

但二分的前提是前缀和是非单调递减的,如果数组中不全是正整数,前缀和就不满足要求,不能使用这种解法。最初我以为双指针的解法应该不会受到负数存在的影响,但举例模拟了一下发现存在负数确实会出问题,特此记录。

59.螺旋矩阵II

题目链接:59. 螺旋矩阵 II - 力扣(Leetcode)

螺旋矩阵就是在模拟转圈的过程,找到规律后比较繁琐的就是边界条件:

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        result = [[0 for _ in range(n)] for _ in range(n)]
        start_x, start_y = 0, 0
        offset = 1
        count = 1
        r = n // 2  # while循环次数
        while r:
            # 第一行(左闭右开)
            j = start_y
            while j < n - offset:
                result[start_x][j] =count
                count += 1
                j += 1
            # 最后一列
            i = start_x
            while i < n - offset:
                result[i][j] = count
                count += 1
                i += 1
            # 最后一行
            while j > start_y:
                result[i][j] = count
                count += 1
                j -= 1
            # 第一列
            while i > start_x:
                result[i][j] = count
                count += 1
                i -= 1
            # 更新边界
            r -= 1
            start_x += 1
            start_y += 1
            offset += 1

        if n % 2:
            # 奇数
            result[start_x][start_y] = count
        return result

第二天刷题任务完成,也是回顾复习为主。

你可能感兴趣的:(刷题,算法)