首页 > 编程语言 >算法DAY 1 二分法查找数的范围

算法DAY 1 二分法查找数的范围

时间:2024-08-03 18:26:37浏览次数:10  
标签:int 边界值 元素 mid 二分法 查找 数组 DAY

题目

给定一个按照升序排列的长度为n的整数数组,以及q个查询。
对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。
如果数组中不存在该元素,则返回-1 -1。
输入格式
第一行包含整数n和q,表示数组长度和询问个数。
第二行包含n个整数(均在1 ~ 10000范围内),表示完整数组。
接下来q行,每行包含- -个整数k,表示一-个询问元素。
输出格式
共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回-1 -1。

本题可在acwing基础算法课中找到详细的视频解析(y总),本文仅进行简单的归纳。

注意题目内容:给定了一个升序数组。

从做简单的进查询一次考虑,假设有一个数组为 1 2 2 3 3 4,我们需要查询元素3的始末位置,假设初始位置为0,那毫无疑问答案是3,4。用代码实现,就是采用二分法,无限二分查找左右边界。

实现二分法的基础:数组必须包含某种性质,可以是单调性,有单调性必定可以二分,但是无单调性未必不可二分。

简单的来说,我们先查找左边界,设左右分别为l和r(初始值l=0,r=n-1),mid=(l+r)/2,此时需要进行判断。

1.如果q[mid]>=x(x为我们的被查询元素,下文同),以mid为分界点,实际上初始值应该在mid左侧包括mid,此时令r=mid。

2.反之(q[mid]<x),以mid为分界点,世界上初始值应该在mid左侧,不包括mid,故而l=mid+1。

如此循环往复,知道查找出初始值位置。

此后我们在去查找右边界值,设数组的左右边界值为l和r(同上),但此时mid=(l+r+1)/2,此处与上文不同,再进行判断。

1.如果q[mid]<=x,以mid为分界点,终末值实际上在mid右侧,包括mid,l=mid。

2.如果q[mid]>x,以mid为分界点,终末值实际上在mid左侧,r=mid-1。

此处我们可以解释为什么mid=(l+r+1)/2,实际上与后续的判断有关。如果mid=(l+r+)/2,假设(l+r)为奇数,会向下取整(假设l=3,r=4,mid=3,终末值在4,q[r]=q[l]),此时就会陷入死循环。如果答案为偶数,那说明区间内至少还有三个元素,仍会发展到奇数的环节。

至于为什么查找左边界值时不需要+1,应该是因为计算机向下取整的机制,考虑到上述的问题(假设l=3,r=4,mid=3,终末值在4,q[r]=q[l]),代码符合条件。

#include<iostream>
using namespace std;
const int N=100010;//升序排列的数组
int q[N];
int main() {
	int n,m;
	cin >>n>>m;
	for (int i = 0; i < n; i++) cin >> q[i];
	while (m--) {
		int x;
		cin >> x;
		int l = 0, r = n - 1;//区间定位
		while (l < r) {
			int mid = l + r >> 1;//若l+r为奇数,则mid向下取整
			if (q[mid] >= x) r = mid;
			else l = mid + 1;
		}
		if (q[l] != x) cout << "-1 -1" << endl;
		else {
			cout << l << " ";
			int l = 0, r = n - 1;
			while (l < r) {
				int mid = l + r + 1 >> 1;
				if (q[mid] <= x) l = mid;
				else r = mid - 1;
			}
			cout << l << endl;
		}
	}
	return 0;
}

实际上代码中有两个模板,查找左边界值和右边界值。

看到评论区有种特殊的记法,男左女右(本文可以反着记,男右女左),男1女0,故而右边界需要+1,左边界无需+1。

标签:int,边界值,元素,mid,二分法,查找,数组,DAY
From: https://blog.csdn.net/2303_79012056/article/details/140746938

相关文章

  • 24暑假集训day2上午
    上午内容:基础数据结构1.链表分类:单向和双向单向:当前链表只指向下一个元素双向:对于每个元素,记录其前面一个元素,也记录其后面一个元素。注意:链表不建议使用STL的某些元素进行替代,手写链表更为方便。1.单向链表做法:维护每个元素编号,然后维护nx指针,表示当前元素的下一个......
  • 24暑假集训day1上午
    上午内容:枚举递推贪心广告:推荐此题单1.枚举枚举:最基础、最容易想到本质:不重复,不遗漏T1数的划分问题简述:将整数\(n\)分成\(k\)份,且每份不能为空,任意两个方案不相同(不考虑顺序)。方法:搜索关键:有顺序具体步骤:尝试从大到小进行拆分,我们记录当前数剩下总和,记录当前还需......
  • 24暑假集训day3下午
    实现代码:intexgcd(inta,intb,int&x,int&y){ if(b==0){ x=1; y=0; returna; } intd=exgcd(b,a%b,x,y); intk=x; x=y; y=k-(a/b)*y; returnd;}intmain(){ inta=read(),b=read(); intx,y; intd=exgcd(a......
  • 24暑假集训day3上午
    进制转换一个\(m\)进制的数字\(\overline{a_0a_1\cdotsa_k}\)实际上是\(a_0m^k+a_1m^{k-1}+\cdots+a_k\)。\(10\)进制转\(m\)进制:每次除以\(m\)并取出余数。\(m\)进制转\(10\)进制:计算\(a_0m^k+a_1m^{k-}+\cdots+a_k\)。进制转换问题简述:将\(n\)进制数转换......
  • 24暑假集训day2下午
    下午内容:STL差分前缀和倍增1.STL#include<iostream>#include<queue>#include<cmath>#include<algorithm>#include<vector>#include<cstring>#include<cstdio>#include<set>#include<map>#include<uno......
  • 24暑假集训day6上午&下午
    DP概念状态、转移方程、初始化先放一张图(相信都能理解:状态、转移方程、初始化的含义,随便引入斐波那契数列的题)入门题Problem1斐波那契数列\[f_i=f_{i-1}+f_{i-2}\]组合数转移方程:\[C(n,m)=C(n-1,m-1)+C(n-1,m)\]\[C(n,0)=1\]杨辉三角:\[f[i][j]=f[i-1][j-1]+f[i-1]......
  • 24暑假集训day5下午
    DFS本质:一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。该算法讲解时常常与BFS并列,但两者除了都能遍历图的连通块以外,用途完全不同,很少有能混用两种算法的情况。关键:递归调用自身对其访问过的点打上访问标记,在遍历图时跳过已打过标记的点,以......
  • 24暑假集训day5上午
    图论差分约束有\(......
  • 24暑假集训day4上午&下午
    基础图论图的存储方式无向边可以拆成两条有向边1.邻接矩阵邻接矩阵:若\(......
  • 代码随想录算法训练营Day18 | Leetcode 530 二叉搜索树的最小绝对差 Leetcode 236 二
    前言今天有一道题目没写,二叉搜索树中的众数,有点太难理解了,先放一放。Leetcode530二叉搜索树的最小绝对差题目链接:530.二叉搜索树的最小绝对差-力扣(LeetCode)代码随想录题解:代码随想录(programmercarl.com)思路:二叉搜索树的性质是中序遍历为升序的,所以我们想找最小绝......