题目
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
题目解析及优化
-
暴力解决法
暴力解决法很好理解,逐步遍历所有的数组组合,找出答案。由于时间复杂度很高,就不贴代码了,太弟弟了。
-
排序+双指针法
先将数组排好序,用两个指针分别指向开始结尾,开始往中间聚拢。如果两指针碰头,就说明没有找到答案。
代码如下:
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { //ans存储答案的两个数 vector<int> ans; //temp存储排序之后的数组, //由于之后需要原数组的位置,不能对原数组直接排序 vector<int> temp; temp=nums; int n=temp.size(); //sort排序的方法推荐读者掌握,很多时候都可以用到 sort(temp.begin(),temp.end()); //两个下标指向开始和结尾 int i=0,j=n-1; while(i<j) if(temp[i]+temp[j]>target){j--;} else if(temp[i]+temp[j]<target){i++;} else break; } if(i<j){ //如果两指针没有碰头说明有答案存在,与之前的下标对应 for(int k=0;k<n;k++){ if(i<n&&nums[k]==temp[i]){ ans.push_back(k); i=n; } else if(j<n&&nums[k]==temp[j]){ ans.push_back(k); j=n; } if(i==n&&j==n)return ans; } } return ans; } };
-
哈希表
此方法需要哈希表的一些知识,可以点击此处。
我们是在知道和的情况下去找两个加数,那么自然可以用一个加数去找另一个加数,遍历一个数组的元素,如果和减去这个元素的值在另一个数组里面出现过,那么答案存在。这个方法明显比暴力解决法更简洁。
在用哈希表的时候也有两种方法:
- 用两次哈希表:第一次将数组的值和索引添加到表中,第二次遍历寻找每个元素以及target-nums[i],切记,同一个元素不能用两次。
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int> ans; map<int,int> p; int l = nums.size(); for(int i = 0;i<l;i++){ //p.second是数字的下标+1,只要存在就不会为0 p[nums[i]] = i+1; } for(int i = 0;i<l;i++){ //用两个判断条件 if(p[target-nums[i]]&&(i!=p[target-nums[i]]-1)){ ans.push_back(i); ans.push_back(p[target-nums[i]]-1); return ans; } } return ans; } };
-
用一次哈希表:其实在上述的用两次哈希表的方法中,会有重复计算的部分,在找一个元素的时候,同时已经找了target-nums[i]的元素,两者选其一即可,在将数组添加到表中的时候,就可以直接寻找,不会影响答案,而且比上述方法更省时间。代码如下:
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { vector<int> ans; map<int,int> p; int l = nums.size(); for(int i = 0;i<l;i++){ if(p[target-nums[i]]){ ans.push_back(i); ans.push_back(p[target-nums[i]]-1); return ans; } //将赋值放在判断之后就能避免判断同一元素的情况 p[nums[i]] = i+1; } return ans; } };
提交之后,发现用时更短的人用的是unordered_map,由于底层原理的不同,unordered_map用的是哈希表,查找的时间复杂度为o(1)
心得及总结
- 暴力法并不是一无是处,有时候在需要时间换取空间的时候可以采取暴力法。(少见)
- 除了暴力法以外的几种方法,均采用了元素互补的思想:即两个元素之间有某种关系的时候,可以用一个元素去找另一个元素,而不用专门去找。
- 这个题目中是用一次哈希表时间胜出了,但是如果题目中是有序数组的话,用指针就会更优秀了。
- 在哈希表的使用过程中,并没有用到map的自动有序的特性,所以可以使用unordered_map去更节省时间。(充分利用不同数据结构的优点)
知识补充
-
三种map的优缺点
运行效率方面:unordered_map最高,hash_map其次,而map效率最低单提供了有序的序列。
占用内存方面:hash_map内存占用最低,unordered_map其次(数量少时优于hash_map),而map占用最高。
需要无序容器时候用unordered_map,有序容器时候用map。