题目
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
1 2 3 4
| Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1].
|
题目大意
在数组中找到两个数,它们的和等于给定的目标值 target
,并返回这两个数在数组中的下标。要求每个元素只能使用一次,且保证有且仅有一个解。
解题思路
方法一:双层循环(暴力枚举)
思路
通过双重循环遍历数组中的每一对元素,检查它们的和是否等于目标值 target
。外层循环控制第一个元素的索引 i
,内层循环控制第二个元素的索引 j
(j > i
以避免重复使用同一元素)。
复杂度分析
- 时间复杂度:O(n²),其中
n
是数组的长度。最坏情况下需要遍历所有可能的元素对。
- 空间复杂度:O(1),仅使用常数额外空间。
方法二:哈希表(优化查找)
思路
利用哈希表(字典)存储已遍历元素的值及其下标。对于当前遍历的元素 nums[i]
,计算需要的补数 complement = target - nums[i]
,并检查补数是否已存在于哈希表中:
- 若存在,则返回哈希表中补数的下标和当前元素的下标
i
。
- 若不存在,则将当前元素的值和下标存入哈希表,继续遍历。
这种方法通过空间换时间,将查找补数的时间复杂度从 O(n) 降为 O(1)。
1. Two Sum
题目
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能重复使用。
你可以按任意顺序返回答案。
示例:
plaintext
复制
1 2 3
| 输入:nums = [2, 7, 11, 15], target = 9 输出:[0, 1] 解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
|
题目大意
在数组中找到两个数,它们的和等于给定的目标值 target
,并返回这两个数在数组中的下标。要求每个元素只能使用一次,且保证有且仅有一个解。
解题思路
方法一:双层循环(暴力枚举)
思路
通过双重循环遍历数组中的每一对元素,检查它们的和是否等于目标值 target
。外层循环控制第一个元素的索引 i
,内层循环控制第二个元素的索引 j
(j > i
以避免重复使用同一元素)。
复杂度分析
- 时间复杂度:O(n²),其中
n
是数组的长度。最坏情况下需要遍历所有可能的元素对。
- 空间复杂度:O(1),仅使用常数额外空间。
方法二:哈希表(优化查找)
思路
利用哈希表(字典)存储已遍历元素的值及其下标。对于当前遍历的元素 nums[i]
,计算需要的补数 complement = target - nums[i]
,并检查补数是否已存在于哈希表中:
- 若存在,则返回哈希表中补数的下标和当前元素的下标
i
。
- 若不存在,则将当前元素的值和下标存入哈希表,继续遍历。
这种方法通过空间换时间,将查找补数的时间复杂度从 O(n) 降为 O(1)。
复杂度分析
- 时间复杂度:O(n),仅需遍历数组一次。
- 空间复杂度:O(n),最坏情况下需要存储所有元素到哈希表中。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h>
// 使用结果数组替代全局变量,提高可维护性 void find_two(int arr[], int target, int len, int *result) { result[0] = -1; // 初始化为-1表示未找到 result[1] = -1; int found = 0; // 标记是否找到目标数对
for (int i = 0; i < len && !found; i++) { // 优化:若当前元素已大于target,无需继续(因数组元素非负) if (arr[i] > target) continue; for (int j = i + 1; j < len && !found; j++) { // 优化:若当前元素已大于target,无需继续 if (arr[j] > target) continue; // 找到和为target的两个数 if (arr[i] + arr[j] == target) { result[0] = i; result[1] = j; found = 1; // 标记已找到,触发循环终止 break; // 退出内层循环 } } } }
int main(void) { int num[100] = { 0 }; srand(time(NULL)); int len = rand() % 100 + 1; // 数组长度至少为1(避免无效遍历) for (int i = 0; i < len; i++) { num[i] = rand() % 100; // 元素范围0-99(非负) } int target = rand() % 100; // 目标值范围0-99 int result[2]; // 存储结果下标
find_two(num, target, len, result); // 输出数组元素(仅输出有效部分) printf("数组元素(前%d个):\n", len); for (int i = 0; i < len; i++) { if(i%15==0) printf("\n"); printf("%d\t", num[i]); } printf("\n目标和为%d\n", target); if (result[0] == -1 || result[1] == -1) { printf("没找到两个数的和等于%d\n", target); } else { printf("找到两个数,下标分别为%d和%d,对应的值为%d和%d,和为%d\n", result[0], result[1], num[result[0]], num[result[1]], target); } return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h>
void find_two(int arr[], int target, int len, int *result) { if (len < 2) { // 数组长度不足2,直接返回-1,-1 result[0] = -1; result[1] = -1; return; } int hash_map[100]; // 哈希表记录值对应的下标,初始化为-1(未出现) for (int i = 0; i < 100; i++) { hash_map[i] = -1; } for (int i = 0; i < len; i++) { int complement = target - arr[i]; // 检查补数是否在有效范围(0-99)且已出现过 if (complement >= 0 && complement < 100 && hash_map[complement] != -1) { result[0] = hash_map[complement]; // 补数的下标 result[1] = i; // 当前元素的下标 return; } // 记录当前元素的下标(覆盖之前的,确保找到最近的或任意一个解) if (arr[i] >= 0 && arr[i] < 100) { // 确保元素在哈希表范围内 hash_map[arr[i]] = i; } } // 未找到符合条件的数对 result[0] = -1; result[1] = -1; }
int main(void) { int num[100] = { 0 }; srand(time(NULL)); int len = rand() % 100 + 1; // 数组长度至少为1(避免len=0时死循环) for (int i = 0; i < len; i++) { num[i] = rand() % 100; // 元素范围0-99 } int target = rand() % 100; // 目标值范围0-99 int result[2] = { -1, -1 };
find_two(num, target, len, result); // 输出数组元素(仅输出有效部分) printf("数组元素(前%d个):\n", len); for (int i = 0; i < len; i++) { printf("%d\t", num[i]); } printf("\n目标和为%d\n", target); if (result[0] == -1 || result[1] == -1) { printf("没找到两个数的和等于%d\n", target); } else { printf("找到两个数,下标分别为%d和%d,对应的值为%d和%d,和为%d\n", result[0], result[1], num[result[0]], num[result[1]], target); } return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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[j] == target - nums[i]) { int* result = malloc(sizeof(int) * 2); result[0] = i; result[1] = j; *returnSize = 2; return result; } } } // Return an empty array if no solution is found *returnSize = 0; return malloc(sizeof(int) * 0); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| #include <stdlib.h> #include <stdbool.h>
// 定义哈希表节点结构体(键:元素值,值:下标) typedef struct HashNode { int key; // 数组元素的值 int value; // 元素的下标 struct HashNode* next; // 链表指针(处理哈希冲突) } HashNode;
// 定义哈希表结构体(桶数组+大小) typedef struct { HashNode** buckets; // 桶数组(每个元素是链表头) int size; // 桶的数量(哈希表大小) } HashTable;
// 哈希函数:将key映射到桶的索引(处理负数) static int hashFunction(HashTable* table, int key) { int hash = key % table->size; return hash < 0 ? hash + table->size : hash; // 确保索引非负 }
// 创建哈希表(初始化桶数组) HashTable* hashTableCreate(int size) { HashTable* table = (HashTable*)malloc(sizeof(HashTable)); table->size = size; table->buckets = (HashNode**)calloc(size, sizeof(HashNode*)); // 初始化为NULL return table; }
// 在哈希表中查找key对应的value(不存在返回-1) static int hashFind(HashTable* table, int key) { int index = hashFunction(table, key); HashNode* current = table->buckets[index]; while (current != NULL) { if (current->key == key) { return current->value; // 找到键,返回下标 } current = current->next; } return -1; // 未找到 }
// 向哈希表中插入key-value对(头插法) static void hashInsert(HashTable* table, int key, int value) { int index = hashFunction(table, key); // 创建新节点并插入链表头部 HashNode* newNode = (HashNode*)malloc(sizeof(HashNode)); newNode->key = key; newNode->value = value; newNode->next = table->buckets[index]; // 头插法覆盖旧值(本题中无需担心) table->buckets[index] = newNode; }
// 释放哈希表所有内存(避免泄漏) static void hashTableFree(HashTable* table) { for (int i = 0; i < table->size; i++) { HashNode* current = table->buckets[i]; while (current != NULL) { HashNode* temp = current; current = current->next; free(temp); // 释放链表节点 } } free(table->buckets); // 释放桶数组 free(table); // 释放哈希表结构体 }
// 两数之和主函数(C语言实现) int* twoSum(int* nums, int numsSize, int target, int* returnSize) { *returnSize = 0; // 初始化返回数组长度为0 if (numsSize < 2) { // 数组长度不足2,直接返回NULL return NULL; }
// 创建哈希表(选择较大的质数10007减少冲突) HashTable* hashTable = hashTableCreate(10007);
for (int i = 0; i < numsSize; i++) { int complement = target - nums[i]; // 计算补数:target - 当前元素值
// 查找补数是否存在于哈希表中 int complementIndex = hashFind(hashTable, complement); if (complementIndex != -1) { // 找到符合条件的数对,分配结果数组并返回 int* result = (int*)malloc(2 * sizeof(int)); result[0] = complementIndex; result[1] = i; *returnSize = 2;
// 释放哈希表内存 hashTableFree(hashTable); return result; }
// 未找到补数,将当前元素插入哈希表 hashInsert(hashTable, nums[i], i); }
// 遍历结束未找到符合条件的数对,释放哈希表内存 hashTableFree(hashTable); return NULL; // 返回NULL表示未找到(调用者需检查returnSize) }
|
本文案例是通过随机生成数组内容来检查函数,实际刷题时写出函数即可,不需要全部写出。