1. 两数之和
分析:
因为需要排序,然后还需要原来的下标,所以只能构造一个vector<pair<int, int>>数组携带者原来的下标信息一起排序。最后才可以得到原来的下标信息。
代码:
双指针思路:使用范式解题,排序加双指针相向而行
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
vector<int> ans;
vector< pair<int, int>> numsi;
for(int i = 0; i< nums.size(); i++){
numsi.emplace_back(nums[i], i);
}
sort(numsi.begin(), numsi.end());
int left = 0, right = n-1;
int lastL = INT_MAX, lastR=INT_MAX;
int sum;
while(left < right){
if(numsi[left].first == lastL){
left++; continue;
}
if(numsi[right].first == lastR){
right--; continue;
}
sum = numsi[left].first + numsi[right].first;
if( sum == target){
ans.emplace_back(numsi[left].second);
ans.emplace_back(numsi[right].second);
lastL = left; lastR = right;
left++; right--;
}
else if(sum < target){
left++;
}
else{
right--;
}
}
return ans;
}
};
直接查找思路:使用find函数和distance函数直接找出对应的一对
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int n = nums.size();
int dis;
for(int i = 0; i< n; i++){
auto it = find(nums.begin(), nums.end(), target - nums[i]);
if( it != nums.end() && (dis = distance(nums.begin(), it)) != i){
return {i, dis};
}
}
return {};
}
};
2. 三数之和
分析:固定一个指针,动两个指针,转化为两数之和问题
注意点:需要三个指针都需要跳过相同的值,其中两个自由指针在每次固定指针移动的时候,上一次的值(LastL/LastR)都需要更新,也就是说,lastL/lastR和lasti的初始化(或声明)位置不同。
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> ans;
sort(nums.begin(), nums.end());
int n = nums.size();
int lasti = INT_MAX;
int lastL, lastR;
int left, right, sum;
for(int i =0; i< n-2; i++){
lastL= INT_MAX, lastR= INT_MAX;
left = i+1, right = n-1;
if(nums[i] == lasti){
continue;
}
lasti = nums[i];
while(left < right){
//跳过相同数的业务逻辑
if(nums[left] == lastL){
left++;
continue;
}
if(nums[right] == lastR){
right--;
continue;
}
//判断是否满足要求
sum =nums[left] + nums[right];
if(sum + nums[i] == 0){
lastL = nums[left];
lastR = nums[right];
ans.push_back({nums[left],nums[i], nums[right]});
left++; right--;
}
else if(sum + nums[i] > 0){
right--;
}
else{
left++;
}
}
}
return ans;
}
};
总结:
i)C++的 变量 没有初始化会自动赋初值,int为0
ii)最好不要在数组下标里面加减
3. 四数之和
分析:先写一个三数之和,然后写一个for函数调用三数之和
注意:i)for循环里面也要跳过重复数字
ii)三数之和模板里面, left是++, right是--,不要搞错了,debug起来很麻烦。
代码:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums,int start, int target){
vector<vector<int>> ans;
// [-2, -1, 0, 0, 1, 2 ]
int lastL, lastR, lasti = INT_MAX;
int left, right;
signed long sum;
int n = nums.size();
for(int i = start; i<n-2; i++){
lastL = INT_MAX; lastR = INT_MIN;
if(nums[i] == lasti){
continue;
}
lasti = nums[i];
left = i+1; right = n-1;
while(left < right){
if(nums[left] == lastL){
left++;
continue;
}
if(nums[right] == lastR){
right--;
continue;
}
sum = nums[left] + nums[right];
if(sum + nums[i] == target){
ans.push_back({nums[i], nums[left], nums[right]});
lastL = nums[left]; lastR = nums[right];
left++; right--;
}
else if(sum + nums[i] < target){
left++;
}
else{
right--;
}
}
}
return ans;
}
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size();
int lasti = INT_MAX;
vector<vector<int>> ans, threeSumAns;
for(int i = 0; i < n-3; i++){
if(nums[i] == lasti){
continue;
}
lasti = nums[i];
threeSumAns = threeSum(nums, i+1, target-nums[i]);
for(auto v : threeSumAns){
v.insert(v.begin(), nums[i]);
ans.push_back(v);
}
}
return ans;
}
};
总结:
i)C++插入元素到开头,v.insert( v.begin(), nums[i])
ii) C++中,用另一个函数得begin()作为数组运算的指针也会出现栈溢出这种数组越界的错误。
iii)数组传引用的作用是加速!!
总结:
技巧:
1. 如何在排序后保持原数组的下标信息?
答:创建一个vector<pair<int, int>> 向量,其中包含下标信息,排序的时候会跟着排序,并且排序默认使用pair的第一个变量排序
2. 如何通过迭代器得到下标?
答: 使用 distance(nums.begin(), iterator)
3. 如何找到某个数字在数组中第一次出现的位置?
答: 使用 find( nums.begin(), nums.end(), integer ),得到的是迭代器,如果没有找到返回 nums.end()
4. emplace_back是比push_back更高效的方式,不需要先构造一个对象,然后再将其添加到容器中。