要求复杂度为O(n)
的算法, 通过两次遍历实现: 第一次遍历将所有元素插入到哈希表中, 第二次遍历过程中对每个元素n
进行处理, 如果哈希表中存在值为n-1
的元素, 则说明该元素已经被处理或即将被处理; 若不存在, 就继续查看值为n+1
, n+2
, n+3
…的元素是否存在哈希表中, 这样就可以求出数组中所有连续序列的长度了。
两个指针从数组的两端开始进行处理, 每次移动指向线高度较小的指针, 因为容器的盛水高度取决于较低的边, 所以选择的这条边无法再和另一边的任何一条线组成盛水更多的容器了。两个指针遍历完数组就可以得到最优的结果了。
接下来思考一下如果两个指针指向线的高度相等的情况下该怎么移动, 我们先猜测随便移动其中一个指针都可以, 为了证明这句话, 我们先讨论它的对立面是否成立, 即不能随便移动其中一个指针; 也即存在一种答案, 必须要且只能使用到这两边中某一条边; 可能会出现这种情况的是, 在这两个指针的范围内, 存在一条更高且不位于这两个指针正中央的边, 但是很明显的是, 及时我们使用了这条更高的边, 围成的容器的容积也不会大于这两条高度相等的边围成的容器容积, 所以上面提出的反面假设是错误的。
暴力枚举前两个数字, 最后一个数字如果也适用枚举的话时间复杂度就成了 O ( N 3 ) O(N^3) O(N3), 这样的算法最多可以处理数据规模为300的问题。所以最后一个数字使用双指针来进行优化: 所以问题的答案就是, 先对数组进行排序, 然后枚举前两个数字, 第三个指针从大到小进行遍历数字, 第三个指针和遍历第二个数字的指针就形成了双指针, 时间复杂度为 O ( N ) O(N) O(N), 再加上外层循环, 总的时间复杂度就变成了 O ( N 2 ) O(N^2) O(N2)。
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
for (int i = 0; i < nums.size(); ++i) {
if (i && nums[i] == nums[i-1]) continue; // 去重
if(nums[i] > 0) break;
int l = i+1, r = nums.size()-1;
int target;
while(l < r) {
if (l > i + 1) {
while(l < r && nums[l] == nums[l-1]) ++l; // 去重
if (l == r) break;
}
target = -1 * (nums[i] + nums[l]);
if(target < nums[r]) {
--r;
} else if (target == nums[r]) {
res.push_back({nums[i], nums[l], nums[r]});
++l;
} else {
++l;
}
}
}
return res;
}
};
第 n n n个柱子正上方的存水单位数为 min ( max ( h e i g h t 1 , h e i g h t 2 , . . . h e i g h t n − 1 ) , max ( h e i g h t n + 1 , h e i g h t n + 2 , . . . . . ) ) − h e i g h t n \min(\max(height_1, height_2, ...height_{n-1}), \max(height_{n+1}, height_{n+2}, .....))-height_n min(max(height1,height2,...heightn−1),max(heightn+1,heightn+2,.....))−heightn
预处理出两个数组就可以了, 可以用双指针来优化空间。
枚举所有不重复字符串的开始位置, 由于字符串的开始和结束位置都是单调递增的, 所以使用滑动窗口来解决问题。
枚举所有的字符串的开始位置, 这题比上一题简单, 字符串的长度是固定的, 窗口大小也是固定的, 扫完整个字符串即可。