首页 > 其他分享 >整数二分查找

整数二分查找

时间:2022-11-23 23:26:10浏览次数:49  
标签:二分 right int mid 整数 bound 查找 left

总的来说

使用二分的条件:具有单调性

注意:二分的思想看似简单,但其实很容易出错

整数二分模板

以单调递增序列的二分为例

给出两个模板:

在单调递增序列中找到xx的后继(大于x的第一个数),也就是大于等于x的第一个数的位置
在单调递增序列中找到xx的前驱(小于x的第一个数),也就是小于等于x的第一个数的位置

// 找到x或x的后继
int bin_search(int *a, int n, int x)	// a 的编码为 [0, n), 左开右闭区间,下面同理
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mid = left + (right - left) / 2;	// 取左中位值
		if (a[mid] >= x) right = mid;
		else left = mid + 1;		// 防止死循环
	}					// 循环结束时 l = r
	return left;
}

// 找到x或x的前驱
int bin_search(int *a, int n, int x)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mid = left + (right - left + 1) / 2;	// 取右中位数
		if (a[mid] <= x) left = mid;
		else right = mid - 1;
	}
	return left;
}

对left, right 的处理

值得注意,第一个模板中循环的结尾left = mid + 1,其原因是整数二分存在取整的问题
例如当 left 等于 2, right 等于 3 时 mid 等于 2,若取 left = mid ,一次循环结束 left 和 right 的值都没有变,程序陷入死循环,解决方法是像模板一样 left = mid + 1

对mid的处理

整数二分的关键是对中间值mid的处理

以第一个模板为例,mid取值有以下几种方法

实现方法 使用条件 可能出现的问题
mid = (left + right) / 2 left >= 0, right >= 0,
left + right 未溢出
(1)负数情况下会出现向0取整问题
(2)left + right 可能溢出
mid = left + right >> 1 left + right 未溢出 left + right 可能溢出
mid = left + (right - left) >> 1 left - right 未溢出 left 和 right 一正一负时 right - left 可能溢出

值得注意的是,上表中第一个实现方式中向0取整问题是指当 left + right 大于 0 使取的是左中位数,而当 left + right 小于 0 时取的是右中位数,正负区间没有统一取左中位数或右中位数,而后两种方法就没有这个问题,当然在对数组进行二分时,三种实现方法都是可行的

如果是对数组进行二分,因为其下标都大于等于0,个人觉得用上表中的第三个更好

单调递减序列二分

了解上面这些之后,我们也能写出单调递减序列的二分模板

//   找到最后一个大于等于x的数的位置
int bin_search(int *a, int n, int x)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mid = left + (right - left + 1) / 2;
		if (a[mid] >= x) left = mid;
		else right = mid - 1;
	}
	return left;
}

// 找到第一个小于小于x的数的位置
int bin_search(int *a, int n, int x)
{
	int left = 0, right = n - 1;
	while (left < right)
	{
		int mid = left + (right - left) / 2;
		if (a[mid] <= x) right = mid;
		else left = mid + 1;
	}
	return left;
}

一个方便(?)记忆的方法

我们观察两个模板的话,会发现:

如果是 left = mid 的话, mid 就要取右中位值
如果是 right = mid 的话,mid 就要取左中位值

写二分的时候,可以先把 mid 写成左中位值,再写对应 left 和 right 的操作,最后再看前面的 mid 是否要改成右中位值

lower_bound() 和 upper_bound()

STL 提供了lower_bound(),upper_bound()这两个函数

lower_bound() 返回大于等于所查找数的最小位置
upper_bound() 返回大于所查找数的最小位置

