【原题】
Given an array of integers, find two numbers such that they add up to a specific target number.The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.You may assume that each input would have exactly one solution.
Input:numbers={2, 7, 11, 15}, target=9
Output:index1=1, index2=2
【翻译】
给定一个整数数组,找出其中两个数满足相加等于你指定的目标数字。要求:这个函数twoSum必须要返回能够相加等于目标数字的两个数的索引,且index1必须要小于index2。请注意一点,你返回的结果(包括index1和index2)都不是基于0开始的。你可以假设每一个输入肯定只有一个结果。例:
输入:numbers={2, 7, 11, 15}, target = 9
输出:index1 = 1, index2 = 2(不是基于0开始的)
【预备知识】
作为一名刷题新手,一见到自动生成的vector就懵了,所以做题之前我还是只能先去查阅了关于vector的资料。
一、vector(向量)
平时写c++程序时极少使用到vector,它是C++中的一种数据结构,与string相同,同属于STL(Standard Template Library,标准模板库)中的一种自定义的数据类型,相当于一个动态的数组,当我们无法知道自己需要的数组规模多大时,用它来解决问题可以达到最大节约空间的目的。
二、基本操作
a.clear() 移除容器中所有数据。
a.empty() 判断容器是否为空。
a.erase(pos) 删除pos位置的数据
a.erase(beg,end) 删除[beg,end)区间的数据
a.front() 传回第一个数据。
a.insert(pos,elem) 在pos位置插入一个elem拷贝
a.pop_back() 删除最后一个数据。
a.push_back(elem) 在尾部加入一个数据。
a.size() 回容器中实际数据的个数。
a.begin() 返回指向容器第一个元素的迭代器(即数组头的指针)
a.end() 返回指向容器最后一个元素的迭代器(即数组最后一个单元+1的指针)
vector<int> b(a.begin(), a.begin()+3) //将a[0]到a[3](共3个)作为向量b的初始值
(a.begin(),a.begin()+3)表示向量起始元素位置到起始元素+3之间的元素位置。
三、迭代器
在元素的输出上,还可以使用遍历器(又称迭代器)进行输出控制。向量元素的位置便成为遍历器,同时,向量元素的位置也是一种数据类型,在向量中遍历器的类型为: vector<int>::iterator。遍历器不但表示元素位置,还可以再容器中前后移动。
//元素全部输出
for(int i=0; i<a.size(); i++)
cout<<a[i]<<" " ;
可以改写为:
vector<int>::iterator t ;
for(t=a.begin(); t!=a.end(); t++)
cout<<*t<<" " ;
(*t为指针的间接访问形式,意思是访问t所指向的元素值。)
四、特别注意:
(1)
vector <int > a;
int b = 5;
a.push_back(b);
此时若对b另外赋值时不会影响a[0]。往a中压入的是b的值,即a[0]=b,此时a[0]和b是存储在两个不同的地址中的,因此改变b的值不会影响a[0]。
(2)
vector <int*> a;
int *b;
b = new int[4];
b[0]=0;
b[1]=1;
b[2]=2;
a.push_back(b);
delete b; //释放b的地址空间
for(int i=0; i <3; i++)
{
cout<<a[0][i]<<endl;
}
此时输出的值并不是一开始b数组初始化的值,而是一些无法预计的值。因为是这里把一个地址(指针)压入向量a,因此释放了b的地址也就等于释放了a[0]的地址,因此a[0]数组中存放的数值也就不得而知了。
【解题思路】
单纯的想解出这道题当然直接使用两层循环就够了,时间复杂度为O(n^2),空间复杂度为O(1),我的代码如下:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> vec;
for(int i=0;i<nums.size();i++){
for(int j=i+1;j<nums.size();j++){ //防止同一个数自己相加,所以j要从i+1开始
if(nums[i]+nums[j]==target){
vec.push_back(i+1);
vec.push_back(j+1);
}
}
}
return vec;
}
};
【其他方法】
显然我的代码效率非常的低下,应该学会更好的解决此类问题:
(1)建立Hash表
使用map<key, value>结构来存储给定数组nums对应的<numValeu, numIndex>。
1)i=0,nums指向数值2,map为空肯定找不到符合条件的值,则将<2,0>加入到map中,即m[2]=0;
2)i=1,nums的指针移到了第二个数值7,找2(=9-7),这时map中正好有key值为2的元组,则将map中找到的这个key值对应的value,再加上1(题中说了不从0开始)存入新的vector,再将nums指针指向的这个数的索引加1也存入vector;因为题中说只有一组结果,那么就可以break了。这时map中只有一个元素<2,0>。
时间复杂度:O(n),空间复杂度:O(n),达到以空间换时间的目的。
class Solution{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> v;
map<int, int> m;
for(int i = 0; i < nums.size(); i++) //可不可以写++i?
{
if(m.find(target - nums[i]) != m.end()) //若找到目标数值,则将对应索引压入新的vector
{
v.push_back(m[target - nums[i]] + 1);
v.push_back(i + 1);
break; //跳出循环
}
m[nums[i]] = i; //没有找到目标数值,把当前nums值压入map
}
return v;
}
};
(2)先排序,后查找
1)首先同样分配一个数组空间,将数据备份,以便获得索引值;
2)将拷贝的数组排序;
3)对排序后的数组,建立两个查找“指针”,一个在数组头,一个在数组尾部,依次相加比较,如果大于target,尾部向前移一位,如果小于target,头部向后移一位,直至有相等的情况或者头部与尾部交叉的情况发生;
4)最后再扫描一遍原数组,获取这两个数字的索引。
时间复杂度:O(nlogn)(取决于排序时间复杂度),空间复杂度:O(n)(取决于排序空间复杂度以及备份数组的空间复杂度)。
class Solution{
public:
vector<int> twoSum(vector<int>& nums, int target)
{
vector<int> v(nums);
sort(v.begin(),v.end());
int l = 0, r = v.size() - 1;
while(l < r)
{
if(v[l] + v[r] == target)
break;
else if(v[l] + v[r] > target)
--r;
else
++l;
}
//遍历原数组找对应的索引值
vector<int> index;
for(int i = 0, n = 2; i < nums.size(); i++)
if(v[l] == nums[i] || v[r] == nums[i])
{
index.push_back(i + 1);
if(--n == 0)
break;
}
return index;
}
};
【运行结果】
LeetCode上这道题的Expected answer应该错了,应该为[2,3]