前言
本来想做一些难点的题目的,可我发现难度太大了,我的考虑还是不够周全,写一篇难度过大的文章太耗费时间和精力,不断地看别人的解读写文章,我有种在抄袭的错觉,随后也不好意思发出来了,那么我还是决定从基础开始,由简单的算法慢慢来吧。
一、题目描述:
给定一个已按照 升序排列 的整数数组 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 春招闯关活动」, 点击查看 活动详情