代码随想录算法训练营第二天| 209.长度最小的子数组、 59.螺旋矩阵II、区间和、开发商购买土地、数组专题总结

209.长度最小的子数组

力扣题目链接(opens new window)

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:

  • 输入:s = 7, nums = [2,3,1,2,4,3]

  • 输出:2

  • 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

提示:

  • 1 <= target <= 10^9

  • 1 <= nums.length <= 10^5

  • 1 <= nums[i] <= 10^5

代码

首先我第一个念头是kmp那种方法,但不知道怎么做。我能想到如果找长度最小的子数组,我需要记录前几个数和的状态,但是我没想到通过滑动窗口去(双指针)去不断移动数和。我想的还是像是前数和那种。

class Solution:

    def minSubArrayLen(self, target: int, nums: list[int]) -> int:
        s = float('inf')
        l, r = 0, 0
        sum = 0
        for r in range(len(nums)):
            # print("l:", l, " " + "r: ", r)
            sum += nums[r]
            while sum >= target:  # 若达到目标值
                s = min(s,r-l+1)
                # print(s)
                sum -= nums[l]
                l += 1

        return 0 if s == float('inf') else s


solution = Solution()
nums = [1,1,1,1,1,1,1,1]
target = 11
s = solution.minSubArrayLen(target, nums)
print(s)  # 输出应该是0

59.螺旋矩阵II

力扣题目链接(opens new window)

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:

输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

代码

一开始我没有完全明白左闭右开的意思,第一次循环准备把三块方块全涂上。但是我马上发现这样子这次循环的最后一边会只有一个方块。感觉很难弄(其实是思路错了)。

然后我想了想检测前方路径的办法,就是如果前方的数是0,就更改,如果不是就顺时针旋转,但是这样子我没想到越界的问题该如何处理(之后我发现可以给循环的每条边弄一个上下左右边界border,让他们涂到墙就自动旋转。

最后我参考的是代码随想录里的。如图

class Solution:

    def generateMatrix(self, n: int) -> list[list[int]]:
        nums = [[0 for i in range(n)] for i in range(n)]  # x是纵轴,y是横轴
        count = 1
        loop = n // 2  # 循环几圈
        start = 0
        mid = n // 2
        offset = 1  # 循环第几圈
        while (loop):
            for j in range(start, n-offset):  # 横列
                nums[start][j] = count
                count += 1
            for i in range(start, n-offset):
                nums[i][n-offset] = count
                count += 1
            for j in range(n - offset, start, -1):
                nums[n - offset][j] = count
                count += 1
            for i in range(n - offset, start, -1):
                nums[i][start] = count
                count += 1
            loop -= 1
            start += 1
            offset += 1
        if n % 2 == 1:
            nums[mid][mid] = n ** 2
        return nums


solution = Solution()
n = 3
arr = solution.generateMatrix(n)
print(arr)

设置loop来显示当前是第几次循环(每次循环画出正方形的四条边)设置n-offset来控制边长,设置start来控制此次loop的起点,最后考虑到n可能为奇数的情况,在为奇数的情况手动填充中心。

但是之后我看了下力扣中K神的代码,我感觉他的更简约,思路更明确。

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        # 初始化边界
        l, r, t, b = 0, n - 1, 0, n - 1
        # 创建 n x n 的二维列表并初始化为 0
        mat = [[0 for _ in range(n)] for _ in range(n)]
        # 初始值和目标值
        num, tar = 1, n * n
        
        while num <= tar:
            # 从左到右填充上边
            for i in range(l, r + 1):
                mat[t][i] = num
                num += 1
            t += 1  # 上边界下移
            
            # 从上到下填充右边
            for i in range(t, b + 1):
                mat[i][r] = num
                num += 1
            r -= 1  # 右边界左移
            
            # 从右到左填充下边
            for i in range(r, l - 1, -1):
                mat[b][i] = num
                num += 1
            b -= 1  # 下边界上移
            
            # 从下到上填充左边
            for i in range(b, t - 1, -1):
                mat[i][l] = num
                num += 1
            l += 1  # 左边界右移
        
        return mat  # 返回生成的矩阵

58. 区间和

本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)

题目链接(opens new window)

题目描述

给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。

输入描述

第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。

输出描述

输出每个指定区间内元素的总和。

输入示例

