数组的遍历
文章内容是自己刷leetcode官方刷题攻略的一些经验与总结。
题目链接详见 leetcode刷题攻略
如果喜欢的话,别忘了点个赞哦 ^ _ ^
一.485.最大连续1的个数
1.题目描述
2.题目分析与解答
因为数组中只有0和1两个元素,所以我们可以维护一个count用来记录当前连续1的个数,用maxCount记录最大连续1的个数。
当遇到1,把count++,当遇到0,更新maxCount,重置count。
考虑到结尾数字可能是1,会导致maxCount没有更新,所以遍历完数组后再次更新maxCount。
C++代码如下:
class Solution {
public:
int findMaxConsecutiveOnes(vector<int>& nums) {
int cnt = 0;
int maxCnt = 0;
for(int i = 0; i < nums.size(); i ++) {
if(nums[i] == 1) {
cnt ++;
} else {
maxCnt = max(maxCnt, cnt);
cnt = 0;
}
}
//最后一个元素可能是1
maxCnt = max(maxCnt, cnt);
return maxCnt;
}
};
二.495.提莫攻击
1.题目描述
2.题目分析与解答
这个题目的关键点是每次攻击会重置中毒时间。
我们最终要记录的是总中毒时间,那么在遍历数组过程中,有这么两种情况:一是两次攻击间隔小于单次中毒时间,那么中毒时间会刷新,我们需要将答案加上攻击间隔的时间;二是两次攻击间隔大于单次中毒时间,那么需要将答案加上单次中毒时间。
所以最终答案更新方法就是每次加上 min(攻击时间间隔,单次中毒时间)。
考虑到最后一次攻击没有处理,最终的答案应该再加上一个单次中毒时间。
C++代码如下:
class Solution {
public:
int findPoisonedDuration(vector<int>& timeSeries, int duration) {
int ans = 0;
for(int i = 1; i < timeSeries.size(); i ++) {
int x = timeSeries[i] - timeSeries[i - 1];
ans += min(x, duration);
}
return ans + duration;
}
};
三.414.第三大的数
1.题目描述
2.题目分析与解答
这个题目如果不考虑时间复杂度的话,可以直接 sort 排序后找第三大的数,时间复杂度是 O(nlogn)。
但是你可以用 O(n) 的时间复杂度解决这个问题吗?
如果要求时间复杂度是 O(n) 就不能使用排序算法了,可以这么思考:
我们可以在遍历数组过程中保留前三个大的元素,那么怎么保留呢?可以使用有序集合这个数据结构。
我们在遍历数组过程中,将每个元素插入到集合中,如果集合的元素个数大于三,说明我们保留的元素过多,需要删除当前集合中最小的元素,因为是有序集合,所以把集合头部的元素(就是四个元素中的最小元素)删掉就可以。
因为原始数组的元素个数可能小于三个,我们最后输出的时候需要判断一下:如果元素没有三个,输出结果为最大元素(集合的末尾),否则输出结果为第三大元素(集合的头部)。
C++代码如下:
class Solution {
public:
int thirdMax(vector<int>& nums) {
//使用有序集合
set<int> s;
for(int i = 0; i < nums.size(); i ++) {
s.insert(nums[i]);
if(s.size() > 3) {
s.erase(s.begin()); //擦去小的
}
}
return s.size() == 3 ? *s.begin() : *s.rbegin();
// c.begin() 返回一个迭代器,它指向容器c的第一个元素
// c.end() 返回一个迭代器,它指向容器c的最后一个元素的下一个位置
// c.rbegin() 返回一个逆序迭代器,它指向容器c的最后一个元素
// c.rend() 返回一个逆序迭代器,它指向容器c的第一个元素前面的位置
}
};
四.628三个数的最大乘积
1.题目描述
2.题目分析与解答
如果数组中只有正数或者只有负数,那么这道题就没有什么难度了,但是现在数组中正负数都可能有,那么就需要分情况讨论。
我们可以先将数组排个序,方便讨论,有以下几种情况:
- 如果数组中只有正数,那么我们选取最大的三个正数就可以;
- 如果数组中只有负数,那最终结果还是负数,我们需要使这个负数最大,也就是使其绝对值最小,那么我们选取最大的三个负数就可以;
- 如果数组中有正数也有负数,那么结果可能是三个最大正数的乘积,也可能是两个最小负数(绝对值最大)与一个最大正数的乘积,也有可能是一个负数与两个正数的乘积(这种情况下数组中只会有三个数)。
那么综合上面的几种情况,其实只涉及了数组中的这么两个组合:一个组合是数组中的最大的三个元素的乘积,一个组合是数组中最大的一个元素与最小的两个元素的乘积。
C++代码如下:
class Solution {
public:
int maximumProduct(vector<int>& nums) {
//排序
//如果全为正数:结果是最大的三个正数(最后三个数)
//如果全为负数:结果是最大的三个负数(最后三个数)
//如果有正数有负数
// 如果结果只有正数(最后三个数)
// 如果结果为一个正数+两个负数(最大一个数和最小两个数)
// 如果结果为两个正数+一个负数(最后三个数)
//其实总的就是找最大的三个数和最小的两个数,不用排序也可以
sort(nums.begin(), nums.end());
int n = nums.size();
return max(nums[n - 1] * nums[n - 2] * nums[n - 3], nums[n - 1] * nums[0] * nums[1]);
}
};
下面是不使用排序,时间复杂度为 O(n) 的方法。
我在上文代码的注释中也提到了,我们只用找数组中最大的三个数和最小的两个数就可以,用 max1,max2,max3,min1,min2 来记录这些元素即可,在遍历数组过程中更新。
C++代码如下:
class Solution {
public:
int maximumProduct(vector<int>& nums) {
//排序
//如果全为正数:结果是最大的三个正数(最后三个数)
//如果全为负数:结果是最大的三个负数(最后三个数)
//如果有正数有负数
// 如果结果只有正数(最后三个数)
// 如果结果为一个正数+两个负数(最大一个数和最小两个数)
// 如果结果为两个正数+一个负数(最后三个数)
//其实总的就是找最大的三个数和最小的两个数,不用排序也可以
//max1表示最大的数,min1表示最小的数
int max1 = INT_MIN, max2 = INT_MIN, max3 = INT_MIN;
int min1 = INT_MAX, min2 = INT_MAX;
for(int x : nums) {
if(x < min1) {
min2 = min1;
min1 = x;
} else if(x < min2) {
min2 = x;
}
if(x > max1) {
max3 = max2;
max2 = max1;
max1 = x;
} else if(x > max2) {
max3 = max2;
max2 = x;
} else if(x > max3) {
max3 = x;
}
}
return max(max1 * max2 * max3, min1 * min2 * max1);
}
};