利用 lower_bound()lower_bound() 我们可以实现如下的操作:

  • 查找第一个大于等于 x 的元素:pos = lower_bound(a, a + n, x) - a
  • 查找第一个大于 x 的元素:pos = upper_bound(a, a + n, x) - a
  • 查找第一个等于 x 的元素:pos = lower_bound(a, a + n, x) - a 并且 a[pos] == x
  • 查找最后一个小于等于 x 的元素:pos = upper_bound(a, a + n, x) - a - 1
  • 查找最后一个等于 x 的元素:pos = upper_bound(a, a + n, x) - a - 1 并且 a[pos] == x
  • 查找第一个小于 x 的元素:pos = lower_bound(a, a + n, x) - a - 1
  • 计算单调序列中 x 的个数:pos = upper_bound() - lower_bound()

注意:对长度为 n 的一段序列查找 x ,如果 x 大于序列中的最后一个数将返回 n
将上述二分模板中r = n - 1 改为 r = n 的时,会跟上面两个函数一样返回 n

整数二分建模

基本思路

while (left < right)
{
	int ans;
	int mid = left + (right - left) / 2;
	if (check(mid))
	{
		ans = mid;
		/* ...*/
	}
	else
		/* ... */
}

本文为《算法竞赛》的笔记,部分内容直接摘自此书

标签:二分,right,int,mid,整数,bound,查找,left
From: https://www.cnblogs.com/Panmaru/p/16917295.html

相关文章

  • LeetCode刷题笔记—13.罗马数字转整数
    在此不再做题目描述。(该题链接:13.罗马数字转整数-力扣(LeetCode))在观察罗马数字时,我们可以发现计算罗马数字的技巧:可以设定一个初始值ans=0,然后对罗马数字从左到......
  • SAP-SD-ABAP-VMOD 查找和应用SD模块用户出口(user exit) 好方法
    针对SD模块,有一个专门管理user-exit的开发包 VMOD,只要用tcode:se80查看它,会发现绝大部分的SD要相关的user-exit都能在这找到。......
  • android 隐藏声音文件-不让音乐播放器查找到
    有时候某些程序自带的声音文件,不想被音乐播放找到,如何实现呢?很简单,在需要隐藏的目录下面添加 .nomedia 文件。内容为空即可.可以用vi来添加这个空文件.在超级端下......
  • 一步一步写算法(之字符串查找 中篇)
       昨天我们编写了简单的字符查找函数。虽然比较简单,但是也算能用。然而,经过我们仔细分析研究一下,这么一个简单的函数还是有改进的空间的。在什么地方改进呢?大家可以慢......
  • 一步一步写算法(之字符串查找 上篇)
       字符串运算是我们开发软件的基本功,其中比较常用的功能有字符串长度的求解、字符串的比较、字符串的拷贝、字符串的upper等等。另外一个经常使用但是却被我们忽视的功......
  • 力扣34(java)-在排序数组中查找元素的第一个和最后一个位置(中等)
    题目:给你一个按照非递减顺序排列的整数数组nums,和一个目标值target。请你找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值target,返回 [-1,-1]......
  • 深度优先遍历查找
    一.题目二.思路深度优先遍历+回溯法三.代码#include<stdio.h>#include<malloc.h>intn,m;int*path;intcount[2];//0代表-,1代表ointkey;//第k个intcount_=......
  • 600. 不含连续1的非负整数(数位dp或数学)
    (hard还是有点强度的)给定一个正整数 n ,请你统计在 [0,n] 范围的非负整数中,有多少个整数的二进制表示中不存在 连续的1 。 示例1:输入:n=5输出:5解释:......
  • C语言二进制、八进制、十六进制整数书写和输出
    文章目录​​一、二进制、八进制、十六进制整数的书写​​​​1、二进制​​​​2、八进制​​​​3、十六进制​​​​4、需要注意的坑​​​​二、二进制、八进制、十六进......
  • C语言整数的输出
    文章目录​​一、整数的基本概念​​​​二、整数的书写​​​​1、二进制​​​​2、八进制​​​​3、十六进制​​​​4、需要注意的坑​​​​三、整数的输出​​​​四......