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

LeetCode977.有序数组的平方

题目链接:https://leetcode.cn/problems/squares-of-a-sorted-array/description/
视频讲解: https://www.bilibili.com/video/BV1QB4y1D7ep
掌握程度:★★☆

这道题中的双指针是左右指针向中间靠拢的形式。

循环条件可以通过左右指针位置判断,写成 while left <= right;也可以通过结果是否完全填充判断,写成 for index in range(len(nums)-1, -1, -1).

比较大小的最直观方式是 比较两个平方的大小,nums[left] × nums[left] 和 nums[right] × nums[right] 的大小。为了简便计算,也可以比较 -nums[left] 和 nums[right] 的大小。

解决办法

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        l = len(nums)
        left, right = 0, l - 1
        res = [0] * l
        for index in range(l-1, -1, -1):
            left_num, right_num = nums[left], nums[right]
            if -left_num <= right_num:
                res[index] = nums[right] * nums[right]
                right -= 1
            else:
                res[index] = nums[left] * nums[left]
                left += 1
        return res

LeetCode209. 长度最小的子数组

题目链接:https://leetcode.cn/problems/minimum-size-subarray-sum/
视频讲解:https://www.bilibili.com/video/BV1tZ4y1q7XE
掌握程度:★☆☆

这道题的思路是滑动窗口

【复杂做法,用于记录,建议忽略】

我拿到这道题最初的思路是快慢指针法:

  1. 当子数组之和大于target时,需要减去一个数,slow += 1;
  2. 当字数组之和小于target时,需要加上一个数,fast += 1;
  3. 当子数组之和等于target时,未找到最小子数组,应先减后加。

循环的停止条件:

  1. .fast += 1 导致fast超出边界,停止
  2. slow += 1 导致slow超出边界,停止

需要注意的是:

  1. fast += 1 写在 sum_nums += nums[fast] 之前,如果将循环条件写为 while fast < len(nums),会出现超出边界的错误,因此只能在循环内写 if fast == len(nums)-1: break
  2. slow += 1写在 sum_nums -= nums[slow] 之后,可以将循环条件写为 while slow < len(nums)
class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        slow, fast = 0, 0
        min_len = float('inf')
        sum_nums = nums[0]
        while slow < len(nums):
            print(slow,fast,sum_nums)
            if sum_nums < target:
                if fast <= len(nums)-2:
                    fast += 1
                    sum_nums += nums[fast]
                else:
                    break
            elif sum_nums > target:
                min_len = min(min_len, fast - slow + 1)
                sum_nums -= nums[slow]
                slow += 1
            else:
                min_len = min(min_len, fast - slow + 1)
                sum_nums -= nums[slow]
                slow += 1
                if fast <= len(nums)-2:
                    fast += 1
                    sum_nums += nums[fast]
                else:
                    break
            
        return 0 if min_len == float('inf') else min_len

滑动窗口思路

这里的fast是持续增加的,不需要对fast的边界做分析,只要分析slow在什么条件下移动即可。

按照上一节的思路,slow只要移动到使得 sum_nums < target 就可以。因此条件为 while sum_nums >= target.

在这里,需要分析一下 sum_nums == target 时,slow是否可以继续增加。slow += 1 在 sum_nums -= nums[slow] 之后,所以 slow == fast+1 时,sum_nums == 0,进入for的下一次循环, fast += 1,不会出现区间无定义的情况。

class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        slow = 0
        min_len = float('inf')
        sum_nums = 0
        for fast in range(len(nums)):
            sum_nums += nums[fast]
            while sum_nums >= target:
                min_len = min(min_len, fast-slow+1)
                sum_nums -= nums[slow]
                slow += 1
        return 0 if min_len == float('inf') else min_len

LeetCode904. 水果成篮

题目链接:https://leetcode.cn/problems/fruit-into-baskets/description/
掌握程度:★★☆

仍然是滑动窗口,这道题的关键是如何实时记录水果种类

slow移动的条件也是水果种类超出限制。

