Day 4
Leetcode 283、移动零
C++
class Solution {
public:
void moveZeroes(vector<int>& nums) {
// 设置一个变量,用来指向经过一系列操作后数组中所有为 0 元素的第一个位置上
// 一开始默认在索引为 0 的位置
int slow = 0;
// 从头到尾遍历数组
// 遍历完毕之后,slow 指向了一个为 0 的元素,或者如果数组中不存在 0 ,就和 fast 一样,超过了数组的范围
for (int fast = 0; fast < nums.size(); fast++) {
// 在遍历过程中,如果发现访问的元素是非 0 元素
// 说明 slow 不在正确的位置上,需要向后移动,寻找合适的位置
if (nums[fast] != 0) {
// 这个时候,原先 slow 的值需要被 fast 的值覆盖
nums[slow] = nums[fast];
// slow 需要向后移动,寻找合适的位置
slow++;
}
}
// 接下来,只需要把 slow 极其后面所有的元素都设置为 0 就行
for (int i = slow; i < nums.size(); i++) {
// 都设置为 0
nums[i] = 0;
}
}
};
Python
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
# 设置一个变量,用来指向经过一系列操作后数组中所有为 0 元素的第一个位置上
# 一开始默认在索引为 0 的位置
slow = 0
# 从头到尾遍历数组
# 遍历完毕之后,slow 指向了一个为 0 的元素,或者如果数组中不存在 0 ,就和 fast 一样,超过了数组的范围
for fast in range(len(nums)) :
# 在遍历过程中,如果发现访问的元素是非 0 元素
# 说明 slow 不在正确的位置上,需要向后移动,寻找合适的位置
if nums[fast] != 0:
# 这个时候,原先 slow 的值需要被 fast 的值覆盖
nums[slow] = nums[fast]
# slow 需要向后移动,寻找合适的位置
slow += 1
# 接下来,只需要把 slow 极其后面所有的元素都设置为 0 就行
for i in range(slow,len(nums)) :
# 都设置为 0
nums[i] = 0
复杂度分析
- 时间复杂度:O(n),其中 n 为序列长度。每个位置至多被遍历两次。
- 空间复杂度:O(1)。只需要常数的空间存放若干变量。
LeetCode 485、最大连续 1 的个数
Python
class Solution(object):
def findMaxConsecutiveOnes(self, nums):
# 最后一个 0 所在的索引位置
lastZero = -1
# 结果
ans = 0
# 从左到右访问数组 nums
for i, num in enumerate(nums):
# 1、当前元素为 0 ,更新 lastZero
if num == 0:
lastZero = i
# 2、否则说明当前元素为 1
else:
# 通过 lastZero 可以获取当前元素距离最前面的 1 的个数
# 对比之前的 ans ,更新获取最大值
ans = max(ans, i - lastZero)
# 返回结果
return ans
LeetCode 26、删除有序数组中的重复项
C++
class Solution {
public:
int removeDuplicates(vector<int>& nums) {
// 指针 i 进行数组遍历
int n = nums.size();
// 指针 j 指向即将被赋值的位置
int j = 0;
// 开始对数组进行遍历
for (int i = 0 ; i < n ; i++) {
// 进行筛选
if ( i == 0 || nums[i] != nums[i - 1]) {
// 赋值
nums[j] = nums[i];
// j 移动
j++;
}
}
// 获取结果
return j ;
}
};
Python
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
# 指针 i 进行数组遍历
n = len(nums)
# 指针 j 指向即将被赋值的位置
j = 0
# 开始对数组进行遍历
for i in range(n):
# 进行筛选
if i == 0 or nums[i] != nums[i - 1] : # 注意初始的情况
# 赋值
nums[j] = nums[i]
# j 移动
j += 1
# 获取结果
return j
LeetCode 80、删除有序数组中的重复项II
Python
class Solution(object):
def removeDuplicates(self, nums):
# 指针 slow 指向即将被赋值的位置
slow = 0
# 开始对数组进行遍历
for fast in range(len(nums)):
# 进行筛选
if slow < 2 or nums[fast] != nums[slow - 2]:
# 赋值
nums[slow] = nums[fast]
# slow 移动
slow += 1
# 获取结果
return slow
Recap: 排序算法
冒泡排序
void bublleSort(vector<int> & nums)
{
int n = nums.size()
// n个数,冒n-1个泡整个数组就有序了
for(int i=0; i < n - 1; i++)
{
// 第i次排序需要对比n-1-i次(第0次冒泡,对比n-1次,后面每次冒泡都少比较一次,以此类推
for(int j=0; j < n-i-1; j++)
{
if(nums[j] > nums[j+1])
{
int temp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = nums[j];
}
}
}
}
插入排序
上面的做法在元素插入时经常要进行元素交换,因此在插入时可以采取下图的做法。
for(int i=1; i < n-1; i++) // 第一个数当作已经是排好序的数组,将后面元素插入进来
{
int key = a[i]; // 一会儿比a[i]小的元素会依次后移覆盖掉a[i],因此先存起来
// 每次插入,将遍历到的第i个元素依次和前面所有的已排序元素进行对比,直到找到比它小的元素为止并插入在该元素后面
int j = i-1;
while(j>=0 && a[j] > key)
{
// 只要j处元素比key大,就将其往后挪一位
a[j+1] = a[j];
j--;
}
a[j+1] = key;
}
选择排序
for(int i=0; i < n-1; i++) // 总共循环n-1次,每次循环找到剩下未排序索引中最小的与已排序数组的最后一位进行交换
{
int index = i; // 初始化本轮未排序数组中的最小元素下标,先假定为索引i,即本轮已排序数组的后一个位置
for(int j=i+1; j < n; j++) // 从i+1开始,对后面的一共n-i-1个元素进行逐个对比查找最小值索引
{
if(nums[j] < nums[index])
{
index = j; // 如果找到较小的,更新最小值索引
}
}
// 本轮遍历完,找到了未排序数组中的最小值,与nums[i]元素进行交换,加入已排序数组的最后一个元素
int temp = nums[i];
nums[i] = nums[index];
nums[index] = temp;
}
【模拟】小红书2023秋招提前批-小红的数组构造
python
n, k = map(int, input().split())
print((1 + n) * n * k // 2)
C++
#include <iostream>
using namespace std;
int main() {
long n, k;
cin >> n >> k;
cout << (1 + n) * n * k / 2 << endl;
return 0;
}
【模拟】科大讯飞2023非凡计划-将企鹅击落水中最小的力
python
n = int(input())
nums = list(map(int, input().split()))
# 寻找企鹅所在的方块,这里使用list的index方法也可以
for i, num in enumerate(nums):
if num == -1:
k = i
break
# 分别计算子数组nums[:k]和nums[k+1:]中的最小值
# 并将两个最小值相加即为答案
print(min(nums[:k]) + min(nums[k+1:]))
C++
#include <iostream>
#include <vector>
#include <climits>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
int k = 0;
for (int i = 0; i < n; i++) {
if (nums[i] == -1) {
k = i;
break;
}
}
int leftMin = INT_MAX;
for (int i = 0; i < k; i++) {
leftMin = min(leftMin, nums[i]);
}
int rightMin = INT_MAX;
for (int i = k + 1; i < n; i++) {
rightMin = min(rightMin, nums[i]);
}
int answer = leftMin + rightMin;
cout << answer << endl;
return 0;
}
【模拟】美团2023秋招-小美走公路
只有两条可能的路径,只需要计算顺时针和逆时针的两个结果进行比较即可。注意:终点也有可能在起点前面。
Python
n = int(input())
distance = list(map(int, input().split()))
x, y = map(int, input().split())
# 令x是索引在前的车站,y是索引在后的车站
# 另外,由于题目所给的车站索引是从1开始的
# 为了能够映射到数组distance中的索引
# 需要进行-1的操作
x, y = (x-1, y-1) if x < y else (y-1, x-1)
# 求两段距离
# 第一段为:从x顺时针走到y,对应的距离为sum(distance[x:y])
# 第二段为:从y顺时针走到x,对应的距离为sum(distance[:x]) + sum(distance[y:])
# 两者取较小值,即为答案
print(min(sum(distance[x:y]), sum(distance[:x]) + sum(distance[y:])))
C++
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> distance(n);
for (int i = 0; i < n; i++) {
cin >> distance[i];
}
int x, y;
cin >> x >> y;
if (x > y) {
swap(x, y);
}
long long sum1 = 0;
long long sum2 = 0;
for (int i = x - 1; i < y - 1; i++) {
sum1 += distance[i];
}
for (int i = 0; i < x - 1; i++) {
sum2 += distance[i];
}
for (int i = y - 1; i < n; i++) {
sum2 += distance[i];
}
cout << min(sum1, sum2) << endl;
return 0;
}
【模拟】OPPO2023秋招提前批-小欧数组求和
Python
# 数组长度n,操作次数q
n, q = map(int, input().split())
# 初始数组nums
nums = list(map(int, input().split()))
# 计算初始数组nums的和
nums_sum = sum(nums)
# 储存q次修改的结果
ans = list()
# 循环q次,修改q次
for _ in range(q):
# 修改的位置i和修改的内容x
i, x = map(int, input().split())
# 注意i输入表示的是第i个元素,将其改为索引需要-1
i -= 1
# 将nums[i]修改为x,则整体的和需要减少nums[i],增加x
nums_sum -= nums[i]
nums_sum += x
# 同时nums[i]被修改为x
nums[i] = x
# 将本次修改后的数组和nums_sum储存在ans中,方便后续按顺序输出
ans.append(nums_sum)
for num in ans:
print(num)
C++
#include <iostream>
using namespace std;
int main() {
int n, q;
cin >> n >> q;
int nums[n];
for (int i = 0; i < n; i++) {
cin >> nums[i];
}
int numsSum = 0;
for (int i = 0; i < n; i++) {
numsSum += nums[i];
}
int ans[q];
for (int k = 0; k < q; k++) {
int i, x;
cin >> i >> x;
i--;
numsSum -= nums[i];
numsSum += x;
nums[i] = x;
ans[k] = numsSum;
}
for (int i = 0; i < q; i++) {
cout << ans[i] << endl;
}
return 0;
}
时空复杂度
- 时间复杂度:
O(N+q)
。第一次计算nums
的和的时间复杂度为O(N)
,进行q
次修改的时间复杂度为O(q)
- 空间复杂度:
O(1)
。仅需若干常数变量。
【模拟】大疆2023秋招-农田中作物的最大产量
- 首先,读取输入,获取农田的行数和列数,然后创建一个二维数组来存储农田的作物产量。
- 接着,遍历二维数组,计算每行和每列的总产量,并累加到一个计数器中。
- 初始化一个变量,用于记录最大产量。
- 使用两层循环遍历每个位置,计算当前位置所在行和列的总产量,减去当前位置的作物产量,得到当前位置的最大产量。然后更新最大产量变量。
- 最后,将最大产量与总产量相加,得到最终结果,并输出。
Python
# 输入二维数组的行数和列数
n, m = map(int, input().split())
# 输入二维数组
grid = list()
# 遍历n次,每次输入一行
for _ in range(n):
grid.append(list(map(int, input().split())))
# 记录每一行的总和
row_sum = [sum(row) for row in grid]
# 记录每一列的总和
col_sum = [sum(col) for col in zip(*grid)]
# 初始化灌溉能够得到的增量inc为0
inc = 0
# 双重遍历二维矩阵
# 考虑以点(i,j)作为行列交点的情况
for i in range(n):
for j in range(m):
# 由于点(i,j)在行、列中被重复计算了两次
# 因此需要减去一次grid[i][j]
# 才能得到以点(i,j)作为交点的灌溉结果
cur = row_sum[i] + col_sum[j] - grid[i][j]
# 更新增量inc
inc = max(inc, cur)
# 最终的答案为增量inc加上原二维数组中所有元素求和
print(inc + sum(row_sum))
C++
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> grid(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> grid[i][j];
}
}
vector<int> rowSum(n, 0);
vector<int> colSum(m, 0);
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
rowSum[i] += grid[i][j];
colSum[j] += grid[i][j];
}
}
int inc = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int cur = rowSum[i] + colSum[j] - grid[i][j];
inc = max(inc, cur);
}
}
int totalSum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
totalSum += grid[i][j];
}
}
cout << inc + totalSum << endl;
return 0;
}
时空复杂度
- 时间复杂度:
O(MN)
。求和需要遍历整个二维矩阵 - 空间复杂度:
O(N+M)
。row_sum
和col_sum
所占空间分别为O(N)
和O(M)
总结
- 记得acm模式不是return而是要打印出结果。
- 快慢指针中,快指针遍历数组元素,慢指针用于隔断(初始化索引为-1)或指示即将赋值的元素(即指示下一个元素要挪到这个位置,通常初始化索引为0)
- C++中,如果两个数都是整型,相除后会返回整数,舍弃小数部分,Python中,
/
默认返回浮点数(如6/2 == 3.0),进行整除应该使用//
。 - C++中,对于数据较大的一些题目,有时候变量需要考虑初始化为
long
或者long long
类型。 - Python中,
nums[a:b]
的切片为左闭右开区间,即[a,b)
。 - 对于数组中的索引相减:
i-j
,其大小可代表的元素数量如下图所示,其中下标的大小i
表示该元素前面有i
个元素。
- 对于题目给定的
第几个
之类的字眼,要注意通常说的第i
个元素所在的位置其下标为i-1
,需要在原来基础上减一。