算法题复盘+代码解读(2)—— 两数之和
题目:给定一个整数数组 nums 和一个整数目标值 target ,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。可以假设每种输入只会对应一个答案,并且不能使用两次相同的元素。
可能应用场景:
电商平台的优惠券和促销系统等。
购物车中的商品价格cart_prices = [ 199, 299, 399, 99, 599]
而优惠券是满500-100的,coupon_target = 500
那就可以寻找两件商品价格之和超过500的最优组合
indices = twoSumVariant( cart_prices , coupon_target) ,返回最优组合的索引,即对应的商品
文字思路:
第一个想到的办法肯定是暴力枚举,一个一个加起来,看看是否等于target值就可以了。
第二办法是哈希表,这个东西其实对初学者挺难理解的,核心思想是记下见过的值。
首先准备工作是创建一个笔记本(哈希表),用来记录数值和它的序号;
流程是边看边记,边记边找。假如数组是[2, 7,11, 15],目标值是9,我首先看到的是数值2,位置是0(边看边记),思考我要找的是 9 - 2 = 7,检查笔记本里面没有7,就把数值2和位置0记进笔记本里;接着往下看(循环),发现是7,思考我要找的是 9 - 7 = 2,检查笔记本里有2,那就对了,那我就返回对应的结果[ 0,1];如果一直没找到,就返回空表示无解。
强化理解:比如你需要50元,你自己有20元,那这个时候你并不需要每个人都去问“你有30元吗”,而是只知道每个人有多少钱,当遇到有30元的人时,就可以立刻合作,减少了询问的次数。
暴力枚举代码:
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {for (int i = 0; i < numsSize; ++i) {for (int j = i + 1; j < numsSize; ++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;
}
哈希表方法代码:
解读一下:这里的hashTable是先定义的一个结构体,后面这个hashtable是定义的一个指针,指向这个hashTable结构体,一开始看的时候会觉得怎么hashTable又来个hashtable的,到底什么关系,看的头都晕了。。UT_hash_handle hh是一个宏,是必须要有的,用来调用自带的一些方法。其余的代码就比较容易理解了。
struct hashTable {int key;int val;UT_hash_handle hh;
};struct hashTable* hashtable;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, key, tmp);} 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]);if (it != NULL) {int* ret = malloc(sizeof(int) * 2);ret[0] = it->val, ret[1] = i;*returnSize = 2;return ret;}insert(nums[i], i);}*returnSize = 0;return NULL;
}
C++哈希表方法实现:
很明显,C++的代码简洁很多,简单说说和C有哪些不同,便于快速上手C++。
按顺序,先从class(类)说起,可以理解它是C里面结构体的升级版,C++的class可以包含函数(称为成员函数或方法),C的struct只能包含数据。另外C++的class默认成员是private,也就是外部不能访问的,而struct默认是public,类外部是可以直接访问的,所以C++相当于是兼容了C。
vector是C++的动态数组,类似C的malloc,但他可以自动管理内存,无需手动分配和释放。
看到下面的第3行代码,vector<int> 表示这个函数返回的是一个整形的动态数组,twoSum是函数名;这个函数接收两个参数:nums和target,vector<int>& nums的意思是直接操作nums的原数组,而不是先把它拷贝进来再操作。
接着是第4行代码,这个unordered_map<>是C++里面创建哈希表的声明,<int,int>这个是表示键(key)和值(value)都是int类型,hashtable是自定义的变量名。
看到下面第5行代码,典型for循环,直接用nums.size()就可以获取nums数组的大小,确实是比C简洁多了,另外C++里面是更倾向与++i的。
来到第6行代码,auto是自适应参数类型的写法,it是自定义的名字,auto it 也可以写成int it,就这个意思。hashtable.find(x)是在哈希表里面查找键x,然后返回一个值,这里用auto it接收。
第7行代码,hashtable.end()表示最后一个元素的下一个元素,也就是空白处,说明找到的话,就执行第8行代码,返回{ it-> second, i},这是一个动态数组的写法,相当于将找到的两个下标打包成一个动态数组(vector<int>)返回,it->second表示哈希表中it对应的【值】,而first表示【键】,相当于C里面的key和value;i表示当前数字的索引。
代码来到第9行,num[i] 是当前哈希表的键(key),i是当前数组的索引(值)。
比如nums=[2, 7, 11, 15],那nums[0]是等于2的,nums[1]=7;
如果执行hashtable[2]=0,即hashtable[nums[0]]=0,就相当于哈希表里会有{2:0},再执行一个hashtable[7]=1,即hashtable[nums[1]]=1,哈希表里就会有{2:0, 7:1}。
最后一行,return {}; 表示返回一个空容器。
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 {};}
};
欢迎批评指正!
--------------------------------------------- END ---------------------------------------------