暴力in

先给出我的超时做法,用 数字 in 数组 的方式计算水果是否是新种类。

class Solution(object):
    def totalFruit(self, fruits):
        """
        :type fruits: List[int]
        :rtype: int
        """
        slow = 0
        max_fruits = 0
        kind = 0
        for fast in range(len(fruits)):
            if fruits[fast] not in fruits[slow:fast]:
                kind += 1
            while kind > 2:
                if not fruits[slow] in fruits[slow+1 : fast+1]:
                    kind -= 1
                slow += 1
            max_fruits = max(max_fruits, fast-slow+1)
        return max_fruits

哈希表

相比之下,用哈希表记录数量应该更快吧。

class Solution(object):
    def totalFruit(self, fruits):
        """
        :type fruits: List[int]
        :rtype: int
        """
        slow = 0
        max_fruits = 0
        fruits_count = collections.defaultdict(int)
        for fast in range(len(fruits)):
            fruits_count[fruits[fast]] += 1
            while len(fruits_count) > 2:
                fruits_slow = fruits[slow]
                fruits_count[fruits_slow] -= 1
                if fruits_count[fruits_slow] == 0:
                    fruits_count.pop(fruits_slow)
                slow += 1
            max_fruits = max(max_fruits, fast-slow+1)
        return max_fruits

通过了

这里的 fruits_count = collections.defaultdict(int) 指字典的默认值为0,导入方式是 import collections.

Counter函数

官方解答上还在for中加了enumerate

class Solution(object):
    def totalFruit(self, fruits):
        """
        :type fruits: List[int]
        :rtype: int
        """
        slow = 0
        max_fruits = 0
        fruits_count = Counter()
        for fast, fruites_fast in enumerate(fruits):
            fruits_count[fruites_fast] += 1
            while len(fruits_count) > 2:
                fruits_slow = fruits[slow]
                fruits_count[fruits_slow] -= 1
                if fruits_count[fruits_slow] == 0:
                    fruits_count.pop(fruits_slow)
                slow += 1
            max_fruits = max(max_fruits, fast-slow+1)
        return max_fruits

Counter的导入方式 from collections import Counter.

LeetCode 76. 最小覆盖子串

题目链接:https://leetcode.cn/problems/minimum-window-substring/description/
掌握程度:★★☆

仍然滑动窗口,难点在于确定子串是否覆盖给定字符串

两个哈希表:

上一道题中,对不同数字的统计用的是哈希表的方法,这里我们也可以这么做。按照上一题的思路,我们对出现的字母做哈希表,比较特定字母的数量是否>=给定字符串字母数量。

一个哈希表:

