算法笔试准备


双指针, 一样的题,就是把数字换成字符。
思路就是遍历数组a中的每一个元素a[i], 对于每一个i,找到j使得双指针[j, i]维护的是以a[i]结尾的最长连续不重复子序列,长度为i - j + 1, 将这一长度与res的较大者更新给res。
而对于每一个i,因为[j, i - 1]是前一步得到的最长连续不重复子序列,所以如果[j, i]中有重复元素,一定是a[i]和[j, i - 1]之间的一个数重复了,因此就右移j直到a[i]不重复为止。
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n; int a[N], cnt[N]; int main() { cin >> n; for (int i = 0; i < n; i ++ ) cin >> a[i]; int res = 0, j = 0; for (int i = 0; i < n; i ++ ) { cnt[a[i]] ++ ; while (cnt[a[i]] > 1 && j < i) { cnt[a[j]] -- ; j ++ ; } res = max(res, i - j + 1); } cout << res << endl; return 0; }

找规律:接满雨水之后,数组在最大值左边不严格递增,右边不严格递减。
比如样例接满雨水之前是:0 1 0 2 1 0 1 3 2 1 2 1
那么接满雨水之后就是: 0 1 1 2 2 2 2 3 2 2 2 1
因此只需要把原数组变成符合这样要求的数组就行了,改变的量就是接的雨水。
具体实现只需要先找到整体的最大值下标,左右各自遍历一遍
两边都维护一个值来表示之前的最大值以保证单调性,
如果比最大值小,说明有坑可以积雨,雨水量就加上这个差值,
如果大于等于,就说明没坑没法积雨,就更新最大值。
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n; int a[N]; int main() { cin >> n; int mx = 0, idx = 0; for (int i = 1; i <= n; i ++ ) { cin >> a[i]; if (mx < a[i]) { mx = a[i]; idx = i; } } int res = 0; int cur = a[1]; for (int l = 1; l <= idx; l ++ ) { if (a[l] > cur) cur = a[l]; else res += cur - a[l]; } cur = a[n]; for (int r = n; r >= idx; r -- ) { if (a[r] > cur) cur = a[r]; else res += cur - a[r]; } cout << res << endl; return 0; }

双指针+贪心,
题目中的示例为:
[1, 8, 6, 2, 5, 4, 8, 3, 7]
在初始时,左右指针分别指向数组的左右两端1和7,它们可以容纳的水量为 min(height[1],height[7])∗8=8。
此时我们需要移动一个指针。移动哪一个呢?直觉告诉我们,应该移动对应数字较小的那个指针(即此时的左指针)。这是因为,由于容纳的水量是由两个指针指向的数字中较小值∗指针之间的距离
决定的。如果我们移动数字较大的那个指针,那么前者「两个指针指向的数字中较小值」不会增加,后者「指针之间的距离」会减小,那么这个乘积会减小。因此,我们移动数字较大的那个指针是不合理的。因此,我们移动 数字较小的那个指针。
所以,我们将左指针向右移动:
此时可以容纳的水量为 min(8,7)∗7=49。由于右指针对应的数字较小,我们移动右指针
此时可以容纳的水量为 min(8,3)∗6=18。由于右指针对应的数字较小,我们移动右指针
此时可以容纳的水量为 min(8,8)∗5=40。两指针对应的数字相同,我们可以任意移动一个,例如移动左指针
此时可以容纳的水量为 min(6,8)∗4=24。由于左指针对应的数字较小,我们移动左指针,并且可以发现,在这之后左指针对应的数字总是较小,因此我们会一直移动左指针,直到两个指针重合。在这期间,对应的可以容纳的水量为:min(2,8)∗3=6,min(5,8)∗2=10,min(4,8)∗1=4。
在我们移动指针的过程中,计算到的最多可以容纳的数量为 49,即为最终的答案。
#include <bits/stdc++.h> using namespace std; const int N = 100010; int n; int a[N]; int main() { cin >> n; for (int i = 1; i <= n; i ++ ) cin >> a[i]; int l = 1, r = n; int res = 0; while (l < r) { res = max(res, (r - l) * min(a[l], a[r])); if (a[l] < a[r]) l ++ ; else r -- ; } cout << res << endl; return 0; }


class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { vector<vector<int>>res; int len = nums.size(); sort(nums.begin(), nums.end()); for (int i = 0; i < len; i ++ ) { if (nums[i] > 0) break; if (i > 0 && nums[i] == nums[i - 1]) continue; int l = i + 1, r = len - 1; while (l < r) { if (nums[i] + nums[l] + nums[r] == 0) { res.push_back(vector<int>{nums[i], nums[l], nums[r]}); l ++ , r -- ; while(l < r && nums[l] == nums[l - 1]) l ++ ; while(l < r && nums[r] == nums[r + 1]) r -- ; } else if (nums[i] + nums[l] + nums[r] < 0) { l ++ ; while (l < r && nums[l] == nums[l - 1]) l ++ ; } else { r -- ; while (l < r && nums[r] == nums[r + 1]) r -- ; } } } return res; } };

滑动窗口,由于是从前往后加,所以当某次sum大于目标值的时候,从前面开始减就可以
class Solution { public: int minSubArrayLen(int target, vector<int>& nums) { int sum = 0, res = 0x3f3f3f3f; int j = 0; for (int i = 0; i < nums.size(); i ++ ) { sum += nums[i]; while(sum >= target) { res = min(res, i - j + 1); sum -= nums[j]; j ++ ; } } if (res == 0x3f3f3f3f) return 0; else return res; } };

单调队列,不想做了,直接滑动窗口打个暴力算了
class Solution { public: vector<int> maxSlidingWindow(vector<int>& nums, int k) { vector<int>res; for (int i = 0; i <= nums.size() - k; i ++ ) { int mx = -0x3f3f3f3f; for (int j = i; j <= i + k - 1; j ++ ) { mx = max(mx, nums[j]); } res.push_back(mx); } return res; } };

浙公网安备 33010602011771号