【欧拉计划】Q14:OnlineJ两数之和

OnlineJ两数之和


  • 二分

  • 双指针

  • 哈希

  • 原题链接:http://oj.haizeix.com/problem/599

题目描述

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

输入

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

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

输出

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

6 15
1 5 6 7 10 26
1 4

1.暴力法:

#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)

#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),是一种效率比较高的解法。

#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.哈希表:

#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

tips:文章部分内容参考算法刷题课程,题解图示内容及代码根据老师课程、以及自己对知识的理解,进行二次整理和部分补充,仅供学习参考使用,不可商业化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值