lintcode Two Sum - Input array is sorted

本文介绍了一种在已排序的整数数组中寻找两个数使其和为目标值的方法。提供了两种解决方案,一种使用二分查找,另一种采用双指针技术。通过实际例子展示了算法的应用。

Two Sum - Input array is sorted 

Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.

The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based.

 注意事项

You may assume that each input would have exactly one solution.

样例

Given nums = [2, 7, 11, 15], target = 9
return [1, 2]


分析:做题的时候一定要读清楚题意!这道题题目中写明了find two numbers such that....,我还在考虑多个数字相加等于target的情况,真是可恨

下面上代码,第一种方法是达不到时间要求但是想法对的,参考过别人,因为用了二分法所以时间复杂度为O(nlgn),差不多超过1000ms了

而方法二,13ms就搞定了  O(nlgn)和O(n)的差距也是大啊,下面是代码

方法一:

class Solution {//从头部开始,在后面用二分法查找符合条件的另一个数
public:
    /*
     * @param nums an array of Integer
     * @param target = nums[index1] + nums[index2]
     * @return [index1 + 1, index2 + 1] (index1 < index2)
     */
    vector<int> twoSum(vector<int> &nums, int target) {
        if(nums.size()<=0)
			return  vector<int>(-1);
		for(int i=0;i<nums.size();i++)
		{
			int k=target-nums[i];
			int Left=i,Right=nums.size()-1;
			while(Left<=Right)
			{
				int Mid=(Left+Right)/2;
				if(k==nums[Mid])
					return {i+1,Mid+1};
				else if(k<nums[Mid])
					{
						Right=Mid;
				}
				else if(k>nums[Mid])
					Left=Mid-1;
			}
		}


    }
};
方法二:用两个指针一前一后夹击

class Solution {
public:
    /*
     * @param nums an array of Integer
     * @param target = nums[index1] + nums[index2]
     * @return [index1 + 1, index2 + 1] (index1 < index2)
     */
    vector<int> twoSum(vector<int> &nums, int target) {
        if(nums.size()<=0)
			return  {-1};
		int s=0,e=nums.size()-1;//莫忘记减一
		int sum=nums[s]+nums[e];
		while(sum!=target)
		{
		    if(sum<target)
		    {
		        s++;
		    }
		    else
		    {
		        e--;
		    }
		    sum=nums[s]+nums[e];
		}
		return{s+1,e+1};
        

    }
};


