two sum
two-sum
https://www.nowcoder.com/questionTerminal/20ef0972485e41019e39543e8e895b7f
题目描述:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
题目中的陷阱:
要求返回两个数的下标不是两个数本身;
不可使用一个数两次;比如[2,7,5,5], target=10, 返回[2,2]错误, 应该返回[2,3]
解法1:暴力遍历(Brute Force)
思路简单,第一层遍历每个数,在遍历每个数的同时,遍历其他的数是不是他的补(target-self, complement)
class Solution { // 最简单的方法,就是从第一个数n1开始遍历,找到(target-n1)的数的下标; public: vector<int> twoSum(vector<int> &numbers, int target) { // 返回数组,记录两个下标 vector<int> res {0,0}; for(int i=0; i<numbers.size(); i++){ for(int j=i+1; j<numbers.size(); j++){ if( numbers[j] == (target-numbers[i]) ){ res[0] = i+1; res[1] = j+1; return res; } } } throw invalid_argument("No two sum solution"); } };
复杂度:
时间复杂度:O(nˆ2)
空间复杂度: O(1)
解法2:哈希表(Two-pass Hash Table)
为了改善时间复杂度,我们需要一种高效的方便来查询complement是否在数组中;也就是说,一旦complement存在,我们就要知道它的下标,那么什么结构能方便快速的映射数据和他的下标?Hash Table。
在c++中,无序hash table的查询时间都是O(1),这个是非常强悍的,也正是我们需要的。解题思路也就明朗了,我们需要两个非嵌套的循环,第一次遍历,我们建立查询表,把数组元素和他们的index建立hash table,通过数据可以map到下标:vale => index
第二次遍历,我们检查每个元素的补(complement = target-numbers[i]),是否存在在table中,一定要注意的是,complement不可以是number[i]本身!,在第一种解法中,无需担心这一点,因为解法一的嵌套循环每次从外层嵌套的index+1开始。
class Solution { // 最简单的方法,就是从第一个数n1开始遍历,找到(target-n1)的数的下标; public: vector<int> twoSum(vector<int> &numbers, int target) { // 返回数组,记录两个下标 vector<int> res ; unordered_map<int, int> mymap; // 建立hash table array's value=> array's index for(int i=0; i<numbers.size(); i++){ mymap[numbers[i]] = i; } // 第二次遍历,查询是否存在当前数的complement在hash table中 for(int i=0; i<numbers.size(); i++){ int complement = target - numbers[i]; if(mymap.count(complement)!=0 && mymap.at(complement)!=i){ res = {i+1, mymap.at(complement)+1}; return res; } } throw invalid_argument("No two sum solution"); } };
特别注意:
第二种方法中,在c++的map函数的insert只是插入新的元素,不会更新元素的,比如:
myMap.insert("a", "apple" ); myMap.insert("b", "bannana" ); myMap.insert("c", "cherry" ); myMap.insert("c", "clementine");
最后的表是:"a"=>"apple"; "b"=>"bannana"; "c"=>"cherry" 坑爹啊!这里一定要用operator[ ]才有改的操作;所有这里会有错误,在这个例子下[0,3,4,0] target=0:
0=>0, 3=>1, 4=>2,在第一次遍历的时候,complement为0,对应的i=0,table中的i=0,不成立,直到i=3的时候,查到complement为index=0;因此在用insert没有更新表的情况下,答案是[3,0],而正确的答案是[0,3];
用[ ]去添加元素的话,就会有更新的效果,也会得到正确的答案;在c++17中有新的操作纠正了insert的操作;
std::map<std::string, std::string> myMap; myMap.insert_or_assign("a", "apple" ); myMap.insert_or_assign("b", "bannana" ); myMap.insert_or_assign("c", "cherry" ); myMap.insert_or_assign("c", "clementine");
输出:
a : apple
b : bannana
c : clementine