1.两数之和:
思想:我们用map来存储已经遍历过的数据,也就是遍历的同时利用map存储遍历过的元素,同时查看map里有没有要找的另外一个数字。注意:map里我们把
1.map底层结构是红黑树,所以容器中不会出现相同的元素,因此count()的结果只能为0和1,可以以此来判断键值元素是否存在(当然也可以使用find()方法判断键值是否存在)。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> p;
vector<int> an(2,-1); //返回答案,默认(-1,-1)
for(int i = 0; i < nums.size();i++){
if(p.count(target - nums[i])>0)
{
an[0] = i;
an[1] = p[target - nums[i]];
break;
}
p[nums[i]] = i;
}
return an;
}
};
1.1相关知识点:
map的使用以及优化,map的底层是有序的红黑树,因此在每次插入元素的时候都要进行旋转排序等操作用占用时间,因此可以使用unordered_map进行优化。
2.三数之和
思想:目标 在一个数组中找出满足a + b + c = 0的三个数,不输出重复答案。首先由于我们输出的是具体数字而不是下标,所以可以进行排序。
sort(nums.begin(),nums.end());//排序
在排序之后我们可以先想一下特判,什么样的数组输出为空?
第一种:有序数组中第一个元素就大于0,返回空
第二种:有序数组中最后一个元素任然小于0,返回空
第三种:数组元素全部为0,直接返回一个答案
第四种:数组元素数量小于3,返回空
if(nums.size() < 3 || nums[0] > 0 || nums[n-1] < 0) return ans; //剪枝
if(!nums[0] && !nums[n-1]) return {
{
0, 0, 0}}; // 如果数组全为0
剩下的就是需要我们来遍历的数组了,怎样遍历三个数字使他们满足a + b + c = 0呢,在三层for循环中,我们是固定,第一个数组a,再固定第二个数b,在数组中遍历满足条件的c. 然后重新固定新的数b,再找c. 重新固定a等等。
思考:我们是否可以有条件的遍历,我们固定数字a,b在有序数组中从前往后遍历,c在有序数组中从后往前遍历。当a + b + c 不满足条件的时候,有a + b + c > 0 或a + b + c < 0两种情况。
则a + b + c > 0 是一定是c太大造成的,移动c往前遍历。
则a + b + c < 0 是一定是b太小造成的,移动b往后遍历。
for( i = 0 ; i < n - 2; ++i){
if( i && nums[i]==nums[i-1]) continue; //消除重复的三元组
j = i + 1;
k = n - 1;
while( j < k){
int target = nums[i] + nums[j] + nums[k]; //避免下面加法运算两次
if(target > 0) --k; //因为nums已经有序,当target > 0; 说明k太大
else if (target < 0) ++j; //因为nums已经有序,当target < 0; 说明j太大
else{
ans.push_back({
nums[i], nums[j], nums[k]}); //target ==0 输出答案
++j; // i 不变,j增加,k减小。不能同时固定两个数字,一定不满足要求(有序)
--k;
while(j < k &&nums[j] == nums[j-1]) ++j; // 有序数组,第二个数字不能相同
while(j < k && nums[k] == nums[k+1]) --k; //同理
}
}
}
把代码综合起来:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector <int>> ans;
int n = nums.size();
ans.reserve( n > 256 ? 256 : n); //预先分配空间,以减少 push_back内存不足的时候重新申请的时间
int i, j, k ;
sort(nums.begin(),nums.end());//排序
if(nums.size() < 3 || nums[0] > 0 || nums[n-1] < 0) return ans; //剪枝
if(!nums[0] && !nums[n-1]) return {
{
0, 0, 0}}; // 如果数组全为0
for( i = 0 ; i < n - 2; ++i){
if( i && nums[i]==nums[i-1]) continue; //消除重复的三元组
j = i + 1;
k = n - 1;
while( j < k){
int target = nums[i] + nums[j] + nums[k]; //避免下面加法运算两次
if(target > 0) --k; //因为nums已经有序,当target > 0; 说明k太大
else if (target < 0) ++j; //因为nums已经有序,当target < 0; 说明j太大
else{
ans.push_back({
nums[i], nums[j], nums[k]}); //target ==0 输出答案
++j; // i 不变,j增加,k减小。不能同时固定两个数字,一定不满足要求(有序)
--k;
while(j < k &&nums[j] == nums[j-1]) ++j; // 有序数组,第二个数字不能相同
while(j < k && nums[k] == nums[k+1]) --k; //同理
}
}
}
return ans;
}
};
2.1相关技巧
ans.reserve( n > 256 ? 256 : n); //预先分配空间,以减少 push_back内存不足的时候重新申请的时间
int i, j, k ;//不要加到循环中,不然每次都要申请会占用运行时间