1. Two Sum
Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
You can return the answer in any order.
Example 1:
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Output: Because nums[0] + nums[1] == 9, we return [0, 1].
Example 2:
Input: nums = [3,2,4], target = 6
Output: [1,2]
Example 3:
Input: nums = [3,3], target = 6
Output: [0,1]
Constraints:
- 2 <= nums.length <= (10^4)
- (-10^9) <= nums[i] <= (10^9)
- (-10^9) <= target <= (10^9)
- Only one valid answer exists.
solution
朴素解——枚举
由于我们每次要从数组中找两个数。
因此一个很简单的思路是:使用两重循环枚举下标 i 和 j ,分别代表要找的两个数。
然后判断 (nums[i] + nums[j] == target) 是否成立。
另外为了防止得到重复的解,我们需要在第一层循环中让 i 从 0 开始,到 n - 2 结束(确保能取到下一位数作为 j );在第二层循环中让 j 从 i + 1 开始,到 n - 1 结束。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int t) {
int l = nums.size();
for(int i = 0;i<l-1;i++){
for(int j = i+1;j < l;j++){
if(nums[i]+nums[j]==t) {
return {i,j};
}
}
}
return {};
}
};
* 时间复杂度:两重循环复杂度是 (O(n^2))
* 空间复杂度:(O(1))
哈希表解法
首先,任何优化的思路都不外乎「减少重复」。
从朴素解法中可以知道,逻辑上我们是先定下来一个数,然后从数组中往后找另外一个值是否存在。但其实我们在找第二个数的过程中是重复扫描了数组多次。
举个例子,对于 nums = [2,3,8,4], target = 9
的样例,我们先确定下来第一个数是 2
,然后从后扫描到最后一个数,检查是否有 7
。发现没有,再决策第一个数为 3
的情况,这时候我们应该利用前一次扫描的结果来帮助我们快速判断是否存在 6
,而不是再重新进行一次扫描。
这是直观的优化思路,不难想到可以使用哈希表进行存储。以数值为 key,数值的下标为 value。对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int t) {
unordered_map<int, int> hashmap;
for(int i = 0;i<nums.size();++i){
auto it = hashmap.find(t - nums[i]);
if(it != hashmap.end()){
return {it->second,i};
}
hashmap[nums[i]]=i;
}
return {};
}
};
-
时间复杂度:(O(N)),其中 (N) 是数组中的元素数量。对于每一个元素 x,我们可以 (O(1)) 地寻找 target - x。
-
空间复杂度:(O(N)),其中 (N)是数组中的元素数量。主要为哈希表的开销。
knowledge
auto的原理就是根据后面的值,来自己推测前面的类型是什么。
auto的作用就是为了简化变量初始化,如果这个变量有一个很长很长的初始化类型,就可以用auto代替。
注意点:
-
用auto声明的变量必须初始化(auto是根据后面的值来推测这个变量的类型,如果后面没有值,自然会报错)
-
函数和模板参数不能被声明为auto(原因同上)
-
因为auto是一个占位符,并不是一个他自己的类型,因此不能用于类型转换或其他一些操作,如sizeof和typeid
-
定义在一个auto序列的变量必须始终推导成同一类型
例如:
std::vector<std::string> ve;
std::vector<std::string>::iterator it = ve.begin();
我们可以用atuo来代替那个初始化类型:
auto it = ve.begin();
此外,如果是可用迭代器的对象的话,还可以这样操作:
int main(){
vector<int>v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
for(auto i : v){
cout<<i<<" ";
}
cout<<endl;
return 0;
}
auto在这里就是简单的替换了int类型。