这篇博客记录第3天刷题的解题思路与心得体会。
11. 盛最多水的容器
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
示例 3:
输入:height = [4,3,2,1,4]
输出:16
提示:
n = height.length
2 <= n <= 3 * 104
0 <= height[i] <= 3 * 104
分析:这道题可以用暴力算法依次对 0 ≤ i < h e i g h t . s i z e ( ) − 1 0 \le i <height.size()-1 0≤i<height.size()−1 和 1 ≤ j < h e i g h t . s i z e ( ) 1 \le j < height.size() 1≤j<height.size()遍历求 m i n ( h e i g h t [ i ] , h e i g h t [ j ] ) × ( j − i ) min(height[i],height[j]) \times (j-i) min(height[i],height[j])×(j−i) 的最大值,但算法复杂度为 O ( n 2 ) O(n^2) O(n2)超出时间限制。可以使用贪心算法求解。这里引用LeetCode评论区大佬的分析:
使用两个指针,值小的指针向内移动,这样就减小了搜索空间 因为面积取决于指针的距离与值小的值乘积,如果值大的值向内移动,距离一定减小,而求面积的另外一个乘数一定小于等于值小的值,因此面积一定减小,而我们要求最大的面积,因此值大的指针不动,而值小的指针向内移动遍历。
class Solution {
public:
int maxArea(vector<int>& height) {
if (height.size() <= 1)
return -1;
int i = 0, j = height.size()-1;
int Area = 0;
while(i < j) {
Area = max(Area, min(height[i], height[j]) * (j-i));
//贪心求解最大面积,较大值指针不动,较小值指针向内移动
if(height[i] < height[j])
i++;
else
j--;
}
return Area;
}
};
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 “”。
示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:""
解释:输入不存在公共前缀。
提示:
0 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成
分析:随便选择一个字符串作为标准,依次取它的前缀字符进行依次遍历,每个都与其他所有字符串的相同下标字符比较,看是否相同,直到遇到不全部相同的下标。这样取公共前缀子串的时间性能是 O ( n ∗ m ) O(n*m) O(n∗m),参考大佬的代码如下:
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
if(strs.empty()) return "";
if (strs.size() == 1) return strs[0];
string prefix = "";
int idx = 0;
bool go_on = true;
while(true) {
for (int i = 1; i < strs.size(); i++) {
if (!(idx < strs[0].size() && idx < strs[i].size() && strs[0][idx] == strs[i][idx])) {
go_on = false;
break;
}
}
if(go_on)
prefix.push_back(strs[0][idx++]);
else
break;
}
return prefix;
}
};
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:
输入:nums = []
输出:[]
示例 3:
输入:nums = [0]
输出:[]
提示:
0 <= nums.length <= 3000
-105 <= nums[i] <= 105
分析:这题如果用三重循环暴力搜索很容易超时,后二重循环可以转换为双指针问题,且为了防止找到重复的三元组,先对原数组进行排序,这里直接摘抄评论区大佬的分析如下:
如果用暴力解,用一个三重循环遍历那么时间复杂度在O( N 3 N^3 N3),然后稍微进行优化,根据题目:找到三元组不能重复。可以想到,如果要排序(能保证重复出现的数字在一起,并且时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN),没啥影响。还能在第二重循环的枚举中找到不小于当前第一重循环的枚举元素,和第三重循环同理,找到不小于第二重循环的枚举元素。
那么能想到了排序,但是本质上还是三重循环,那么时间复杂度还是O( N 3 N^3 N3),继续优化,将下面的两重循环变成一重循环:
可以发现我们是固定了第一个数然后去找其他两个数的,那么可以将后面两个数看成一个数,那么问题就变成了在有序数组中从[i+1, len-1]这个范围内找到一个符合要求的数,那么就变成了双指针问题,而这个数的值不再是mid,而是两个边界left和right的和。而指针的移动条件就是:如果当前的sum值太大,那么右指针就移动;如果sum太小,那么左指针就移动;如果值正好,那么就是当前值,并且左指针右移,右指针左移(因为是找到所有满足的解);循环的结束条件就是左右指针相遇 而双指针情况下,第二三重循环就从O( N 2 N^2 N2)变成 O ( N ) O(N) O(N).
C++代码如下:
#include <iostream>
#include <vector>
#include <stdlib.h>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res; //二重vectorv数组
if (nums.size() < 3)
return res;
//从三重循环暴力搜索到二重循环双指针求解
sort(nums.begin(), nums.end()); //由小到大排序,保证重复数字在一起且方便搜索
for (int i = 0; i < nums.size() - 2; i++) {
if(nums[i] > 0) //一重循环元素(<0)<二重<三重
return res;
if(i > 0 && nums[i] == nums[i-1]) //一重循环跳过重复数字
continue;
int left = i + 1, right = nums.size() - 1; //[i+1, len-1]上的左右指针
while(left < right) {
int sum = nums[i] + nums[left] + nums[right];
if (sum < 0) //当前sum值太小,左指针移动
left++;
else if (sum > 0) //当前sum值太大,右指针移动
right--;
else { //sum=0,左指针和右指针都移动继续搜索
res.push_back({nums[i], nums[left], nums[right]});
while (left < right && nums[left] == nums[left + 1]) left++; //跳过重复数字
while (left < right && nums[left] == nums[right - 1]) right--;
left++;
right--;
}
}
}
return res;
}
};
int main(){
Solution S;
int a[] = {-1, 0 , 1, 2, -1, -4};
vector<int> array(begin(a), end(a));
vector<vector<int>> threeArr = S.threeSum(array);
for (int i = 0; i < threeArr.size(); i++) {
for (int j = 0; j < threeArr.at(i).size(); j++)
cout << (threeArr.at(i)).at(j)<<" ";
cout<<endl;
}
system("pause");
return 0;
}