1.题目:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
2.示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
输入:nums = [3,2,4], target = 6
输出:[1,2]2
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
3.分析:
最简单的方法是暴力破解法,时间复杂度为O(n^2),这种方式非常简单,就是内外两个循环,这里不多做介绍。要想找到一种时间复杂度少于0(n^2)的方式,就要使用哈希算法,这里我介绍一个C语言的第三方库——uthash。它是用C语言实现的hash算法,它以宏定义的方式实现hash表,不仅加快了运行的速度,而且与key类型无关的优点。
用hash算法的解题思想:
为了方便说明,据如下例子:
数组:[2,11,7,15] target:9=2+7
解题思想:
target=加数+加数
遍历数组,每次遍历时,本轮循环的数组元素作为一个加数,去寻找另一个加数。那另一个加数在哪里啦?它被保存到hash表格中。因此每轮循环就只有两个操作:
(1)在hash表格中寻找另一个加数。
(2)如果有,那target的两个加数就找到了。
(3)如果没有,就用本轮循环的数组元素的值和下标构成一个新的hash节点,把这个节点加入到hash表格中。
(4)进入下轮循环,重复(1)(2)(3),直到找到两个加数
4.代码:
#include"uthash.h"//这个头文件是要下载的,它就是用C语言实现的hash
struct hashTable {
int key;//用来保存数组元素
int val;//用来保存下标
UT_hash_handle hh;//这个宏是必须要有的,我们后期不用它,但是如果要使用这个c语言的hash库,就要有这个宏,它相当于一个hash处理句柄
};
struct hashTable* hashtable;//定义一个哈希表格
//在hashtable表格中寻找是否有该数组元素值的节点。有,就返回该节点,反之返回NULL
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);//在hashTable中寻找是否已经存在该数组元素的节点,
if (it == NULL) {//如果没有找到,就要构造一个新的节点,通过这个节点保存该数组元素的值和下标
struct hashTable* tmp = malloc(sizeof(struct hashTable));//创建一个hashTable节点
tmp->key = ikey;//保存该数组元素的值
tmp->val = ival;//保存该数组元素的下标
HASH_ADD_INT(hashtable, key, tmp);//把新创建的hashtable节点添加到已经存在的哈希表格中去
} else {//如果在哈希表格中已经发现了一个与该数组元素相同的值的节点,那么只需要更新该节点的下标,不需要又添加一个新的节点来保存该数组元素值和下标了。这里是很明显的牺牲了时间复杂度,而提升了空间复杂度
it->val = ival;//就记录这个数组的下标
}
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
hashtable = NULL;//对于指针变量,在使用之前最好明确赋值,否则容易出现错误
for (int i = 0; i < numsSize; i++) {//遍历数组
struct hashTable* it = find(target - nums[i]);//查找nums[i]+?=target的这个?值,这里用的是:减数=和-另一个减数
if (it != NULL) {//判断建立的哈希表中是否有这个减数,如果有,则说明在数组中找到两个数之和为target
int* ret = malloc(sizeof(int) * 2);//开辟空间
//保存这两个函数的下标
ret[0] = it->val;
ret[1] = i;
*returnSize = 2;//这是返回数组的元素个数
return ret;//返回开辟数组
}
//如果在已经存在的hashTable中没有这个节点,那就把这个节点加入到hashTable中
insert(nums[i], i);
}
*returnSize = 0;
return NULL;
}
5.总结:
以上代码是官方提供的算法。本题花费了很多时间,如果用暴力运算是非常简单的,但是时间复杂度是O(n^2)。为了优化时间复杂度,是花费了很多时间的,最终都还是参考了官方算法才明白怎样优化。如果要想彻底弄清楚,必须要精通数据结构的hash算法。