原题链接 1. 两数之和
解题思路
看到题面,我们可以想到可以使用双指针法解决这个问题。
在一个升序数组p[N]
中,设置指针i
和指针j
,i
从左向右遍历,j
从右向左遍历,
假如p[i] + p[j] > 目标值
的话,我们就让j
指针向左移动;
假如p[i] + p[j] < 目标值
的话,我们就让i
指针向右移动;
假如p[i] + p[j] = 目标值
我们的目的就达成啦,退出循环并返回i, j
(数组下标)就行啦。
但是,本题中所给的数组居然是乱序的,那我们就需要先给它排好序,但聪明的同学肯定想到了:排完序之后数组下标岂不是都乱了?题目需要返回它们原来的数组下标呀。
这个时候,我们就可以使用pair了,由于它本质上就是一个可以存两个相对应数据的结构体,其实也可以自己实现一个结构体解决(但我太懒了)。
我们可以用pair的第一个位置存数组元素,第二个位置存每个数组元素对应的下标,这样的话,我们在给数组元素们排完序后,它们原来的下标也会跟着自己对应的数组元素变动,之后我们就可以轻松地返回它们啦。
举个栗子
输入:nums = [3,2,7,15,11], target = 10
输出:[0,2]
p数组中保存的是这样的:
3 0————p[0]
2 1————p[1]
7 2————p[2]
15 3————p[3]
11 4————p[4]
排了序之后是这样的:
2 1————p[0]
3 0————p[1]
7 2————p[2]
11 4————p[3]
15 3————p[4]
双指针判断过程是这样的:
2 + 15 > 10 ————j指向11 4
2 + 11 > 10 ————j指向7 2
2 + 7 < 10 ————i指向3 0
3 + 7 = 10 ————成功了!退出循环
返回0和2
时间复杂度
可以看出,两个指针相向运动,最多在相互碰到后停下,也就是仅遍历了一遍数组,因此时间复杂度是O(n)
最后给一下力扣的通过详情,还是挺有成就感的嘿嘿
代码
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
pair<int, int> p[nums.size()];//最好再加个10啥的防止RE
for(int i = 0; i < nums.size(); i++) p[i]={nums[i], i};//给p数组的第一个和第二个位置赋值,第一个位置存数组元素,第二个位置存它们的下标
sort(p , p + nums.size());//给p数组的第一个位置(数组元素)排序
int i = 0, j = nums.size() - 1;//两个指针i和j,i从左向右遍历,j从右向左遍历
while(i < j)
{
while(p[i].first + p[j].first > target) j--;
while(p[i].first + p[j].first < target) i++;
if(p[i].first + p[j].first == target) break;
}
return {p[i].second, p[j].second};//返回我们找到的两个元素在排序之前的数组下标
}
};