
原链接:https://leetcode.cn/problems/two-sum/
难度:简单
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例一:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
进阶: 你可以想出一个时间复杂度小于 O(n2) 的算法吗?
二、解题思路 1.暴力枚举很容易想到两层for循环(写出来也木得工作)。第一层从i开始,第二层从i+1开始。
class Solution {
public int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; ++i) {
for (int j = i + 1; j < n; ++j) {
if (nums[i] + nums[j] == target) {
return new int[]{i, j};
}
}
}
return new int[0];
}
}
复杂度分析:
· 时间复杂度:O(N2)。
· 空间复杂度:O(1)。
可以看到第二层的遍历有些浪费,可以通过字典记录下来。
暴力枚举时间复杂度较高的原因是寻找 target - nums[i] 的时间复杂度过高。使用哈希表,可以将寻找 target - nums[i]的时间复杂度降低到从 O(N) 降低到 O(1)。
class Solution {
public:
vector twoSum(vector& nums, int target) {
map m;
for(int i=0; i < nums.size(); i++){
auto it = m.find(target - nums[i]);
//cout<second<
return {i, it->second};
}
m[nums[i]] = i;
}
return {};
}
};
复杂度分析:
· 时间复杂度:O(N2)。
· 空间复杂度:O(1)。
底层是红黑树,该结构具有自动排序的功能,因为红黑树本来就是一种二叉搜索树,所以从begin()遍历到end()得到的key值是有序的,这也是map的一个特点。
空间复杂度为O(n) 随着节点的增加才增加。
查找的时间时间复杂度 则固定是O(log(n))。
优点:有序性。
缺点:空间占用高。
hash_map基于hash table(哈希表)。
优点:数据的存储和查找消耗的时间低,几乎可以看成是常数时间;而代价仅仅是消耗比较多的内存。然而在当前可利用内存越来越多的情况下,用空间换时间的做法是值得的。另外,编码比较容易也是它的特点之一。
查找的时间复杂度可以达到O(1),基本上都是比map快的。
hash_map,首先分配一大片内存,形成许多桶。是利用hash函数,对key进行映射到不同区域(桶)进行保存。其插入过程是:
3.unordered_map最初的 C++ 标准库中没有类似 hash_map 的实现,但不同实现者自己提供了非标准的 hash_map。 因为这些实现不是遵循标准编写的,所以它们在功能和性能保证方面都有细微差别。从 C++ 11 开始,hash_map 实现已被添加到标准库中。但为了防止与已开发的代码存在冲突,决定使用替代名称 unordered_map。这个名字其实更具描述性,因为它暗示了该类元素的无序性。而且unordered_map已经成为标准,不建议再使用hash_map。
hash_map是SGI实现,而unordered_map是C++11标准中新增的。虽然大多数情况下hash table实现的map都会比红黑树实现的map查找快,但不是绝对的,因为冲突过多的话,可能耗费时间比map还要多。所以对于前者基本上都会有个rehash的操作,而由于rehash的处理不一样,也导致了hash_map和unordered_map的性能有一些差别。hash_map的rehash简单粗暴,直接就是更新bucket数目为需要的数目就行了,而不会像unordered_map那样子进行数量的优化。