LeetCode01-两数之和
1.算法题目
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
2.算法思路
- 暴力枚举法
将数组的每个元素都一一比较,例如先将第一个元素和后面所有元素相加,判断能不能得到target值,如果不能就用第二个元素和之后的所有元素来相加比较,以此类推,直到找到target值,如果循环下来没找到就返回空。
其代码为:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int i,j;
for(i = 0;i<nums.size()-1;++i){
for(j = i+1;j<=nums.size()-1;++j){
if(nums[i] + nums[j] == target){
return {i,j};
}
}
}
return {};
}
};
时间复杂度:O(n^2)。
优点:简单容易想到。
缺点:运行速度慢且内存空间消耗较大。
- 两遍哈希表
使用哈希表的key、value思想,将数组数据存放在哈希表中,通过哈希表的算法来查找符合两数之和等于target值的数据,最后返回。
其代码为:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//建立哈希表
map<int,int> a;
vector<int> b(2,-1);//存放返回值
//将数据存入到哈希表中
for(int i = 0;i<nums.size();++i){
a.insert(map<int,int>::value_type(nums[i],i)); //前面是值,后面是值所在的位置
}
//查找
for(int i=0;i<nums.size();++i){
//使用count函数来查找是否出现过两数之和等于target的值,同时也避免查找到值是i本身
if(a.count(target-nums[i]) >0 && (a[target-nums[i]] != i)){
b[0] = i;
b[1] = a[target - nums[i]];
break;
}
}
return b;
}
};
其中,count(key)是查找key出现的次数,代码中是查找是否有另外一个数能等于target减去i值,并且一般value = map[key],文中是i = a[nums[i]]。
优点:大大减少了内存的消耗并且运行速度也得到了很大的提升。
- 哈希表改进
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
map<int,int> a;
vector<int> b(2,-1);
for(int i=0;i<nums.size();i++)
{
if(a.count(target-nums[i])>0)
{
b[0]=a[target-nums[i]];
b[1]=i;
break;
}
a[nums[i]]=i;
}
return b;
};
};
刚开始哈希表内是没有数据的,在循环函数内将每一个键值对都放入表中,直到找到判断函数被调用的时候。
- 双指针
直接贴代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> ids(nums.size());
iota(ids.begin(), ids.end(), 0);
sort(ids.begin(), ids.end(), [&](int& a, int& b){return nums[a] < nums[b];});
int left = 0, right = ids.size() - 1;
while (left < right) {
int res = nums[ids[left]] + nums[ids[right]];
if (res < target) {
left++;
} else if (res > target) {
right--;
} else {
break;
}
}
return vector<int>{ids[left], ids[right]};
}
};
其中,itoa代码是将ids填充为从0开始的递增序列,之后sort进行排序,以lambda表达式中的逻辑进行排序,也就是升序排序,之后进行逻辑判断,跟二分查找算法逻辑有点像。
LeetCode11-盛最多水的容器
1.算法题目
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
2.算法思路
- 双指针
首先要知道水量的算法逻辑,先定义一个left和一个right以及num来分别表示左指针、右指针、水量存放。水量的宽是确定了的,主要是看水量的高度,当left表示数组的第一个元素,right表示数组的最后一个元素,假设left位置的值大于right位置的值,如果移动left那么水量的面积只有两种可能,要么保持不变要么变小,不可能变大;反过来移动right就有多种可能了,可能变大可能变小可能不变。所以移动的时候只能移动小的那一边才可能得到最大值。
代码如下:
class Solution {
public:
int maxArea(vector<int>& height) {
int left = 0,right = height.size()-1,num = 0;
while(left<right){
num = height[left] > height[right] ?
max(num,(right-left)*height[right--]):
max(num,(right-left)*height[left++]);
}
return num;
}
};
在这还要注意一个问题,我刚开始也一直没发现,发现的时候感觉自己好蠢,max函数中必须要将height[right–]和height[left++]放在最后,因为如果你放在前面,你调用数组元素的时候已经使用了left或者right值了,那么它就会自动加一或者减一这会影响后面的right-left的值,从而得不到正确的水量值。
LeetCode15-三数之和
1.算法题目
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
2.算法思路
- 暴力枚举
直接三层循环每个都枚举一遍,这样也能得到三元组,但是会因为时间的关系无法通过测试。
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int i,j,k;
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(i = 0;i<nums.size();i++){
for(j = i+1;j<nums.size();j++){
for(k = j+1;k<nums.size();k++){
if(nums[i] + nums[j]+ nums[k] == 0)
ans.push_back({nums[i],nums[j],nums[k]});
while (k + 1 < nums.size() && nums[k] == nums[k + 1])k++;//重复的直接移动到最后
}
while (j + 1 < nums.size() && nums[j] == nums[j + 1])j++; //重复的直接移动到最后
}
while (i + 1 < nums.size() && nums[i] == nums[i + 1])i++; //重复的直接移动到最后
}
return ans;
}
};
- 双指针法
这个跟前面那个两数之和逻辑其实差不多,只不过在双指针循环外面加了一层for循环,而在while循环外面首先要进行对第一个值的去重之后才能进行while循环,建立两个值分别指向i+1和nums.size()-1,并赋值给left和right。之后进行判断语句,首先判断三数相加和等于0的,如果是就将三个值插入到最开始所建立的res数组中,而插入之后也要进行去重,因为还要进行查找,left和right都需要进行去重。最后对三数之和大于0和小于0都进行相应的操作,这时候不需要去重了,最外层返回res数组。
代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
//创建返回参数
vector<vector<int>> res;
//排序
sort(nums.begin(),nums.end());
for(int i = 0;i<nums.size();++i){
//去重
if(i > 0&&nums[i] == nums[i-1])
continue;
//双指针
int left = i+1,right = nums.size()-1;
while(left < right){
if(nums[i] + nums[left] +nums[right] == 0){
res.push_back({nums[i],nums[left],nums[right]});
++left;
//去重
while(left<right && nums[left-1] == nums[left])
++left;
--right;
while(left<right && nums[right] == nums[right+1])
--right;
}
else if(nums[i] + nums[left] +nums[right] > 0){
--right;
}
else{
++left;
}
}
}
return res;
}
};
代码中要注意去重之前是进行了自增的操作,要相应的改一下判断语句。