当前位置: 首页 > news >正文

Day2 第一章 数组part02

滑动窗口

定义

所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果

在暴力解法中,是一个for循环滑动窗口的起始位置,一个for循环为滑动窗口的终止位置,用两个for循环 完成了一个不断搜索区间的过程。

那么滑动窗口如何用一个for循环来完成这个操作呢。

首先要思考 如果用一个for循环,那么应该表示 滑动窗口的起始位置,还是终止位置。

如果只用一个for循环来表示 滑动窗口的起始位置,那么如何遍历剩下的终止位置?

此时难免再次陷入 暴力解法的怪圈。

所以 只用一个for循环,那么这个循环的索引,一定是表示 滑动窗口的终止位置。

与双指针的区别

双指针是关心两个点,首位的数

滑动窗口关心的是区间内的

停止条件及左右指针

外层循环的停止条件永远是「右指针走到头」,而不是左右指针都走到头。

右指针负责"遍历完所有可能性",所以它走到头才停;
左指针只负责"优化当前窗口",所以它跟着条件走,条件没了它就停。
左指针永远是被右指针"拖着走"的,绝不会自己跑到终点。

长度最小的子数组

题目

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。 示例: 输入:s = 7, nums = [2,3,1,2,4,3] 输出:2 解释:子数组 [4,3] 是该条件下的长度最小的子数组。

思路

left right都在最左边,然后右指针往右搜索,直到窗口内的数满足条件,然后记录长度,则左窗口-1,并且记录下长度,直到不满足条件,然后右指针再往右搜索,往复直到右指针到达最优侧,结束。

代码

#(版本一)滑动窗口法 class Solution: def minSubArrayLen(self, s: int, nums: List[int]) -> int: l = len(nums) left = 0 right = 0 min_len = float('inf') cur_sum = 0 #当前的累加值 while right < l: cur_sum += nums[right] while cur_sum >= s: # 当前累加值大于目标值 min_len = min(min_len, right - left + 1) cur_sum -= nums[left] left += 1 right += 1 return min_len if min_len != float('inf') else 0

注意点:

1. 他的赋值,学习一下:float('inf')

2. 最后一句是条件表达式:return min_len if min_len != float('inf') else 0

A if 条件 else B= "条件成立就取 A,不成立就取 B"

题目链接

209. 长度最小的子数组 - 力扣(LeetCode)

螺旋矩阵II

题目

给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。 示例: 输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

思路

本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。

代码

class Solution: def generateMatrix(self, n: int) -> list[list[int]]: # 1. 初始化 n*n 矩阵和四个边界 matrix = [[0] * n for _ in range(n)] top, bottom, left, right = 0, n - 1, 0, n - 1 num = 1 # 当前要填入的数字 target = n * n # 总共需要填的数字个数 # 2. 只要还有数字没填完,就继续转圈 while num <= target: # ① 上边:从左到右 → for col in range(left, right + 1): matrix[top][col] = num num += 1 top += 1 # 上边界下移 # ② 右边:从上到下 ↓ for row in range(top, bottom + 1): matrix[row][right] = num num += 1 right -= 1 # 右边界左移 # ③ 下边:从右到左 ← for col in range(right, left - 1, -1): matrix[bottom][col] = num num += 1 bottom -= 1 # 下边界上移 # ④ 左边:从下到上 ↑ for row in range(bottom, top - 1, -1): matrix[row][left] = num num += 1 left += 1 # 左边界右移 return matrix

题目链接

59. 螺旋矩阵 II - 力扣(LeetCode)

区间和

题目

58. 区间和(第九期模拟笔试) 题目描述 给定一个整数数组 Array,请计算该数组在每个指定区间内元素的总和。 输入描述 第一行输入为整数数组 Array 的长度 n,接下来 n 行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间下标:a,b (b > = a),直至文件结束。 输出描述 输出每个指定区间内元素的总和。 输入示例 5 1 2 3 4 5 0 1 1 3 输出示例 3 9 提示信息 数据范围: 0 < n <= 100000

就是先给一个数字,比如说5,那么数组就是[1, 2, 3, 4, 5]

然后再给一个区间,比如说0 1,那么就是从0到1,在比如说4 9,那么就是从4到9

思路

前缀和,用空间换时间,记录下每一次sum到值,比如说sum3就是前三个数和,sum10就是前10个数的和,那么我们求区间4 9,那就是sum10-sum4

代码

import sys input = sys.stdin.read def main(): data = input().split() index = 0 n = int(data[index]) index += 1 vec = [] for i in range(n): vec.append(int(data[index + i])) index += n p = [0] * n presum = 0 for i in range(n): presum += vec[i] p[i] = presum results = [] while index < len(data): a = int(data[index]) b = int(data[index + 1]) index += 2 if a == 0: sum_value = p[b] else: sum_value = p[b] - p[a - 1] results.append(sum_value) for result in results: print(result) if __name__ == "__main__": main()

两个学习的点

1. input函数改成,input = sys.stdin.read,然后注意, data = input().split(),有个括号

2. print函数,print('\n'.join(map(str, result))),就是先map(str, result)),把int改成str,然后'\n'.join(map(str, result)),['3', '9']'3\n9'

题目链接

58. 区间和(第九期模拟笔试)

开发商购买土地

题目

题目描述 在一个城市区域内,被划分成了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。

思路

还是利用区间和的思路,这里成为前缀和,将每一行直接累加,然后比较差值,先横着切然后竖着切,最后得出差值最小的

代码

