给你一个下标从 1 开始的整数数组 numbers ,该数组已按 非递减顺序排列 ,请你从数组中找出满足相加之和等于目标数 target 的两个数。如果设这两个数分别是 numbers[index1] 和 numbers[index2] ,则 1 <= index1 < index2 <= numbers.length 。以长度为 2 的整数数组 [index1, index2] 的形式返回这两个整数的下标 index1 和 index2。你可以假设每个输入 只对应唯一的答案 ,而且你 不可以 重复使用相同的元素。你所设计的解决方案必须只使用常量级的额外空间。示例 1:输入:numbers = [2,7,11,15], target = 9
输出:[1,2]
解释:2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。
示例 2:输入:numbers = [2,3,4], target = 6
输出:[1,3]
解释:2 与 4 之和等于目标数 6 。因此 index1 = 1, index2 = 3 。返回 [1, 3] 。
示例 3:输入:numbers = [-1,0], target = -1
输出:[1,2]
解释:-1 与 0 之和等于目标数 -1 。因此 index1 = 1, index2 = 2 。返回 [1, 2] 。提示:2 <= numbers.length <= 3 * 104
-1000 <= numbers[i] <= 1000
numbers 按 非递减顺序 排列
-1000 <= target <= 1000
仅存在一个有效答案
167. 两数之和 II - 输入有序数组 - 力扣(LeetCode)
算法设计
拿到这道题,想到了很多方法?二分法?哈希表?但是他们都有自己的弊端,二分法必须套在二层循环里,哈希表必须使用桶式结构来保存哈希标签重合的Index,且代码逻辑比较复杂。
想要以最快速度解决这道题,真正的入手点在数学。首先需要意识到一个点,我们是能通过不断求取局部最优解来获得全局最优解的,为什么这么说?
求解规则:
1. 定义头指针尾指针,并分别初始化在表头表尾。
2. 如果头尾指针指向元素和等于target,则返回标签
3. 如果头尾指针指向元素和大于target,尾指针--;
4. 如果头尾指针指向元素和小于target,尾指针++;
5. 跳到2直到head和tail重合或tail<head;
为什么能如此自信的通过一步步贪心求得最终解?难道这个过程中不会遗漏掉最终解吗?
我们正常推理不太好思考,不如用反证法:
假设上述求解规则中,两个最终标签解被称为小解和大解。会出现head大于小解或tail小于大解的情况:
1. head大于小解,tail大于等于大解 =》则必然有一刻head等于小解,tail大于大解 =》此时头尾指向和必然大于target =》则tail左移即可得到结果,head没有机会右移 =》不存在head大于小解tail大于等于大解,与题设矛盾
2. head小于等于小解,tail小于大解 =》某一刻有tail等于大解,head小于小解 =》头尾指向和小于target =》此时head右移即可求解,tail没有机会左移 =》不存在head小于等于小解,tail小于大解,与题设矛盾
命题得证
代码实现
int* twoSum(int* numbers, int numbersSize, int target, int* returnSize) {int head=0,tail=numbersSize-1;int * ret=(int*) malloc(sizeof(int)*2);while(head<tail){if(*(numbers+head)+*(numbers+tail)==target){ret[0]=head+1;ret[1]=tail+1;*(returnSize)=2;return ret;}if(*(numbers+head)+*(numbers+tail)>target){tail--;}if(*(numbers+head)+*(numbers+tail)<target)head++;}*(returnSize)=0;return ret;
}