一种二分查找变形

博客介绍了原始二分查找,当数组中有重复值时,如 1 3 5 5 5 5 10 11 20,需对原始二分查找变形以找到重复值的第一个和最后一个位置,并给出了非递归版代码。

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

原始的二分查找非常简单,只要找到值返回即可;但是如果某值在数组中是重复的,比如 1 3 5 5 5 5 10 11 20这样,要求找到第一个5和最后一个5,就需要对原始的二分查找进行一点变形。

代码如下:

非递归版

int GetFirst_NonRecurse(int a[], int N, int k, int start, int end)
{

	int mid;
	while (start <= end)
	{
		mid = start + (end-start)/2;
		
		if (a[mid] == k)
		{
			if (mid>0&&a[mid-1]!=k || mid==0)
				return mid;
			else
				end = mid - 1;
		}
		else if (a[mid] > k)
			end = mid - 1;
		else
			start = mid + 1;
	}
	return  -1;
}

int GetLast_NonRecurse(int a[], int N, int k, int start, int end)
{
	int mid;
	while (start <= end)
	{
		mid = start + (end - start)/2;
		if (a[mid] == k)
		{ 
			if (mid < N-1 && a[mid+1] != k || mid == N-1)
				return mid;
			else
				start = mid + 1;
		}
		else if (a[mid] > k)
		{
			start = mid + 1 ;
		}
		else
		{
			end = mid - 1;
		}
	}
	return -1;
}
递归版

int GetFirst(int a[], int N, int k, int start, int end)
{
	if (start > end)
		return -1;

	int mid = start + (end-start)/2;

	if (a[mid] == k)
	{
		if (mid>0&&a[mid-1]!=k || mid==0)
			return mid;
		else
			end = mid - 1;
	}
	else if (a[mid] > k)
		end = mid - 1;
	else
		start = mid + 1;

	return GetFirst(a,N,k,start,end);

}
int GetLast(int a[], int N, int k, int start, int end)
{
	if (start > end)
		return -1;

	int mid = start + (end - start)/2;

	if (a[mid] == k)
	{
		if (mid<N-1&&a[mid+1]!=k || mid==N-1)
			return mid;
		else
			start = mid + 1;
	}
	else if (a[mid] > k)
	{
		end = mid - 1;
	}
	else
	{
		start = mid + 1;
	}
	return GetLast(a,N,k,start,end);
}

### 关于二分查找算法的数学原理与实现 #### 数学原理 二分查找的核心在于通过不断将区间划分为两部分来减少搜索空间,从而提高效率。其基本假设是输入数据已经按照某种顺序排列(通常是升序)。每次迭代中,算法会计算当前区间的中间位置 `mid` 并将其对应的值与目标值进行比较。如果目标值小于中间值,则继续在左半部分寻找;反之则在右半部分寻找[^2]。 这种划分方式使得每一步都能排除掉一半的数据量,因此时间复杂度为 \(O(\log n)\),其中 \(n\) 是数组长度。这是基于对数性质的结果——连续多次除以 2 后达到单个元素所需的次数正好是对数级别[^3]。 另外需要注意的是,在某些情况下可能需要调整边界条件以适应不同的需求,比如找第一个大于等于某个数值的位置或是最后一个小于该数值的地方等变种问题[^1]。 #### C++ 实现示例 以下是标准版本以及两种常见变形的 C++ 实现: ##### 基础版本 - 查找特定值是否存在 ```cpp int binarySearchBasic(const std::vector<int>& nums, int target) { int left = 0; int right = static_cast<int>(nums.size()) - 1; while (left <= right) { int mid = left + (right - left) / 2; // 防止溢出 if (nums[mid] == target) return mid; else if (nums[mid] > target) right = mid - 1; else left = mid + 1; } return -1; // 若未找到返回-1 } ``` ##### 变形一 - 寻找 >=x 的最小索引 当存在多个相同的目标值时,此版本可以定位到最左侧的那个。 ```cpp int lowerBound(const std::vector<int>& nums, int target) { int left = 0; int right = static_cast<int>(nums.size()); while (left < right) { // 注意这里是 "<" int mid = left + (right - left) / 2; if (nums[mid] >= target) right = mid; else left = mid + 1; } if (left != nums.size() && nums[left] == target) return left; return -1; // 或者其他表示不存在的方式 } ``` ##### 变形二 - 寻找 <=x 的最大索引 与此相对应地,这个版本适用于获取不超过指定值的最大项。 ```cpp int upperBound(const std::vector<int>& nums, int target) { int left = 0; int right = static_cast<int>(nums.size()); while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] <= target) left = mid + 1; else right = mid; } if (right > 0 && nums[right - 1] == target) return right - 1; return -1; // 类似处理找不到的情况 } ``` 以上三个例子分别展示了如何利用二分查找解决不同类型的查询任务,并且都遵循了高效性和准确性原则[^3]。 ### 性能分析 对于长度为 \(N\) 的已排序列表来说,传统线性扫描的时间消耗将是 \(O(N)\),而采用二分法则只需执行大约 \(\log_2{N}\) 步操作即可完成整个过程。这极大地提升了大规模数据集上的检索速度[^2]。 然而值得注意的一点是,尽管如此快速有效,但前提是原始集合必须先经过预处理成为有序状态。如果没有事先做好这一点的话,那么额外花费在这上面的成本可能会抵消掉后续应用所带来的好处[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值