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

81 阅读2分钟

977.有序数组的平方

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目第第一想法

要非递减顺序的排序数字,这种情况可以双指针。

left_pt指向左端元素,平方为left_sqright_pt指向右端元素的平方right_sq

比较left_sqright_sq,

如果left_sq较小,则将新元素加入结果数组,更新left_pt向右移动。

直到两个指针碰到,将最后一个元素加入队尾即可。

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left_pt = 0
        right_pt = len(nums) - 1
        sq_res_list = []
        while left_pt < right_pt:
            left_sq = nums[left_pt] * nums[left_pt]
            right_sq = nums[right_pt] * nums[right_pt]
            if left_sq <= right_sq:
                sq_res_list = [right_sq] + sq_res_list
                right_pt -= 1
            else:
                sq_res_list = [left_sq] + sq_res_list
                left_pt += 1
        sq_res_list = [(nums[left_pt] * nums[left_pt])] + sq_res_list
        return sq_res_list

3. 实现过程中遇到的困难

因为nums是非递减序列,所以左右两端开始时,平方值一定哪个都不是最小值。

要拿到最小值只能是最后两个指针重叠的那个值,一定不是哪个较小先放哪个,这样求不出一个非递减序列的。

所以我姑且每次新进来一个值都插到数组的开头,但是Python是否有从头插入的方法?我不清楚,所以直接用列表拼接的。

4. 看完代码随想录之后的想法

l, r, i = 0, len(nums)-1, len(nums)-1
res = [float('inf')] * len(nums) # 需要提前定义列表,存放结果

这样创建列表不会遇到for循环费时间的问题。


class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left_pt = 0
        right_pt = len(nums) - 1
        sq_res_list = [float("inf")] * len(nums)
        res_idx = len(nums) - 1
        while left_pt < right_pt:
            left_sq = nums[left_pt] * nums[left_pt]
            right_sq = nums[right_pt] * nums[right_pt]
            if left_sq <= right_sq:
                sq_res_list[res_idx] = right_sq
                right_pt -= 1
                res_idx -= 1
            else:
                sq_res_list[res_idx] = left_sq
                left_pt += 1
                res_idx -= 1
        sq_res_list[res_idx] = (nums[left_pt] * nums[left_pt])
        return sq_res_list

5. 学习时长

构思:15分钟

实现:10分钟

209.长队最小的子数组

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目第第一想法

连续字数组应该使用滑动窗口。

因为要返回最小长度数组的长度,我觉得还需要一个最小长度的统计量min_len_int

从滑动窗口长度为11 开始,

  1. 如果滑动窗口内的数字比target小,那就应该右侧边界增长一个单元。
  2. 如果滑动窗口内的数字target\ge target,更新min_len_int, 那就应该左侧边界收缩一个单元。

直到右侧边界不能继续增长,或者左侧边界继续收缩将会超出数组右端。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        win_left_bound = 0 
        win_right_bound = 1 # 滑窗是左闭右开
        sum_int = nums[win_left_bound]
        min_len_int = len(nums) + 1
        while win_right_bound <= len(nums):
            # print(win_left_bound, win_right_bound, sum_int)
            if (sum_int < target) and (win_right_bound < len(nums)):
                sum_int += nums[win_right_bound]
                win_right_bound += 1
            elif (sum_int >= target) and (win_left_bound < win_right_bound):
                tmp_len_int = win_right_bound - win_left_bound
                if tmp_len_int < min_len_int:
                    min_len_int = tmp_len_int
                sum_int -= nums[win_left_bound]
                win_left_bound += 1
            else:
                break
        if min_len_int == len(nums) + 1:
            return 0
        return min_len_int

3. 实现过程中遇到的困难

我觉得最困难的就是想到判断条件,既能保证找到我们想要的最小长度,又能让指针不超范围。 一开始想的是控制循环的条件,但是后来发现应该是控制增长和收缩的判断条件,而且如果不满足增长条件,也不满足收缩条件,应该立刻终止了,因为已经不能再移动滑窗了。