def main(): import sys input = sys.stdin.read data = input().split() idx = 0 n = int(data[idx]) idx += 1 m = int(data[idx]) idx += 1 sum = 0 vec = [] for i in range(n): row = [] for j in range(m): num = int(data[idx]) idx += 1 row.append(num) 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') horizontalCut = 0 for i in range(n): horizontalCut += horizontal[i] result = min(result, abs(sum - 2 * horizontalCut)) verticalCut = 0 for j in range(m): verticalCut += vertical[j] result = min(result, abs(sum - 2 * verticalCut)) print(result) if __name__ == "__main__": main()

题目链接

44. 开发商购买土地(第五期模拟笔试)

最后两道题:前缀和和区间和

核心本质

只要题目涉及“连续子数组/子矩阵的元素之和”,无论它是让你“查询”还是让你“枚举分割点”,底层数学原理都是前缀和。

什么时候使用

出现“和”,连续一段,子区间,连续重复查询,就可以想到前缀和

总结数组部分

数组经典题目

二分法

这道题目呢,考察数组的基本操作,思路很简单,但是通过率在简单题里并不高,不要轻敌。

可以使用暴力解法,通过这道题目,如果追求更优的算法,建议试一试用二分法,来解决这道题目

  • 暴力解法时间复杂度:O(n)
  • 二分法时间复杂度:O(logn)

在这道题目中我们讲到了循环不变量原则,只有在循环中坚持对区间的定义,才能清楚的把握循环中的各种细节。

二分法是算法面试中的常考题,建议通过这道题目,锻炼自己手撕二分的能力

双指针法

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

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

这道题目迷惑了不少同学,纠结于数组中的元素为什么不能删除,主要是因为以下两点:

  • 数组在内存中是连续的地址空间,不能释放单一元素,如果要释放,就是全释放(程序运行结束,回收内存栈空间)。
  • C++中vector和array的区别一定要弄清楚,vector的底层实现是array,封装后使用更友好。

双指针法(快慢指针法)在数组和链表的操作中是非常常见的,很多考察数组和链表操作的面试题,都使用双指针法。

滑动窗口

本题介绍了数组操作中的另一个重要思想:滑动窗口。

  • 暴力解法时间复杂度:O(n^2)
  • 滑动窗口时间复杂度:O(n)

本题中,主要要理解滑动窗口如何移动 窗口起始位置,达到动态更新窗口大小的,从而得出长度最小的符合条件的长度。

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

如果没有接触过这一类的方法,很难想到类似的解题思路,滑动窗口方法还是很巧妙的。

模拟行为​​​​​​​

模拟类的题目在数组中很常见,不涉及到什么算法,就是单纯的模拟,十分考察大家对代码的掌控能力。

在这道题目中,我们再一次介绍到了循环不变量原则,其实这也是写程序中的重要原则。

相信大家有遇到过这种情况: 感觉题目的边界调节超多,一波接着一波的判断,找边界,拆了东墙补西墙,好不容易运行通过了,代码写的十分冗余,毫无章法,其实真正解决题目的代码都是简洁的,或者有原则性的,大家可以在这道题目中体会到这一点。

前缀和​​​​​​​

前缀和的思路其实很简单,但非常实用,如果没接触过的录友,也很难想到这个解法维度,所以 这是开阔思路 而难度又不高的好题。

​​​​​​​

总结图片

http://www.jsqmd.com/news/1128715/

相关文章:

  • 嵌入式系统讨论
  • C# 自定义特性(Attribute)+ 反射读取特性 +WinForm 自定义控件常用特性
  • 收藏!小白程序员也能轻松掌握大模型核心玩法,打造个人专属AI优势
  • 3步快速上手FanControl:Windows风扇智能控制终极指南
  • ORB-SLAM3 mFeatVec
  • 全球小程序开发工具:餐宝盈/BBWEYY/比文云/Siter.io/Weblium实测对比,含零代码SAAS、AI编程、源码定制交付
  • Redis初识
  • 量子通信产业化:从保密通信到全域应用,重构信息安全底层体系
  • 告别混乱!3个技巧让mRemoteNG成为你的远程连接管理专家
  • 51单片机录音笔(带闹钟)
  • AI驱动的知识图谱如何重塑信息管理
  • 超厉害!Windows CE Dreamcast 社区版让 Dreamcast 变身可用系统
  • 【共创季稿事节】待办清单应用开发实战:ArkTS 列表渲染与状态管理深度解析
  • 智能体“互联互通“的国家标准发布:AI助手终于能互相聊天了
  • 终极音乐歌词批量获取神器:163MusicLyrics完整使用指南
  • C++语言基础3:用户自定义类型“class”详细讲解
  • 【Qt】控件(二) (geometry及与frameGeometry的区别)
  • B. Good times Good times(Codeforces 2241)
  • 51单片机电冰箱保护器
  • 独立站搭建工具测评:BBWEYY/比文云/Prismic/Vercel/Supabase(2026年7月更新)含零代码SAAS、AI编程、源码定制交付
  • 英语单词测试
  • 2026最新AI大模型零基础入门学习计划|小白程序员专属,从理论到实战直通高薪
  • 从零开始学AI:2周上手,半年做项目,1年工程落地(收藏版)
  • 训练框架实战——FSDP vs DeepSpeed,选框架不是选最好的
  • Audacity音频编辑完全指南:从零开始制作专业音频的免费方案
  • 【共创季稿事节】记事本应用开发:ArkTS 中编辑模式切换与文本处理
  • Kimi LeetCode 3464. 正方形上的点之间的最大距离 Java实现
  • 数据产业服务分类(29)——数据产业——数字经济与数据产业
  • AI 文档助手知识库:先清理旧文档,再接检索
  • 如何用ChanlunX缠论插件3分钟完成专业股票技术分析