Leetcode167 两数之和 II - 输入有序数组|刷题打卡02

385 阅读3分钟

前言


本来想做一些难点的题目的,可我发现难度太大了,我的考虑还是不够周全,写一篇难度过大的文章太耗费时间和精力,不断地看别人的解读写文章,我有种在抄袭的错觉,随后也不好意思发出来了,那么我还是决定从基础开始,由简单的算法慢慢来吧。

一、题目描述:


给定一个已按照 升序排列  的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target

函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length

你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例 1:

输入:numbers = [2,7,11,15], target = 9

输出:[1,2]

解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2

示例 2:

输入:numbers = [2,3,4], target = 6

输出:[1,3]

示例 3:

输入:numbers = [-1,0], target = -1

输出:[1,2]

提示:

2 <= numbers.length <= 3 * 10^4
-1000 <= numbers[i] <= 1000
numbers 按 递增顺序 排列
-1000 <= target <= 1000 仅存在一个有效答案

来源:力扣(LeetCode)
链接:leetcode-cn.com/problems/tw…

二、思路分析:


看到这道题目,第一反应还是想用暴力破解法,暴力法很基础,只要运用两个for循环遍历找到相加起来等于target的两个元素,再初始化一个长度为2的数组,将i+1和j+1分别给数组的前两位赋值,再返回数组res即可。思路其实很简单,可是代码写起来很容易就超出了时间限制,不得不通过添加break的方式降低时间复杂度,最终成功的代码在如下

其实如果细心一点,就会发现这道题想考察的知识点已经在提示中加黑了,题目强调了两次该numbers序列为升序排列也就是有序序列,所以我们很容易联想到基础的算法,即二分查找法。

什么是二分查找法呢? 二分查找是一种算法,其输入为一个有序的元素列表。如果要查找的元素包含在列表中,二分查找返回器位置,否则返回null。举一个栗子,假设任务是要在字典中找一个单词,该字典包括240000个单词,所以每种查找最多需要多少步?

如果使用简单查找,当目标单词位于词典的末尾时,最多需要240000步。可是使用二分查找时,每次排除一半的单词,直到最后只剩下一个的单词,因此二分法只需要18步,相比简单查找方法,方便得多。也可通过计算得知,对于包含n个元素的列表,用二分法查找最多需要log2(n)步,而简单查找最多需要n步。

二分法在某种程度上,也算是对暴力法的优化,核心就在于优化了时间复杂度。同样的,这道题目也可以用二分法。将数列的最左位设置为l,最右为r,当最左边的数小于最右边的数值时,循环得以运行。同时将m设置成数列的中间数,当最左边即第一位数与中间数的值加起来大于target时,命令最右边的数即末尾数值为m-1位,这也等价于去掉了一半的数。而如果所加值小于target,则最右边的数将等于m+1,往更大的数位后推,直到得到所加值等于target,将得到的m和i放入数组,并且输出。

三、AC 代码:


暴力破解

int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    int i, j, m = 0;

    for (i = 0; i < numbersSize - 1; i++){
        for (j = i + 1; j < numbersSize; j++){
            if (numbers[i] + numbers[j] == target){
                m = 1;
                break;
            }
        }
        if (m)
            break;
    }
    *returnSize = 2;
    int *res = (int*)malloc(sizeof(int) * 2);
    res[0] = i + 1;
    res[1] = j + 1;
    return res;
}

二分查找

int* twoSum(int* numbers, int numbersSize, int target, int* returnSize){
    int i, l, r, m, s = 0;
   
    for (i = 0; i < numbersSize - 1; i++){ 
        l = i + 1, r = numbersSize - 1;
        while (l <= r){
            m = (l + r) / 2;
            if (numbers[m] > target - numbers[i])
                r = m - 1;
            else if (numbers[m] < target - numbers[i])
                l = m + 1;
            else{
                s = 1;
                break;
            }
        }
        if (s)
            break;
    }
    *returnSize = 2;
    int *res = (int*)malloc(sizeof(int) * 2);
    res[0] = i + 1;
    res[1] = m + 1;
    return res;
}

四、总结:


这道题是leetcode上较为简单而且基础的一道题目,其实还有其他的方法,如双指针法,但双指针我并不是很熟悉,所以,如果等我学习到了,会再次更新这篇文章。

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情