C++ 实现:有序数组中二分查找左右边界(首次/最后出现位置)

标题

C++ 实现:有序数组中二分查找左右边界(首次/最后出现位置)
左边界:东方博宜OJ ——1894 - 二分查找左侧边界
右边界:东方博宜OJ ——1895 - 二分查找右侧边界


一、前言

在算法竞赛与日常刷题中,二分查找是非常经典且高效的查找算法,时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn)。本文通过两道具体题目:

  1. 在有序非递减数组中查找给定 x x x 第一次出现的位置(左侧边界)。
  2. 在有序非递减数组中查找给定 x x x 最后一次出现的位置(右侧边界)。

来讲解左右边界的二分查找技巧。

二、题目一:查找左侧边界(首次出现)

给定一个长度为 n n n 的有序非递减数组(可重复),查找值为 x x x 的元素第一次出现的位置;若不存在,则输出 -1。

输入/输出格式、示例、解题思路 同上文,不再赘述。可见 左侧边界实现

左侧边界代码示例 {#左侧边界代码}

// 查找左侧边界(第一个 >= x)
int bin_serach_left(int a[], int left, int right, int x){
	while(left <= right){
		int mid = left + (right - left) / 2;
		if(a[mid] >= x) right = mid - 1;
		else left = mid + 1;
	}
	return left;
}

三、题目二:查找右侧边界(最后出现)

在同样的有序非递减数组中,查找值为 x x x 的元素最后一次出现的位置;若不存在,则输出 -1。

  • 输入格式 与题目一一致。
  • 示例
    输入:
    6
    1 2 2 2 3 3
    3
    3 2 5
    输出:
    6 4 -1
    
    解释:3 最后一次在位置 6,2 最后一次在位置 4,5 不存在。

三.1 解题思路

  1. 明确目标:定位目标值 x x x 在数组中最后一次出现的位置(右侧边界)。
  2. 二分模板调整:同样维护 l=0, r=n-1
  3. 边界更新:若 a[mid] <= x,则需要往右搜寻,l = mid + 1;否则(a[mid] > x),缩右界,r = mid - 1
  4. 后处理:循环结束后,r 指向最后一个满足 a[r] <= x 的元素位置。需判断 r >= 0 && a[r] == x,否则返回 -1。

三.2 右侧边界代码示例 {#右侧边界代码}

// 查找右侧边界(最后一个 <= x)
int bin_serach_right(int a[], int left, int right, int x) {
	while(left <= right){
		int mid = left + (right - left) / 2;
		if(a[mid] <= x) left = mid + 1;
		else right = mid - 1; 
	}
	return right;
}

三.3 代码解析

  • a[mid] <= x 时,说明目标的最后一处可能在 mid 或右侧,故令 l = mid + 1
  • 否则(a[mid] > x),令 r = mid - 1
  • 退出循环后,r 指向最后一个小于等于 x x x 的索引,需验证其是否等于 x x x

四、复杂度分析

  • 时间复杂度:单次二分查找 O ( log ⁡ n ) O(\log n) O(logn)
  • 空间复杂度 O ( n ) O(n) O(n)

五、总结与练习

  • 左右边界差异:左侧收缩右界 (r = mid - 1),右侧收缩左界 (l = mid + 1)。
  • 后处理:左侧使用 l 验证,右侧使用 r 验证。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值