求一个有序数组中两个元素值相加为k的数组,返回这两个元素的下标。

本文介绍两种在有序数组中查找两数之和为特定值k的方法。方法一使用双重循环,时间复杂度O(n^2)。方法二采用双指针策略,从两端向中间搜索,时间复杂度优化至O(n)。提供了Java和C语言的实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述:求一个有序数组(假设升序)中两个元素值相加为k的数组,返回这两个元素的下标。没找到返回 -1。

Java实现:查看完整测试代码

方法1:设置两个循环遍历数组,依次相加比较。时间复杂度O(n^2),空间复杂度O(n)。

public static int[] sumK(int[] a,int k){
        int[] index = {-1,-1};
        int i = 0;
        int j = 0;
        for(i = 0;i < a.length - 1;i++){
            for(j = i + 1;j < a.length;j++ ){
                if(a[i] + a[j] == k){
                    index[0] = i;
                    index[1] = j;
                }
            }
        }
        return index;
    }

方法2:设置两个指针 x,y,依次指向数组第一个元素和最后一个元素,两者相加结果与 k 进行比较,若

  • 等于 k ,返回此时元素下标
  • 小于 k,x++
  • 大于 k,y--

若 x == y 时 x + y != k,则说明数组中不存在两个值相加为 k。返回 -1,-1。时间复杂度O(n),空间复杂度O(n)

public static int[] sumK2(int[] a,int k){
        int x = 0;
        int y = a.length - 1;
        int[] result = {-1,-1};
        while(x < y){
            if(a[x] + a[y] == k){
                result[0] = x;
                result[1] = y;
                return result;
            }else if(a[x] + a[y] > k){
                y--;
            }else{
                x++;
            }
        }
        return result;
    }

C语言实现:查看完整测试代码  时间复杂度O(n),空间复杂度O(1)。

由于空间复杂度为O(1),那么就不能定义新的数组存储下标,定义结构体。

typedef struct index{
	int x;
	int y;
}A;
A arrIndex;
void sumK(int a[], int len, int k){
	int left = 0;
	int right = len - 1;
	while (left < right){
		if (a[left] + a[right] < k){
			left++;
		}
		else if (a[left] + a[right] > k){
			right--;
		}
		else{
			arrIndex.x = left;
			arrIndex.y = right;
			break;
		}
	}
}

 