4. 看完代码随想录之后的想法

  1. 窗口右侧边界不需要判断,就是数组的遍历指针,
  2. 窗口左侧会根据sum的值超过target而向前,从而搜索滑窗。
  3. 更新最小长度直接可以用min不需要用一个if.
  4. 不是看到双层循环就是O(n2)O(n^2), 每个元素就做一次入队一次出队,实际上是O(2n)O(2n)

5. 学习时长

构思:20分钟

实现:30分钟 (主要浪费在滑窗边界没有坚守住不变量)

看文档:15分钟

59.螺旋矩阵 II

1. 文章链接

代码随想录 (programmercarl.com)

2. 看到题目第第一想法

一定要规定好上下左右四个边界,这个很容易出错。

  1. 从左向右走完一行,那说明从上到下的起点需要+1.
  2. 从上到下走完一列,那说明从右向左的起点需要-1.
  3. 从右向左走完一行,那说明从下到上的起点需要-1.
  4. 从下到上走完一列,那说明从左到右的起点需要+1.
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        res_list = [[0] * n for _ in range(n)]
        count = 1
        left = 0
        right = n
        up = 0
        down = n
        
        while (n * n) >= count:
            # 从左到右
            col_idx = left
            row_idx = up
            while col_idx < right:
                print(row_idx, col_idx, count)
                res_list[row_idx][col_idx] = count
                count += 1
                col_idx += 1
            up += 1
            # 从上到下
            row_idx = up
            col_idx = right - 1
            while row_idx < down:
                print(row_idx, col_idx, count)
                res_list[row_idx][col_idx] = count
                count += 1
                row_idx += 1
            right -= 1
            ## 从右到左
            col_idx = right - 1
            row_idx = down - 1
            while col_idx >= left:
                print(row_idx, col_idx, count)
                res_list[row_idx][col_idx] = count
                count += 1
                col_idx -= 1
            down -= 1
            # 从下到上
            row_idx = down - 1
            col_idx = left
            while row_idx >= up:
                print(row_idx, col_idx, count)
                res_list[row_idx][col_idx] = count
                count += 1
                row_idx -= 1
            left += 1
        return res_list
        

3. 实现过程中遇到的困难

一个是每一行起始位置都要重新赋值,这一点一开始没注意到,只有打印输出后才发现位置有问题。 另一个是二维数组的初始化不对:

我用的是res_list = [[0] * n] * n

而文档中用的是res_list = [[0] * n for _ in range(n)]

这一点ChatGPT如是说:

在Python中,[[0] * n] * n[[0] * n for _ in range(n)]都可以用来创建一个n * n的二维数组,但是它们的内部结构是不同的。

[[0] * n] * n会创建一个包含n个指向相同列表对象的引用的列表。这意味着当您修改其中一个列表时,所有n个列表都会发生改变。例如:

>>> res_list = [[0] * 3] * 3
>>> res_list[0][0] = 1
>>> print(res_list)
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]

在这个例子中,修改res_list[0][0]会导致所有子列表的第一个元素都被修改,因为它们都指向同一个列表对象。这通常不是我们希望看到的行为。

另一方面,[[0] * n for _ in range(n)]会创建一个包含n个不同列表对象的列表。这意味着当您修改其中一个列表时,其他列表不会受到影响。例如:

>>> res_list = [[0] * 3 for _ in range(3)]
>>> res_list[0][0] = 1
>>> print(res_list)
[[1, 0, 0], [0, 0, 0], [0, 0, 0]]

在这个例子中,修改res_list[0][0]只会影响到第一个子列表,其他子列表不会受到影响。

因此,为了避免上述问题,建议使用[[0] * n for _ in range(n)]来创建n * n的二维数组。

4. 看完代码随想录之后的想法

他用了for循环,我用的while

他用offsetn-offset来表示边界。

另外我觉得,我的根据count是否达到n×nn\times n来判断是否跳出循环更简单。

5. 学习时长

1小时。