这么做还是复杂了,有一个更简单的方法:将给定字符串字母数量设置为哈希表need,在子串中遇到该字母时,只需要对need做加减即可。(我愿称之为 残差哈希表

class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        need = collections.defaultdict(int)
        res = ""
        min_len = float('inf')
        slow = 0

        for a in t:
            need[a] += 1
        
        for fast, s_fast in enumerate(s):
            need[s_fast] -= 1
            while all(v <= 0 for v in need.values()):
                if len(s[slow: fast+1]) < min_len:
                    res = s[slow: fast+1]
                    min_len = len(res)
                s_slow = s[slow]
                need[s_slow] += 1
                slow += 1
        return res

slow更新的循环条件还可以优化,以 仍需更新的字母个数 为标志。

class Solution(object):
    def minWindow(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: str
        """
        need = collections.defaultdict(int)
        need_len = len(t)
        res = ""
        min_len = float('inf')
        slow = 0

        for a in t:
            need[a] += 1
        
        for fast, s_fast in enumerate(s):
            if need[s_fast] > 0 :
                need_len -= 1
            need[s_fast] -= 1
            while need_len == 0:
                if len(s[slow: fast+1]) < min_len:
                    res = s[slow: fast+1]
                    min_len = len(res)
                s_slow = s[slow]
                need[s_slow] += 1
                if need[s_slow] > 0:
                    need_len += 1
                slow += 1
        return res

LeetCode59. 螺旋矩阵 II

题目链接:https://leetcode.cn/problems/spiral-matrix-ii/
视频讲解:https://www.bilibili.com/video/BV1SL4y1N7mV/
掌握程度:★★★

这道题的重点在于边界的控制条件。

  1. 矩阵旋转填写数字,一共有四种方向,把他们分别记为0, 1, 2, 3,分别讨论;
  2. 该方向的填写在什么时候结束?一是碰到边界,二是碰到已填写过的数字。边界很好判断,无非是坐标为0或n-1;已填写的数字如何判断,我们可以把矩阵初始化为一个零矩阵,如果数字为0,代表未填写过,那么判断下一个需要填写的位置是否为0就可以了;
  3. 最终循环何时结束?我们需要填写 [1, n*n] 的数字,只要把它作为循环条件就可以了。
class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        direct = 0 # 0,1,2,3 四个方向
        matrix = [[0]*n for _ in range(n)]
        i, j = 0, 0
        for num in range(1, n*n+1):
            matrix[i][j] = num
            if direct == 0:
                if j == n-1 or matrix[i][j+1] != 0:
                    i += 1
                    direct = 1
                else:
                    j += 1
            elif direct == 1:
                if i == n-1 or matrix[i+1][j] != 0:
                    j -= 1
                    direct = 2
                else:
                    i += 1
            elif direct == 2:
                if j == 0 or matrix[i][j-1] != 0:
                    i -= 1
                    direct = 3
                else:
                    j -= 1
            elif direct == 3:
                if i == 0 or matrix[i-1][j] != 0:
                    j += 1
                    direct = 0
                else:
                    i -= 1
        return matrix
            

这里需要注意的是:
matrix = [[0]*n for _ in range(n)] 不能写成 matrix = [[0]*n]*n
因为 [[0]*n]*n 表示指向同一位置的 [0]*n] 复制n次,改写任意一行的某个数字,都会导致其他行相同位置的数字同步变动。

还有一种方法是:直接设置边界,在边界内循环。

随着方向的改变,边界会向内压缩。

class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        left, right, top, bottle = 0, n-1, 0, n-1
        matrix = [[0]*n for _ in range(n)]
        num = 1
        tar = n*n
        while num <= tar:
            for i in range(left, right+1):
                matrix[top][i] = num
                num += 1
            top += 1
            for i in range(top, bottle+1):
                matrix[i][right] = num
                num += 1
            right -= 1
            for i in range(right, left-1, -1):
                matrix[bottle][i] = num
                num += 1
            bottle -= 1
            for i in range(bottle, top-1, -1):
                matrix[i][left] = num
                num += 1
            left += 1
        return matrix

LeetCode 54. 螺旋矩阵

题目链接:https://leetcode.cn/problems/spiral-matrix/description/
掌握程度:★★☆

这道题就不能用上道题解法一的思路了,因为没办法判断已输出的数字位置。

根据解法二的思路:

class Solution(object):
    def spiralOrder(self, matrix):
        """
        :type matrix: List[List[int]]
        :rtype: List[int]
        """
        left, right, top, bottle = 0, len(matrix[0])-1, 0, len(matrix)-1
        res = []
        while True:
            for i in range(left, right+1):
                res.append(matrix[top][i])
            top += 1
            if top > bottle:
                break
            for i in range(top, bottle+1):
                res.append(matrix[i][right])
            right -= 1
            if left > right:
                break
            for i in range(right, left-1, -1):
                res.append(matrix[bottle][i])
            bottle -= 1
            if top > bottle:
                break
            for i in range(bottle, top-1, -1):
                res.append(matrix[i][left])
            left += 1
            if left > right:
                break
        return res

退出循环的条件不能在while中写,而只能在每次for循环之后写,因为不是每次都能正好转完四个方向才退出循环。

今天就到这里啦,拜拜┏(^0^)┛

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值