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

本文介绍了使用暴力法和滑动窗口算法解决两个编程问题:有序数组的平方和长度最小子数组。通过实例展示了如何用滑动窗口优化求解过程,包括904.水果成篮和76.最小覆盖子串。同时提及了螺旋矩阵II的两种解法。作者通过实战提高了对算法的理解和运用。

题目和思路:

977.有序数组的平方:这题是昨天的拓展题已经做过了,参考昨日博客代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素-优快云博客

209.长度最小的子数组

暴力解法:

遍历所有符合要求的子序列,第一层循环找起点,第二个循环从起点开始找符合要求的子序列的终点。比较难受的一点就是找不到符合要求的序列,我的方法是初始化时设定一个比数组长度大的值,然后每次更新找记录值和当前值中较小的min<res,cur>,最后判断一下如果res比原数组还大就输出0。好歹能跑对先不管他了。以下是代码:

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        res = len(nums)+1
        for i in range(len(nums)):
            cnt = target - nums[i]
            j = i+1
            while cnt > 0 and j < len(nums):
                cnt -= nums[j]
                j+=1
            if cnt <= 0:
                res = min(j-i, res)
        if res > len(nums):return 0
        else:return res

滑动窗口:

其实是双指针的变形,快指针指向终点,慢指针指向起点,子序列的和小于target时移动快指针,大于等于target时记录res并尝试缩小窗口也就是移动慢指针,如此循环最终找到最小的res。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        res = len(nums) + 1
        s, fast,slow = 0,0,0
        for fast in range(res - 1):
            s += nums[fast]
            while s >= target:
                res = min(res,fast - slow + 1)
                s -= nums[slow]
                slow += 1
        return res if res<=len(nums) else 0

*学到一个return res if res<=len(nums) else 0的写法

904. 水果成篮

做了挺久,主要应用滑动窗口和哈希表。fast指针遍历数组,每移动一次查看当前哈希表,如果指向元素就在表内,给表的值加一(用于后续slow指针移动计数);如果哈希表内只有0,1个元素,指向元素不在表内,添加一个键值给这个元素;如果哈希表内有2个元素了已经满了,指向元素还不在表内,说明从slow到fast-1是一个合法的区间,记录长度,并且开始移动slow缩小窗口,缩小的逻辑是每次移动给哈希表对应键的值-1,如果值变成0,就把这个键删掉,创建新的键——fast指向的元素。需要注意的是,如果队尾元素也在哈希表内,就少了一次记录长度的过程,在return的时候直接比较一下就可以得到结果。这样就完成了一个O(2n)的滑动窗口算法。代码如下:

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        l = len(fruits)
        s,fast,slow = 0,0,0
        basket = {}
        for fast in range(l):
            if fruits[fast] in basket: basket[fruits[fast]] += 1
            elif len(basket) < 2: basket[fruits[fast]] = 1 
            else:
                s = max(s,fast - slow)
                while len(basket) == 2:
                    basket[fruits[slow]] -= 1
                    if basket[fruits[slow]] == 0: del basket[fruits[slow]]
                    slow += 1
                basket[fruits[fast]] = 1
        return max(s,l-slow)

76. 最小覆盖子串

终于AC了!

滑动窗口由两个指针 slowfast生成,移动这两个指针来控制窗口。window_counts 字典用来跟踪当前窗口中每个字符的数量,而 dict_t 字典用来存储字符串 t 中每个字符的期望数量。当窗口中包含了 t 中所有所需的字符时,cnt 计数器会与 required 相等,此时尝试通过移动slow指针来缩小窗口,直到找到最小的包含 t 所有字符的子串。最终,函数返回这个最小子串,如果不存在这样的子串,则返回空字符串。代码如下:

class Solution:
    def minWindow(self, s: str, t: str) -> str:
        if not s or not t:
            return ""

        from collections import Counter
        # 计算t中每个字符的数量
        dict_t = Counter(t)
        required = len(dict_t)

        fast,slow = 0,0
        cnt = 0
        window_counts = {}

        res = float('inf'), None, None

        while fast < len(s):
            character = s[fast]
            window_counts[character] = window_counts.get(character, 0) + 1

            if character in dict_t and window_counts[character] == dict_t[character]:
                cnt += 1
            
            while slow <= fast and cnt == required:
                character = s[slow]
                if fast -slow + 1 < res[0]: res = (fast - slow + 1, slow, fast)

                window_counts[character] -= 1
                if character in dict_t and window_counts[character] < dict_t[character]:
                    cnt -= 1

                slow += 1
            
            fast += 1
        
        return "" if res[0]==float('inf') else s[res[1]:(res[2] + 1)]

*学到一个dict[key] = dict.get(key,0)+1的写法。

59. 螺旋矩阵 II

自己写跟代码随想录的想法完全不一样,我逐个更新当前位置值,定义方向"微分"以及边界判断是否需要转向...具体看代码吧...

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
       # 创建一个 n x n 的空矩阵
        matrix = [[0] * n for _ in range(n)]
        # 定义起始位置和方向
        x, y, dx, dy = 0, 0, 1, 0
        # 填充矩阵
        for i in range(1, n * n + 1):
            matrix[y][x] = i
            # 检查下一个位置是否超出边界或已经被访问过
            if matrix[(y + dy) % n][(x + dx) % n]:
                # 改变方向
                dx, dy = -dy, dx
            x += dx
            y += dy
        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          
        count = 1                           

        for offset in range(1, loop + 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 :
            nums[mid][mid] = count 
        return nums

今日总结:

2小时两道拓展题累死自己,但是还是更好的理解了滑动窗口解法。螺旋矩阵小聪明避开了循环问题,自己练习循环不变式的时候还是会出错,勤加练习吧...

一刷训练营感觉数组题目其实技巧性还是有一些的,有些题没有经验完全不知道如何下手。跟完两天收获很大,感觉薄弱的算法正在走向正轨...

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值