二分查找二(二分查找的经典变形问题)

一、查找第一个值等于给定值的元素

主要代码:

int bsearch1(int* a, int n, int value)
{
	int low = 0;
	int high = n - 1;
	while (low <= high)
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] < value)
		{
			low = mid + 1;
		}
		else if (a[mid] > value)
		{
			high = mid - 1;
		}
		else
		{
			// 如果该给定值等于数组的第一个元素或者
			// 当前数组元素的前一个元素的下标不等于value则返回mid
			if ((mid == 0) || (a[mid - 1] != value)) return mid;
			// 否则让high往前移
			else high = mid - 1;
		}
	}
	return -1;
}

这段代码的处理重点就是 a[mid] = value 时的情况,如果 mid 等于 0,即这个元素已经是数组的第一个元素,那毫无疑问,这个元素肯定就是我们要找的;如果 mid 不等于 0,但 a[mid] 的前一个元素 a[mid-1] 不等于 value,那也说明 a[mid] 就是我们要找的第一个值等于给定值的元素。

如果经过检查之后发现 a[mid] 前面的一个元素 a[mid-1] 也等于 value,那说明此时的 a[mid] 肯定不是我们要查找的第一个值等于给定值的元素。那我们就更新 high = mid - 1,因为要找的元素肯定出现在[low, mid-1]之间。

二、查找最后一个值等于给定值的元素

主要代码:

int bsearch2(int* a, int n, int value)
{
	int low = 0;
	int high = n - 1;
	while (low <= high) 
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] < value)
		{
			low = mid + 1;
		}
		else if (a[mid] > value)
		{
			high = mid - 1;
		}
		else
		{
			// 如果给定值等于数组的最后一个元素或者
			// 当前元素的后一个元素的下标不等于value,则返回mid
			if ((mid == n - 1) || (a[mid + 1] != value)) return mid;
			// 否则让low往后移
			else low = mid + 1;
		}
	}
	return -1;
}

如果 a[mid]这个元素已经是数组中的最后一个元素了,那它肯定是我们要找的;如果 a[mid]的后一个元素 a[mid+1]不等于 value,那也说明 a[mid]就是我们要找的最后一个值等于给定值的元素。

如果我们经过检查之后,发现 a[mid]后面的一个元素 a[mid+1]也等于 value,那说明当前的这个 a[mid]并不是最后一个值等于给定值的元素。我们就更新 low=mid+1,因为要找的元素肯定出现在[mid+1, high]之间。

三、查找第一个大于等于给定值的元素

主要代码:

int bsearch3(int* a, int n, int value)
{
	int low = 0; 
	int high = n - 1;
	int flag = 0;
	while (low <= high)  // 按照类型二的方法先找出给定值在数组中的最后一个元素
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] < value) low = mid + 1;
		else if (a[mid] > value) high = mid - 1;
		else
		{
			if (mid == n - 1) 
			{
				return mid;  // 如果给定值等于数组的最后一个元素,则直接返回mid
			}
			else if 
			{
				// 由于这里查找的是给定值在数组中的最后一个元素,
				// 所以如果我们想要得到第一个大于给定值的元素的下标,自然而然地应该返回mid + 1
				(a[mid + 1] != value) return mid + 1;
			}
			else low = mid + 1;
		}
	}
	low = 0;  // 重新初始化low和high
	high = n - 1;
	while (low <= high)  // 说明给定值不在数组中
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] >= value)
		{
			if ((mid == 0) || (a[mid - 1] <= value))
			{
				return mid;  
			}
			else high = mid - 1;
		}
		else low = mid + 1;
	}
}

类型三的代码稍微有点长,因为这里多了一个查找的操作,我们在进行查找第一个大于等于给定值的元素时,先判断给定值是否在数组中,并且因为我们要查找的是第一个大于等于给定值的元素,我们在查找的时候还需要额外考虑该给定值在数组中有多个相同值的情况,所以这时候我们就要用类型二的查找方式来进行判断,判断完毕后直接返回该元素的下一个元素的下标。

而如果给定值不在数组中的话就要通过另外的一个循环查找方式来进行了。

查找第一个大于等于给定值的元素操作的几个判断标准:

  1. 如果给定值在数组中,且是数组中的第一个或最后一个元素时,函数还能不能给出正常的返回值
  2. 如果给定值在数组中有多个相同的元素时,能否正确返回自己想要查找的元素的下标

四、查找最后一个小于等于给定值的元素

主要代码:

int bsearch4(int* a, int n, int value)
{
	int low = 0;
	int high = n - 1;
	int flag = false;
	while (low <= high)  // 按照类型一的方法先找出给定值在数组中的第一个元素
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] < value) low = mid + 1;
		else if (a[mid] > value) high = mid - 1;
		else
		{
			if (mid == 0)
			{
				return mid;  // 如果给定值为数组中的第一个元素,则直接返回mid
			}
			if (a[mid - 1] != value)
			{
				// 由于这里查找的是给定值在数组中的第一个元素,
				// 所以如果我们想要得到最后一个小于给定值的元素的下标,自然而然地应该返回mid - 1
				if (a[mid] <= value) return mid - 1;  
			}
			else high = mid - 1;
		}
	}
	low = 0;  // 重新初始化low和high
	high = n - 1;
	while (low <= high)  // 进入这个循环说明给定值不在数组中
	{
		int mid = low + ((high - low) >> 1);
		if (a[mid] <= value)
		{
			if ((mid == n - 1) || (a[mid + 1] > value))
			{
				return mid;  
			}
			else low = mid + 1;
		}
		else high = mid - 1; 
	}
	return -1;
}

类型四的查找操作以及判断标准与类型三的差不多,部分细节已经在代码中注释,这里就不多表述。

值得注意的是,当给定值在数组中时,类型三借助类型二进行判断;类型四借助类型一进行判断。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值