1. 两数之和
1.强行遍历法
- 本题直接强行遍历可以完成,但是时间复杂度较高O(n2),以下代码为强行遍历。
- 注意j的起始位置,如果从0开始会产生重复运算,只需要从i+1开始即可。
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int>result;
int n=nums.size();
for(int i=0;i<n;i++){
for(int j=i+1;j<n;j++){
if(nums[i]+nums[j]==target){
result.push_back(i);
result.push_back(j);
}
}
}
return result;
}
};
2.哈希表法
-
使用哈希表的方式,将遇到过的数字保存下来,就能在O(1)的时间复杂度找到目标数字,总复杂度O(n)+O(1),O(n)来源于一次遍历;
-
但这里我们不使用简单的哈希表,因为还需要知道目标数字对应的下标,这里使用unordered_map来保存对应的数字位置,其实unordered_map的实现原理就是哈希表;
-
unordered_map和map类似,都是 <键,值>结构,可以根据key找到value,区别在于map会自动根据key排序,unordered_map如其名,不会自动排序,减小时间复杂度。这里我们不需要有序的map,遂使用unordered_map;
-
unordered_map的几种用法:
-
unordered_map.insert() 插入元素
-
unordered_map.empty() 是否为空
-
unordered_map.count() 如果Map中存在具有给定键的值,则此函数返回1,否则返回0。
-
unordered_map.find() 如果给定的键存在于unordered_map中,则它向该元素返回一个迭代器,否则返回映射迭代器的末尾。
-
擦除操作:unordered_map.erase(const key_type& k)根据键值擦除
擦除操作:unordered_map.erase(const_iterator position)根据迭代器擦除
-
-
迭代器写法:unordered_map<int,int>::iterator it;需要写上完整的键值对结构和iterator,但可以直接用auto代替
-
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int,int> mapp;//map用来保存数字对应的位置编号 int n=nums.size(); for(int i=0;i<n;i++){ // unordered_map<int,int>::iterator it=mapp.find(target-nums[i]); auto it=mapp.find(target-nums[i]); if(it!=mapp.end()){ return{it->second,i};//注意元素是按序插入的,如果找到了结果,那么这个结果的序号一定小于i } mapp[nums[i]]=i; } return {};//注意力扣是不支持没有确定的返回值的,一定在结尾要有返回 } };
3.unordered_map的原理
-
如果算法初学,可以不深究原理。以下内容建议理解哈希散列后阅读。
-
元素在内部不以任何特定顺序排序,而是组织进桶中。元素放进哪个桶完全依赖于其键的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。
还有一种叫法,很多教材都叫它 拉链法、开链法,其实都是一回事。
也就是在出现散列冲突时,就挂在这个位置的后面,像放在桶里或者是延长拉链。
-
哈希桶的实现原理,使得unordered_map的插入、查找、删除时间复杂度都在常数复杂度内。
-
但是如果哈希冲突太多,会导致每个桶内的链表节点太长,哈希表时间复杂度接近于链表,成为哈希退化,需要重构哈希表。