5
1
2
3
4
5
0 1
1 3

输出示例

3
9

数据范围:

0 < n <= 100000

代码

学过前缀和,一看直接秒杀了,改改代码就好。

# 读取所有输入,并按空格分割成列表
data = input().split()

# 初始化索引变量
index = 0

# 读取数组长度 n
n = int(data[index])
index += 1  # 更新索引

# 初始化数组 vec
vec = []

# 初始化前缀和数组 p
p = [0] * n

for i in range(n):
    # 从数据中读取数组元素并添加到 vec 中
    vec.append(int(data[index + i]))
    p[i] = vec[i] + p[i-1]  #p[i-1]当i=0时是最后一个元素


index += n  # 更新索引

# 初始化结果列表
results = []

# 处理查询
while index < len(data):
    # 读取查询区间的起始索引 a 和结束索引 b
    a = int(data[index])
    b = int(data[index + 1])
    index += 2  # 更新索引

    # 计算区间 [a, b] 的和
    if a == 0:
        # 如果 a 是 0,直接使用 p[b] 作为结果
        sum_value = p[b]
    else:
        # 否则,计算 p[b] - p[a-1]
        sum_value = p[b] - p[a - 1]

    # 将结果添加到 results 列表中
    results.append(sum_value)

# 输出每个查询的结果
for result in results:
    print(result)

44. 开发商购买土地

本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)

题目链接(opens new window)

【题目描述】

在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。

现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。

然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。

为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。

注意:区块不可再分。

【输入描述】

第一行输入两个正整数,代表 n 和 m。

接下来的 n 行,每行输出 m 个正整数。

输出描述

请输出一个整数,代表两个子区域内土地总价值之间的最小差距。

【输入示例】

3 3 1 2 3 2 1 3 1 2 3

【输出示例】

0

【提示信息】

如果将区域按照如下方式划分:

1 2 | 3

2 1 | 3

1 2 | 3

两个子区域内土地总价值之间的最小差距可以达到 0。

【数据范围】:

  • 1 <= n, m <= 100;

  • n 和 m 不同时为 1。

代码

看到这个正方形矩阵,我第一反应就是横纵轴前缀和,但是我不知道该如何计算最小切割差值。代码给的方法是弄个总和,再循环一列列求。代码是

diff = abs(total_sum - 2 * horizontal_cut)

我的想法是给横纵轴前缀和再弄个前缀和,这样就能快速计算差值。感觉代码给的方法在时间复杂度上会快一点,因为并不需要多次查找,只需要一次找最小差距

import sys
input = sys.stdin.read
# 读取所有输入,并按空格分割成列表
data = input().split()

# 初始化索引变量
idx = 0

# 读取矩阵的行数 n 和列数 m
n = int(data[idx])
idx += 1
m = int(data[idx])
idx += 1

# 初始化总和 sum
total_sum = 0
# 初始化二维数组 vec
vec = []

# 读取矩阵的元素
for i in range(n):
    row = []
    for j in range(m):
        num = int(data[idx])
        idx += 1
        row.append(num)
        total_sum += num
    vec.append(row)

# 统计每行的和
horizontal = [0] * n
for i in range(n):
    for j in range(m):
        horizontal[i] += vec[i][j]

# 统计每列的和
vertical = [0] * m
for j in range(m):
    for i in range(n):
        vertical[j] += vec[i][j]

# 初始化结果为无穷大
result = float('inf')

# 计算水平切割的最小差值
horizontal_cut = 0
for i in range(n):
    horizontal_cut += horizontal[i]
    # 计算当前切割的差值
    diff = abs(total_sum - 2 * horizontal_cut)
    # 更新结果
    result = min(result, diff)

# 计算垂直切割的最小差值
vertical_cut = 0
for j in range(m):
    vertical_cut += vertical[j]
    # 计算当前切割的差值
    diff = abs(total_sum - 2 * vertical_cut)
    # 更新结果
    result = min(result, diff)

# 输出结果
print(result)

总结

二分法

循环不变量原则,只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节。

双指针法

双指针法(快慢指针法):通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。

  • 暴力解法时间复杂度:O(n^2)
  • 双指针时间复杂度:O(n)

滑动窗口

滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)的暴力解法降为O(n)。

模拟行为

真正解决题目的代码都是简洁的,或者有原则性的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值