查询区间内第一个大于x的数

本文详细介绍了一种使用段式树状数组进行高效查询和更新操作的数据结构实现方法。通过递归构建和查询过程,该方法能够快速找到指定范围内第一个满足条件的元素位置,适用于需要频繁进行区间最大值查询的应用场景。

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

#include <bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
#define mid (l+r>>1)
using namespace std;
const int N = 2e5+100;
int tree[4*N];
void build(int o,int l,int r) {
    if(l==r) cin>>tree[o];
    build(ls,l,mid);
    build(rs,mid+1,r);
    tree[o] = max(tree[ls],tree[rs]);
}
int get(int o, int l, int r, int x) {
    if (l == r) return l;
    return tree[ls] > x ? get(ls, l, mid, x) : get(rs, mid+1, r, x);
}
int query(int o, int l, int r, int ql, int qr, int x) { //查询[ql,qr]内第一个大于x的数的位置
    if (qr < l || r < ql) return -1;
    if (ql <= l && r <= qr) return tree[o] > x ? get(o, l, r, x) : -1;
    int t = query(ls, l, mid, ql, qr, x);
    return t != -1 ? t : query(rs, mid+1, r, ql, qr, x);
}

 

<think>我们有一个非递减组(即升序,但可能有重复元素),需要查找: 1. 第一个大于等于x的元素的索引(即lower_bound) 2. 第一个大于x的元素的索引(即upper_bound) 在C++标准库中,我们可以使用<algorithm>中的lower_bound和upper_bound函来实现。 但是,如果我们要自己实现,通常使用二分查找。 对于第一个问题:查找第一个大于等于x的索引(lower_bound) - 我们定义左边界left=0,右边界right=n(n为组长度,注意区间为左闭右开[left, right)) - 当left < right时,循环: mid = left + (right - left) / 2 - 如果nums[mid] >= x,则说明mid满足条件,但我们要找第一个满足条件的,所以将right缩小到mid(因为mid可能是答案,但左边可能还有) - 否则,说明mid不满足,那么答案一定在[mid+1, right)中,所以left = mid+1 - 循环结束时,left(或right,因为此时相等)就是第一个大于等于x的索引。注意:如果所有元素都小于x,则返回n(即组长度,表示不存在) 对于第二个问题:查找第一个大于x的索引(upper_bound) - 同样,定义left=0, right=n - 当left < right时: mid = left + (right - left) / 2 - 如果nums[mid] > x,则说明mid满足条件,但我们要找第一个满足条件的,所以将right缩小到mid - 否则(即nums[mid]<=x),说明mid不满足,那么答案在[mid+1, right)中,所以left = mid+1 - 循环结束,left就是第一个大于x的索引。 注意:这两个函的实现非常相似,区别只在于判断条件(一个是>=,一个是>)。 下面我们给出C++代码实现:</think>### 非递减组中查找索引的二分方法 在非递减组中查找特定索引的经典方法是**二分查找**,其时间复杂度为 $O(\log n)$,空间复杂度为 $O(1)$。以下是两种情况的实现原理和代码: #### 1. 查找第一个大于等于 $x$ 的索引(`lower_bound`) **原理**: - 当 `nums[mid] >= x` 时,说明 `mid` 可能是目标,但需继续向左检查更小的索引 - 否则向右缩小范围 - 最终 `left` 指向第一个满足条件的索引 **C++ 实现**: ```cpp int lowerBound(vector<int>& nums, int x) { int left = 0, right = nums.size(); while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] >= x) { right = mid; // 向左收缩 } else { left = mid + 1; // 向右收缩 } } return left; // 若未找到返回 nums.size() } ``` #### 2. 查找第一个大于 $x$ 的索引(`upper_bound`) **原理**: - 当 `nums[mid] > x` 时,说明 `mid` 可能是目标,需向左检查 - 否则向右缩小范围 - 最终 `left` 指向第一个满足条件的索引 **C++ 实现**: ```cpp int upperBound(vector<int>& nums, int x) { int left = 0, right = nums.size(); while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] > x) { right = mid; // 向左收缩 } else { left = mid + 1; // 向右收缩 } } return left; // 若未找到返回 nums.size() } ``` #### 关键点说明 1. **区间定义**:使用左闭右开区间 `[left, right)`,保证终止时 `left == right` 2. **移动逻辑**: - `lower_bound` 判断 `>=` 时收缩右边界 - `upper_bound` 判断 `>` 时收缩右边界 3. **边界处理**: - 若所有元素小于 $x$,返回 `nums.size()` - 若元素重复,仍能定位到第一个满足条件的索引 #### 示例 ```cpp vector<int> nums = {1, 2, 3, 3, 5, 6}; int x = 3; cout << lowerBound(nums, x); // 输出 2 (第一个>=3的索引) cout << upperBound(nums, x); // 输出 4 (第一个>3的索引) ``` ### 算法分析 - **时间复杂度**:$O(\log n)$,每次迭代将搜索范围减半 - **空间复杂度**:$O(1)$,仅使用常级额外空间 - **稳定性**:严格保证找到第一个满足条件的索引,适用于重复元素的非递减序列 > 注:C++标准库的 `std::lower_bound()` 和 `std::upper_bound()` 也采用相同实现[^1][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值