<think>嗯,我现在得帮用户解决这个问题,用C++实现二分查找。用户给的条件是有序升序数组,目标target,找到返回下标,否则返回-1。首先,我得回忆一下二分查找的基本原理。 首先,二分查找的前提是数组有序,这里已经满足升序,所以没问题。那思路应该是不断将搜索区间分成两半,比较中间元素和目标的大小,然后调整左右边界。 那具体步骤呢?初始化左右指针left和right,left开始是0,right是n-1。然后循环条件应该是left <= right,因为当left超过right时,说明没找到。每次循环中计算中间位置mid,可以用(left + right)/2,不过为了防止溢出,可能应该用left + (right - left)/2更好。 然后比较nums[mid]和target。如果等于,直接返回mid。如果nums[mid] < target,说明目标在右半边,所以left = mid + 1。否则,right = mid - 1。如果循环结束还没找到,返回-1。 需要注意边界条件。比如当数组只有一个元素时,或者当目标正好在中间的情况。还有,当left和right非常大的时候,相加可能导致溢出,所以计算mid的时候需要注意方法。 用户可能需要具体的代码示例。所以接下来得写出这个函数的框架。函数名应该是search,参数是vector<int>& nums,int target。返回int。 然后处理特殊情况,比如数组为空,直接返回-1。不过题目说是n个元素,可能不需要处理空的情况? 然后初始化left=0,right=nums.size()-1。循环条件是while(left <= right)。计算mid。比较nums[mid]和target,调整左右边界。如果找到,返回mid。循环结束后返回-1。 测试一下例子,比如nums = [-1,0,3,5,9,12], target=9,应该返回4。另一个例子,target=2,返回-1。这样应该没问题。 那代码的大致结构应该是这样的: int search(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] == target) { return mid; } else if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return -1; } 这样应该符合要。要确认是否处理了所有可能的边界情况,比如数组长度为0的情况。不过题目里说是n个元素,可能假设n>=1?或者代码是否需要处理nums为空的情况? 比如,如果nums.size()是0,那么right初始化为-1,循环不会执行,直接返回-1,这也是正确的。所以没问题。 总结步骤的话,应该是先初始化左右指针,循环直到left超过right,每次计算中间点,比较后调整左右,直到找到或者结束。 可能用户还需要了解时间复杂度和空间复杂度。时间复杂度是O(log n),因为每次减半。空间是O(1)。 有没有可能哪里出错?比如,mid的计算是否正确,是否有可能死循环?比如当left和right相邻的时候,mid会取到left,这时候如果调整是否正确。比如当left=3,right=4时,mid=3,如果target在右边,left变为4,然后mid=4,比较后退出,没问题。 所以代码是正确的。然后用户可能需要解释这个算法的每一步,或者需要注意的地方。例如,循环条件是<=,而不是<,否则会漏掉当left等于right的情况。比如当数组中只有一个元素时,如果没有等号,就不会进入循环,直接返回-1,即使存在目标。 所以这个条件很重要。另外,mid的计算方式避免溢出,这点需要说明。 所以总结起来,步骤是: 1. 初始化左右指针。 2. 循环直到left > right。 3. 计算中间索引。 4. 比较中间和目标,调整左右。 5. 找到返回,否则返回-1。 这样应该可以解决问题。用户的需应该能得到满足。</think>好的,我将分步骤介绍如何用C++实现二分查找算法解决该问题: ### 一、算法原理 二分查找的核心思想是通过不断缩小搜索区间快速定位目标。 适用条件: 1. 数组必须是有序的(本题已给出升序数组) 2. 元素可随机访问 时间复杂度:$O(\log n)$ 空间复杂度:$O(1)$ ### 二、实现步骤 1. **初始化指针** ```cpp int left = 0; int right = nums.size() - 1; ``` 2. **循环查找** 循环条件:`while (left <= right)` ✅ 必须包含等于的情况,否则会漏查最后一个元素 3. **计算中间位置** ```cpp int mid = left + (right - left)/2; ``` ✅ 比直接相加更安全,避免整数溢出 4. **比较中间元素** - `nums[mid] == target`:找到目标,返回下标 - `nums[mid] < target`:目标在右侧,缩小左边界 `left = mid + 1` - `nums[mid] > target`:目标在左侧,缩小右边界 `right = mid - 1` 5. **未找到返回-1** ### 三、完整代码实现 ```cpp #include <vector> using namespace std; int search(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; // 右边界初始化为最后一个元素的索引 while (left <= right) { // 关键:必须包含等号 int mid = left + (right - left) / 2; // 防止整数溢出 if (nums[mid] == target) { return mid; // 找到目标 } else if (nums[mid] < target) { left = mid + 1; // 调整左边界 } else { right = mid - 1; // 调整右边界 } } return -1; // 未找到 } ``` ### 四、边界测试案例 1. **目标在中间** `nums = [-1,0,3,5,9,12], target = 9` ✅ 返回4 2. **目标不存在** `nums = [2,4,6,8], target = 5` ✅ 返回-1 3. **空数组处理** `nums = [], target = 1` ✅ 返回-1(函数自动处理) 4. **单元素数组** `nums = [5], target = 5` ✅ 返回0 ### 五、常见错误分析 1. **循环条件写成`left < right`** 会漏查区间缩小到单个元素的情况 2. **未更新边界导致死循环** 例如:错误地写成`right = mid`或`left = mid` 3. **整数溢出问题** 错误写法:`mid = (left + right)/2` 当`left + right > INT_MAX`时会溢出 ### 六、算法优化 若数组中有重复元素,找到第一个或最后一个出现位置时,需调整判断条件。但本题只需找到任意一个匹配位置即可满足要。 --- 这样实现可以高效地完成有序数组的查找任务,确保在所有边界情况下都能正确工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值