<think>我们注意到用户提供的引用[1][2][3]中都是使用双重循环的暴力解法,时间复杂度为$O(n^2)$,空间复杂度为$O(1)$。但用户在上一次问题中,我们提到了哈希表法(时间$O(n)$,空间$O(n)$)和双指针法(适用于已排序数组)。然而,用户本次要求补全C语言实现的twoSum函数,并且引用[3]提供了C语言的暴力解法代码。 考虑到用户可能希望得到更高效的解法,但C语言中实现哈希表相对复杂(需要自己实现或使用第三方库),而暴力解法在数据量不大时也是可行的。因此,我们将提供两种C语言实现: 1. 暴力解法(直接参考引用[3]) 2. 使用哈希表的解法(需要自己实现一个简单的哈希表) ### 方法1:暴力解法(双重循环) - 思路:遍历每个元素x,并查找是否存在一个值等于target-x的元素(通过内层循环)。 - 时间复杂度:$O(n^2)$ - 空间复杂度:$O(1)$ - 代码实现(与引用[3]类似): ```c #include <stdlib.h> 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 = (int*)malloc(sizeof(int) * 2); ret[0] = i; ret[1] = j; *returnSize = 2; return ret; } } } *returnSize = 0; return NULL; } ``` ### 方法2:哈希表法 - 思路:使用哈希表存储每个元素的索引。遍历数组,对于每个元素`nums[i]`,计算补数`complement = target - nums[i]`,然后检查补数是否在哈希表中。如果在,则返回这两个数的索引。 - 时间复杂度:$O(n)$ - 空间复杂度:$O(n)$ 由于C语言标准库中没有哈希表,我们需要自己实现一个简单的哈希表(例如,使用开放寻址法或链地址法)。这里我们使用开放寻址法,并假设数组中的元素值不会太大(或者我们可以取一个合适的模数)。 注意:哈希表的大小通常选择大于数组长度两倍的质数,以减少冲突。这里我们选择10007(一个质数),并假设数组长度不超过5000。 具体步骤: 1. 定义一个哈希表结构,用于存储值和索引。 2. 实现哈希表的插入和查找函数。 3. 在twoSum函数中,遍历数组,对于每个元素,查找其补数是否在哈希表中,如果找到则返回;否则将当前元素插入哈希表。 代码实现如下(包含哈希表实现): ```c #include <stdlib.h> #define HASH_SIZE 10007 typedef struct { int key; // 存储数组元素值 int val; // 存储数组索引 int used; // 标记该位置是否已被使用 } HashEntry; typedef struct { HashEntry* entries; } HashTable; // 初始化哈希表 HashTable* hash_table_create() { HashTable* table = (HashTable*)malloc(sizeof(HashTable)); table->entries = (HashEntry*)malloc(sizeof(HashEntry) * HASH_SIZE); for (int i = 0; i < HASH_SIZE; i++) { table->entries[i].used = 0; } return table; } // 销毁哈希表 void hash_table_destroy(HashTable* table) { free(table->entries); free(table); } // 哈希函数(简单的取模) int hash(int key) { return (abs(key) % HASH_SIZE); } // 向哈希表插入键值对 void hash_table_put(HashTable* table, int key, int val) { int idx = hash(key); // 开放寻址法处理冲突 while (table->entries[idx].used) { idx = (idx + 1) % HASH_SIZE; } table->entries[idx].key = key; table->entries[idx].val = val; table->entries[idx].used = 1; } // 从哈希表中查找键key,找到返回索引值,否则返回-1 int hash_table_get(HashTable* table, int key) { int idx = hash(key); while (table->entries[idx].used) { if (table->entries[idx].key == key) { return table->entries[idx].val; } idx = (idx + 1) % HASH_SIZE; } return -1; } int* twoSum(int* nums, int numsSize, int target, int* returnSize) { HashTable* table = hash_table_create(); for (int i = 0; i < numsSize; i++) { int complement = target - nums[i]; int comp_index = hash_table_get(table, complement); if (comp_index != -1) { int* ret = (int*)malloc(sizeof(int) * 2); ret[0] = comp_index; ret[1] = i; *returnSize = 2; hash_table_destroy(table); return ret; } hash_table_put(table, nums[i], i); } hash_table_destroy(table); *returnSize = 0; return NULL; } ``` ### 方法比较 - **暴力解法**:代码简单,适合小数组($n$较小),无额外空间开销。 - **哈希表法**:时间复杂度更优,适合大数组,但需要额外空间,且代码复杂。 ### 注意事项 1. 在哈希表实现中,我们使用了开放寻址法处理冲突,但冲突较多时性能会下降。在实际应用中,可能需要更复杂的哈希函数或动态调整哈希表大小。 2. 哈希表的大小`HASH_SIZE`需要根据数组大小合理选择,这里选择10007(质数)可以处理数组长度不超过5000的情况(因为开放寻址法要求哈希表大小至少为数组大小的两倍以上)。 3. 哈希表在查找时,如果补数不存在,则将当前数存入,这样后续的数就可以利用之前存入的数。 ### 相关问题 1. 如果数组中有重复元素,上述哈希表解法是否仍然有效? 2. 如何修改哈希表解法以支持返回所有满足条件的索引对(而不仅仅是一对)? 3. 如果数组已经排序,如何用双指针法在C语言中实现?时间复杂度是多少?</think>### C语言实现twoSum函数 根据您的要求,我将提供两种C语言实现方案:**暴力解法**(时间复杂度$O(n^2)$)和**哈希表优化解法**(时间复杂度$O(n)$)。两种方法都严格遵循问题要求:返回两个整数的下标,且不使用相同元素。 --- ### 方法1:暴力解法(双重循环) **时间复杂度**:$O(n^2)$ **空间复杂度**:$O(1)$ **思路**:通过两层循环遍历所有可能的元素组合,检查它们的和是否等于目标值。 ```c #include <stdlib.h> int* twoSum(int* nums, int numsSize, int target, int* returnSize) { for (int i = 0; i < numsSize - 1; i++) { for (int j = i + 1; j < numsSize; j++) { if (nums[i] + nums[j] == target) { int* result = (int*)malloc(2 * sizeof(int)); result[0] = i; result[1] = j; *returnSize = 2; return result; } } } *returnSize = 0; // 未找到解 return NULL; } ``` **调用示例**: ```c int main() { int nums[] = {2, 7, 11, 15}; int target = 9; int returnSize; int* result = twoSum(nums, 4, target, &returnSize); if (result != NULL) { printf("[%d, %d]\n", result[0], result[1]); // 输出:[0, 1] free(result); } return 0; } ``` --- ### 方法2:哈希表优化解法 **时间复杂度**:$O(n)$ **空间复杂度**:$O(n)$ **思路**:使用哈希表存储元素值到索引的映射,只需遍历一次数组即可找到解。 ```c #include <stdlib.h> #define HASH_SIZE 10007 // 质数哈希表大小 typedef struct { int key; // 数组元素值 int value; // 数组索引 int used; // 标记是否已使用 } HashEntry; int* twoSum(int* nums, int numsSize, int target, int* returnSize) { HashEntry* hashTable = (HashEntry*)calloc(HASH_SIZE, sizeof(HashEntry)); for (int i = 0; i < numsSize; i++) { int complement = target - nums[i]; int hashIndex = abs(complement) % HASH_SIZE; // 哈希函数 // 在哈希表中查找补数 while (hashTable[hashIndex].used) { if (hashTable[hashIndex].key == complement) { int* result = (int*)malloc(2 * sizeof(int)); result[0] = hashTable[hashIndex].value; result[1] = i; *returnSize = 2; free(hashTable); return result; } hashIndex = (hashIndex + 1) % HASH_SIZE; // 开放寻址法 } // 将当前元素插入哈希表 int insertIndex = abs(nums[i]) % HASH_SIZE; while (hashTable[insertIndex].used) { insertIndex = (insertIndex + 1) % HASH_SIZE; } hashTable[insertIndex].key = nums[i]; hashTable[insertIndex].value = i; hashTable[insertIndex].used = 1; } free(hashTable); *returnSize = 0; // 未找到解 return NULL; } ``` **关键实现细节**: 1. **哈希函数**:使用取模运算 `abs(key) % HASH_SIZE` 确保索引在有效范围内 2. **冲突解决**:采用开放寻址法(线性探测) 3. **内存管理**:动态分配哈希表,使用后释放内存 4. **补数查找**:对每个元素计算 `target - nums[i]` 并在哈希表中搜索 --- ### 方法对比 | **方法** | 时间复杂度 | 空间复杂度 | 适用场景 | |----------------|------------|------------|------------------------| | 暴力解法 | $O(n^2)$ | $O(1)$ | 小规模数组($n \leq 10^3$) | | 哈希表优化解法 | $O(n)$ | $O(n)$ | 大规模数组($n > 10^3$) | --- ### 相关问题 1. **哈希冲突优化** 如何改进哈希函数以减少开放寻址法的探测次数?使用二次探测或链地址法是否更优?[^1][^3] 2. **内存管理** 当数组元素值范围极大时(如$[-10^9, 10^9]$),如何设计动态扩容的哈希表避免内存浪费?[^3] 3. **多解扩展** 如果要求返回所有可能的索引组合(而非仅第一组解),应如何修改算法?时间复杂度如何变化?[^4] 4. **变体问题** 在已排序数组中(如[LeetCode 167](https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/)),如何用$O(1)$空间实现双指针解法?[^2][^5]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值