1、两数之和:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。你可以按任意顺序返回答案。
示例 1:
输入: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]
提示:
2 <= nums.length <= 103
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
【数据结构与算法】知识点提取:线性查找-数组,哈希表
方法一:【线性查找 0(n) 】数组枚举法,使用两个for循环遍历数组,数组中每个元素与其后面每个元素依次相加,判断是否等于target。
c实现:注意该函数返回后记得释放内存free,防止内存泄漏。
int* twoSum(int* nums, int numSize, int target, int* returnSize)
{
if(nums == NULL || numSize == 0)
{
*returnSize = 0;
return NULL;
}
for(int i = 0; i < numSize; i++)
{
for(int j = i+1; j < numSize; j++)
{
if(nums[i] + nums[j] == target)
{
int *ret = malloc(sizeof(int)*2);
ret[0] = i;
ret[1] = j;
*returnSize = 2;
return ret;
}
}
}
*returnSize = 0;
return NULL;
}
c++实现:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
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) {
return {i, j};
}
}
}
return {};
}
};
复杂度分析:
时间复杂度:0(N^2),其中N是数组中的元素数量。最坏情况下数组中任意两个数都要被匹配一次。
空间复杂度:0(1)。
方法二:【哈希表】
思路及算法
注意到方法一的时间复杂度较高的原因是寻找 target - x 的时间复杂度过高。因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找出它的索引。使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O(N^2) 降低到 O(1)。这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
c实现:
struct hashTable
{
int key;
int val;
UT_hash_handle hh;
}
struct hashTable* find(int ikey)
{
struct hashTable* tmp;
HASH_FIND_INT(hashTable, &ikey, tmp);
return tmp;
}
void insert(int ikey, int ival)
{
struct hashTable* it = find(ikey);
if(it == NULL)
{
struct hashTable* tmp = malloc(sizeof(struct hashTable));
tmp->key = ikey;
tmp->val = ival;
HASH_ADD_INT(hashtable, ikey , tmp);
}
else
{
it->val = ival;
}
}
int* twoSum(int* nums, int numSize, int target, int* returnSize)
{
if(nums == NULL || numSize == 0)
{
*returnSize = 0;
return NULL;
}
hashtable = NULL;
for(int i = 0; i < numSize; i++)
{
struct hashTable * it = find(target - nums[i]);
if(it != NULL)
{
int *ret = malloc(sizeof(int)*2);
ret[0] = it->val;
ret[1] = i;
*returnSize = 2;
return ret;
}
intsert(nums[i],i);
}
*returnSize = 0;
return NULL;
}
c++实现:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable;
for (int i = 0; i < nums.size(); ++i) {
auto it = hashtable.find(target - nums[i]);
if (it != hashtable.end()) {
return {it->second, i};
}
hashtable[nums[i]] = i;
}
return {};
}
};
java实现:
// 时间复杂度:O(n)
// 空间复杂度:O(n)
// 空间换时间
public int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 0) return new int[0];
// 数据预处理
HashMap<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) { // O(n)
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) { // O(n)
int x = nums[i];
// 哈希查找 - O(1)
if (map.containsKey(target - x)) {
int index = map.get(target - x);
// i 和 index 不是同一个元素,同一个元素不能使用两次
if (i != index) return new int[]{i, index};
}
}
return new int[0];
}
java实现优化:
// 时间复杂度:O(n)
// 空间复杂度:O(n)
// 空间换时间
public int[] twoSum(int[] nums, int target) {
if (nums == null || nums.length == 0) return new int[0];
int n = nums.length;
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < n; i++) { // O(n)
int x = nums[i];
// 哈希查找
if (map.containsKey(target - x)) {
int index = map.get(target - x);
return new int[]{i, index};
}
map.put(x, i);
}
return new int[0];
}
复杂度分析
时间复杂度:O(N),其中 NN 是数组中的元素数量。对于每一个元素 x,我们可以 O(1)地寻找 target - x。
空间复杂度:O(N),其中 NN 是数组中的元素数量。主要为哈希表的开销。
现在你再刷下面的 5 道类似题目:(举一反三,总结知识点,反复思考)
leetcode 167 号算法题:两数之和Ⅱ - 输入有序数组
leetcode 170 号算法题:两数之和Ⅲ - 数据结构设计
leetcode 653 号算法题:两数之和Ⅳ - 输入 BST
leetcode 15 号算法题:三数之和
leetcode 18 号算法题:四数之和
首先, 同学们不要为了刷题而刷题,要掌握核心该题知识点,做到举一反三。平时练习基本思路:碰到一个算法, 不知道最优解的话, 先用最简单、最暴力的解法-->
找到暴力解法的性能瓶颈,-->进行优化,不断的优化, 优化的方法可能有好几个, 你可以都进行分析-->选择一个最优的解法, 看看还能不能继续优化, 在追求程序极致性能的过程中, 你的功力会慢慢提高的,做算法题的效率也会提高,自信心也就建立了。最后总结反思,并构建自己的【数据结构与算法】知识体系。