Leetcode刷题记录:1 两数之和(数组 哈希)
题干:
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
- 暴力解法:用两个for循环嵌套,一一枚举,直至发现和为target,返回这两个数的下标。
/**
1. 采用动态数组的方式;
2. Note: The returned array must be malloced, assume caller calls free().
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int i;
int j;
int *ret;
for(i=0;i<numsSize;i++){
for(j=i+1;j<numsSize;j++){
if(nums[i]+nums[j]==target){
ret=(int*)malloc(sizeof(int)*2);
ret[0]=i;
ret[1]=j;
*returnSize=2;
return ret;
}
}
}
*returnSize=0;
return 0;
}
注:该方法时间复杂度为O(n^2),空间复杂度为O(1)。
- 哈希表法:对暴力解法进行改进,第二个for循环即在找target-nums[i]中时间复杂度为O(n),用哈希表时间复杂度O(1)来实现target-nums[i]的查找。
整体思路为:
对数组遍历,针对第一个for循环的元素nums[i],计算出其和目标值target之间的差值,即anotherNum=target-nums[i]。
之后在哈希表中查找anotherNum,如果存在,则表示找到了两个数,其和等于target。若不存在,则将当前考察的元素值作为key,其对应的索引 i 作为value,存入哈希表中。
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
struct Hashmap{
int key;
int value;
UT_hash_handle hh;
};
struct Hashmap* hashtable;
struct Hashmap* find(int ikey){
struct Hashmap* temp;
HASH_FIND_INT(hashtable,&ikey,temp);
return temp;
}
void insert(int ikey,int ivalue){
struct Hashmap* temo;
temo=find(ikey);
if(temo==NULL){
struct Hashmap* addhash=malloc(sizeof(struct Hashmap));
addhash->key=ikey;
addhash->value=ivalue;
HASH_ADD_INT(hashtable,key,addhash);
}
else{
temo->value=ivalue;
}
}
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
hashtable=NULL;
int i;
int j;
int anothernumber;
int *ret;
for(i=0;i<numsSize;i++){
anothernumber = target-nums[i];
struct Hashmap* hashelement=find(anothernumber);
if(hashelement!=NULL){
ret=malloc(sizeof(int)*2);
ret[0]=hashelement->value;
ret[1]=i;
*returnSize=2;
return ret;
}
insert(nums[i],i);
}
*returnSize=0;
return NULL;
}
注:时间复杂度为O(n),空间复杂度为O(n)。
两次运行时间对比如下图:
由上图前后执行时间与内存消耗对比明显。
补充:
哈希法代码使用了uthash代码。uthash
是C的开源代码,它实现了常见的hash操作函数,例如构建、查找、插入、删除等。该套开源代码采用宏的方式实现hash函数的相关功能,支持C语言的任意数据结构最为key值,甚至可以采用多个值作为key,无论是自定义的struct还是基本数据类型,需要注意的是不同类型的key其操作接口方式略有不通。
使用uthash代码时只需要包含头文件"uthash.h"即可。由于该代码采用宏的方式实现,所有的实现代码都在uthash.h文件中,因此只需要在自己的代码中包含该头文件即可。
uthash的英文使用文档介绍为:
http://troydhanson.github.io/uthash/userguide.html#_add_item
错误记录:
- 错误记录1:
在上图第二次截图中,运行错误是因为twoSum函数中为对hashtable进行初始化
原函数为:
int* twoSum(int* nums, int numsSize, int target, int* returnSize){
int i;
int j;
int anothernumber;
int *ret;
for(i=0;i<numsSize;i++){
anothernumber = target-nums[i];
struct Hashmap* hashelement=find(anothernumber);
if(hashelement!=NULL){
ret=malloc(sizeof(int)*2);
ret[0]=hashelement->value;
ret[1]=i;
*returnSize=2;
return ret;
}
insert(nums[i],i);
}
*returnSize=0;
return NULL;
}
加入初始化
hashtable=NULL;
之后运行正确。
输入为:
[3,3]
6
我的输出:
[0,0]
正确输出:
[0,1]
原因猜测是上一个测试的影响,具体还不清楚,等明白了再补充…
- 错误记录2:
HASH_ADD_INT(hashtable,key,addhash);
这个函数第一个参数是哈希表,第二个参数是键字段的名称,第三个参数是要增加的指针结构,这个地方如果写成
HASH_ADD_INT(hashtable,ikey,addhash);就会输出NULL。
HASH_ADD_INT()函数要注意!!!