最近更新于 2024-05-05 14:19
题目
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
输入:nums = [2,7,11,15], target = 9 输出:[0,1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
Python3
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: size = len(nums) # 输入数组的大小 for i in range(size-1): # 第一个加数从数组第一个开始,倒数第二个结束 add_num = target - nums[i] if add_num in nums[i+1:]: # 第二个加数从第一个加数的后一位开始,最后一个结束 return [i, (nums[i+1:].index(add_num) + i + 1)]
这个是暴力枚举,依次两个两个地挑选看是否满足条件。
class Solution: def twoSum(self, nums: List[int], target: int) -> List[int]: size = len(nums) dict = {} for idx, val in enumerate(nums): dict[val] = idx # 将数组每一个元素放入字典,元素值对应下标 for idx, val in enumerate(nums): add_index = dict.get(target - val) # 求出另外一个加数,并搜索字典中是否存在,存在且不是重复第一个就找出来了 if add_index is not None and idx != add_index: return [idx,add_index]
相比于前面暴力的解法,时间降了一个数量级,我提交几次,时间波动一点,显示的击败用户百分比却有十几二十的跳动,这点时间变化是随机不可控的,在能忽略的范围内。
Python 中字典的搜索是基于哈希查找,时间复杂度为 O(1)。
C
/** * Note: The returned array must be malloced, assume caller calls free(). */ int* twoSum(int* nums, int numsSize, int target, int* returnSize){ int *result = (int *) malloc(sizeof(int *) * 2); // 申请两个整数大小的内存空间 memset(result, 0, sizeof(result)); // 初始化为 0 for (int i = 0; i < numsSize - 1; ++i) { for (int j = i + 1; j < numsSize; ++j) { if (nums[i] + nums[j] == target) { result[0] = i; result[1] = j; } } } *returnSize = 2; // 说实话没理解这个参数的意义,明明题目说了返回两个结果的下标,还要我来告诉? return result; }
这个也是暴力枚举法,不过和 Python 比起来,速度还是快一倍左右。当然这是在传入数组只有几个元素的情况下,如果成百上千处理的时候,差距应该就会明显起来。
/** @struct 类似于 Python 中字典的结构(键-值) */ typedef struct hashTable { int key; int val; UT_hash_handle hh; // uthash:https://github.com/troydhanson/uthash } hashTable; hashTable *hashtable = NULL; /** @brief 查找哈希表 @param key 查找值 @return 找到的哈希节点,未找到返回 NULL */ hashTable *find(int key) { hashTable *tmp; HASH_FIND_INT(hashtable, &key, tmp); return tmp; } /** @brief 哈希表插值 @param ikey 插入键 @param ival 插入值 */ void insert(int ikey, int ival) { hashTable *it = find(ikey); if (it == NULL) // 不存在则插值 { hashTable *tmp = (hashTable *) malloc(sizeof(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) { hashTable *it = find(target - nums[i]); if (it != NULL) { int *ret = (int *) malloc(sizeof(int) * 2); ret[0] = it->val; ret[1] = i; *returnSize = 2; return ret; } insert(nums[i], i); } *returnSize = 0; return NULL; }
该算法的查找部分是基于哈希查找,代码中实现了一个类似于 Python 字典的结构。思路主体和 Python 的一样,但是有点小不同。Python 代码我写的是先将所有数组元素插入字典,后面循环的查找的时候,每次查找都要将所有数组元素搜索一遍。而这里我用 C 写的时候换了个方式,哈希表先为空,每次循环查找,如果没找到,则将当此循环的数组元素和下标插入,插入一次后哈希表有一个元素,下一次循环从上一轮插入后的哈希表中搜索,如果又没找到,又再插入,才有两个元素,最坏的情况是,数组最后两个元素满足要求,则最后一次循环时,哈希表元素个数才是 n-1 个。即相对写 Python 代码的那个思路,每次循环都从 n 个元素里搜索处理量更小一点,总体上对于哈希查找差异其实并不是很大。