数组 - 代码随想录

数组 - 代码随想录
Mr.L数组
数组刷题记录,基于代码随想录整理。
- 704 二分查找(边界定义)
- 27 移除元素(快慢指针)
- 977 有序数组平方(双端指针)
- 209 长度最小子数组(滑动窗口)
- 59 螺旋矩阵 II(模拟)
- 58 区间和(前缀和)
- 44 开发商购买土地(二维切分 + 前缀思想)
- 二分:704
- 双指针:27、977
- 滑动窗口:209
- 模拟:59
- 前缀和:58、44
704. 二分查找
问题描述:
给定一个
n个元素有序的(升序)整型数组nums和一个目标值target,写一个函数搜索nums中的target,如果target存在返回下标,否则返回-1。你必须编写一个具有
O(log n)时间复杂度的算法。
示例 1:
1 | 输入: nums = [-1,0,3,5,9,12], target = 9 |
示例 2:
1 | 输入: nums = [-1,0,3,5,9,12], target = 2 |
提示:
- 你可以假设
nums中的所有元素是不重复的。 n将在[1, 10000]之间。nums的每个元素都将在[-9999, 9999]之间。
思路
二分法前提: 数组为有序数组,且无重复元素,因为一旦有重复元素,使用二分查找法返回的元素下标可能不是唯一的;
区间定义: 一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
[left, right]:target在一个左闭右闭的区间,有如下两点:while (left <= right)使用<=,因为left == right是有意义的,所以使用<=if (nums[middle] > target),right要赋值为middle - 1,接下来要查找的左区间结束下标位置就是 middle - 1;
[left, right):target在一个左闭右开的区间里,有如下两点:while (left < right),这里使用<,因为left == right在区间[left, right)没有意义if (nums[middle] > target),right更新为middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
代码
1 | class Solution { |
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
1 | class Solution { |
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
27. 移除元素
问题描述:
给你一个数组
nums和一个值val,你需要 原地 移除所有数值等于val的元素。元素的顺序可能发生改变。然后返回nums中与val不同的元素的数量。假设
nums中不等于val的元素数量为k,要通过此题,您需要执行以下操作:
- 更改
nums数组,使nums的前k个元素包含不等于val的元素。nums的其余元素和nums的大小并不重要。- 返回
k。
示例 1:
1 | 输入:nums = [3,2,2,3], val = 3 |
示例 2:
1 | 输入:nums = [0,1,2,2,3,0,4,2], val = 2 |
提示:
0 <= nums.length <= 1000 <= nums[i] <= 500 <= val <= 100
思路
因为数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖,所以引入双指针法;
双指针法: 通过一个快指针和慢指针在一个for循环下完成两个for循环的工作。
- 快指针:寻找数组的元素 ,若值等于
val跳过,新数组就是不含有目标元素的数组; - 慢指针:指向更新新数组下标的位置;
代码
1 | class Solution { |
注意这些实现方法并没有改变元素的相对位置!
- 时间复杂度:O(n)
- 空间复杂度:O(1)
977.有序数组的平方
问题描述
给你一个按 非递减顺序 排序的整数数组
nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
1 | 输入:nums = [-4,-1,0,3,10] |
示例 2:
1 | 输入:nums = [-7,-3,2,3,11] |
提示:
1 <= nums.length <= 104-104 <= nums[i] <= 104nums已按 非递减顺序 排序
进阶:
- 请你设计时间复杂度为
O(n)的算法解决本问题
思路
因为数组是有序的, 负数平方之后可能为最大数,所以数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法 了
i指向起始位置,j指向终止位置。定义一个新数组
result,和A数组一样的大小,让k指向result数组终止位置。如果
A[i] * A[i] < A[j] * A[j]那么result[k--] = A[j] * A[j];如果
A[i] * A[i] >= A[j] * A[j]那么result[k--] = A[i] * A[i];
代码
1 | class Solution { |
209.长度最小的子数组 - 力扣(LeetCode)
问题描述
给定一个含有
n个正整数的数组和一个正整数target。找出该数组中满足其总和大于等于
target的长度最小的 子数组[numsl, numsl+1, ..., numsr-1, numsr],并返回其长度0。如果不存在符合条件的子数组,返回0。
示例 1:
1 | 输入:target = 7, nums = [2,3,1,2,4,3] |
示例 2:
1 | 输入:target = 4, nums = [1,4,4] |
示例 3:
1 | 输入:target = 11, nums = [1,1,1,1,1,1,1,1] |
提示:
1 <= target <= 1091 <= nums.length <= 1051 <= nums[i] <= 104
进阶:
- 如果你已经实现
O(n)时间复杂度的解法, 请尝试设计一个O(n log(n))时间复杂度的解法。
思路
滑动窗口:根据当前子序列和大小的情况,不断的调节子序列的起始位置和终止位置,从而得出我们想要的结果。
主要确定如下三点:
- 窗口内是什么?
- 如何移动窗口的起始位置?
- 如何移动窗口的结束位置?
窗口: 满足其和 ≥s 的长度最小的连续子数组。
窗口的起始位置如何移动: 如果当前窗口的值≥s了,窗口就要向前移动了(也就是该缩小了)。
窗口的结束位置如何移动: 窗口的结束位置就是遍历数组的指针,也就是for循环里的索引。
代码
1 | class Solution { |
59.螺旋矩阵 II - 力扣(LeetCode)
问题描述
给你一个正整数
n,生成一个包含1到n2所有元素,且元素按顺时针顺序螺旋排列的n x n正方形矩阵matrix。
示例 1:

1 | 输入:n = 3 |
示例 2:
1 | 输入:n = 1 |
提示:
1 <= n <= 20
思路
本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。
求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程,由外向内一圈一圈这么画下去。:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人。
螺旋矩阵遍历顺序(可折叠)
- 左到右填充上边
- 上到下填充右边
- 右到左填充下边
- 下到上填充左边
每一圈结束后收缩边界,继续下一圈。
代码
1 | class Solution { |
58. 区间和
本题为代码随想录后续扩充题目,还没有视频讲解,顺便让大家练习一下ACM输入输出模式(笔试面试必备)
问题描述
给定一个整数数组
Array,请计算该数组在每个指定区间内元素的总和。输入: 第一行输入为整数数组
Array的长度n,接下来n行,每行一个整数,表示数组的元素。随后的输入为需要计算总和的区间,直至文件结束。输出: 输出每个指定区间内元素的总和。
输入示例
1 | 5 |
输出示例
1 | 3 |
提示信息
数据范围:0 < n <= 100000
思路
前缀和: 重复利用计算过的子数组之和,降低区间查询需要累加计算的次数。在涉及计算区间和的问题时非常有用!
我们先做累加,即
p[i]表示 下标0到i的vec[i]累加之和。在
vec数组上下标2到下标5之间的累加和,用p[5] - p[1]就可以了。1
2
3p[1] = vec[0] + vec[1];
p[5] = vec[0] + vec[1] + vec[2] + vec[3] + vec[4] + vec[5];
p[5] - p[1] = vec[2] + vec[3] + vec[4] + vec[5];在使用前缀和的时候,分不清前缀和的区间,建议画一画图,模拟一下 思路会更清晰
前缀和区间示意(可折叠)
以 p[i] 表示 nums[0...i] 之和:
- 区间
[a,b]的和为p[b] - p[a-1](当a > 0) - 当
a = 0时,区间和直接是p[b]
代码
1 |
|
44.开发商购买土地
问题描述
在一个城市区域内,被划分成了n * m个连续的区块,每个区块都拥有不同的权值,代表着其土地价值。目前,有两家开发公司,A 公司和 B 公司,希望购买这个城市区域的土地。
现在,需要将这个城市区域的所有区块分配给 A 公司和 B 公司。
然而,由于城市规划的限制,只允许将区域按横向或纵向划分成两个子区域,而且每个子区域都必须包含一个或多个区块。 为了确保公平竞争,你需要找到一种分配方式,使得 A 公司和 B 公司各自的子区域内的土地总价值之差最小。
注意:区块不可再分。
输入描述:
第一行输入两个正整数,代表 n 和 m。
接下来的 n 行,每行输出 m 个正整数。
输出描述:
- 请输出一个整数,代表两个子区域内土地总价值之间的最小差距。
输入示例
1 | 3 3 |
输出示例
1 | 0 |
提示信息
如果将区域按照如下方式划分:
1 2 | 3
2 1 | 3
1 2 | 3
两个子区域内土地总价值之间的最小差距可以达到 0。
数据范围:
1 <= n, m <= 100;
n 和 m 不同时为 1。
思路
本题可以使用前缀和 的思路来求解,先将行方向,和列方向的和求出来用于横切或者纵切,方便知道划分的两个区间的和。
代码
1 |
|
- 时间复杂度:
O(n^2)
总结
二分法: 左闭右闭([left,right])、左闭右开([left,right)),核心是边界定义与循环条件一致。
双指针法: 用快慢指针在一个循环中完成元素筛选/重排。
滑动窗口: 根据窗口内和的变化,动态移动起点与终点。
前缀和: 预处理累计和,把多次区间查询降为 O(1)。

