OnlineJ两数之和


题目描述

给定一个从小到大的数组和一个目标数t,在其中找到两个数,使得两数之和与目标数相等,输出两个数在数组中的位置。

输入

第一行输入两个整数 n, t(1 ≤ n ≤ 1000000, 1 ≤ t ≤ 20000000)

接下来一行 n 个数,均小于10,000,000

输出

输出两个用空格隔开的数表示位置(从零开始计数),答案有唯一解

1
2
6 15
1 5 6 7 10 26
1
1 4

1.暴力法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
#include<cstdio>
using namespace std;

int num[1000005];
int n, target;

int main() {
scanf("%d%d", &n, &target);
for (int i = 0; i < n; ++i) {
scanf("%d", &num[i]);
}
for (int i = 0; i < n; ++i) {
for (int j = i + 1; j < n; ++j) {
if (num[i] + num[j] == target) {
cout << i << " " << j << endl;
return 0;
}
}
}
cout << "not fimd" << endl;
return 0;
}

image-20220504165225033

注意:由于数据范围(1≤n≤1000000, 1≤t≤20000000),暴力枚举的时间复杂度为O(n^2)一定会超时,空间复杂度为O(1)

2.二分法:

==思路==:

  1. 使用for循环遍历数组中的每一个数字
  2. 在循环体内部,使用target值减去当前遍历的数字值(结果为配对值),在数组中二分查找该数字
  3. 如果找到配对值则输入答案,否则没有配对值输出nofind

复杂度分析:使用二分查找程序的时间复杂为O(nlogn)、空间复杂度为O(1)

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
#include<iostream>
#include<cstdio>
using namespace std;

int num[1000005];
int n, target;

int main(){
scanf("%d%d", &n, &target);
for(int i = 0; i < n; ++i){
scanf("%d", &num[i]);
}
for(int i = 0; i < n; ++i){
int temp = target - num[i];
int l = i + 1, r = n - 1;
while(l <= r){
int mid = (l + r) / 2;
if(temp == num[mid]){
cout << i << " " << mid << endl;
return 0;
}
if(temp < num[mid]){
r = mid - 1;
}else{
l = mid + 1;
}
}
}
cout << "not fimd" << endl;
return 0;
}

3.双指针法:

==思路==:

  1. 分别初始化两个指针,指向数组的起始和结尾
  2. 将数组头尾元素相加sum值与target值比较:
    如果sum>target(数据偏大),则将右指针向左移动一位
    如果sum<target(数据偏小),则将左指针向右移动一位
    如果sum==target(找到结果),直接输出左右指针
  3. 左右指针错开时,target查找结束(数组遍历完成没有找到结果nofind)

复杂度分析:使用双指针法的时间复杂度为O(n),空间复杂度为O(1),是一种效率比较高的解法。

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
#include<iostream>
#include<cstdio>
using namespace std;

int num[1000005];
int n, target;

int main(){
scanf("%d%d", &n, &target);
for(int i = 0; i < n; ++i){
scanf("%d", &num[i]);
}
int l = 0, r = n - 1;
while(l < r){
int t = num[l] + num[r];
if(t == target){
printf("%d %d\n", l, r);
return 0;
}
if(t > target){
r--;
}else{
l++;
}
}
printf("no find\n");
return 0;
}

4.哈希表:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;

int n, target;
int num[1000005], mark[1000005];

int main() {
scanf("%d%d", &n, &target);
for (int i = 1; i <= n; ++i) {
scanf("%d", &num[i]);
mark[num[i]] = i;
}
for (int i = 1; i <= n; ++i) {
int temp = target - num[i];
if (mark[temp] != 0) {
printf("%d %d\n", i - 1, mark[temp] - 1);
return 0;
}
}
printf("no find!\n");
return 0;
}

注意:时间复杂度为O(n)、空间复杂度为O(n)

3.问题总结:

两数之和问题,四种解法时空复杂分析,

在不限空间的情况下,最快的方法是哈希表法时间复杂度为O(n)

image-20220504183325705