分块算法是兼顾时间与空间的RMQ实用方案:预处理将数组分为⌊√n⌋大小的块并计算每块最大值;查询时分类处理同块、残段和完整块,复杂度O(√n);通过2的幂次块大小和向量化优化缓存与性能。

如果您需要在C++中实现区间最大值查询(RMQ),且对查询效率有较高要求,但又不希望使用空间开销较大的稀疏表(ST表)或构建复杂度较高的线段树,则分块算法是一种兼顾时间与空间的实用选择。以下是针对该问题的高效分块实现步骤:
一、分块预处理:对每块内元素计算块内最大值
将原数组划分为若干大小为 ⌊√n⌋ 的连续块,对每个块单独遍历一次,记录该块内部的最大值并存入块最大值数组 block_max[]。此步骤确保后续跨块查询时可直接使用块级极值信息,避免逐个比较。
1、计算块大小 block_size = static_cast<int>(std::sqrt(n))</int>;
2、确定总块数 num_blocks = (n + block_size - 1) / block_size;
立即学习“C++免费学习笔记(深入)”;
3、对每个块索引 i(从 0 到 num_blocks - 1),遍历其覆盖范围 [i * block_size, min((i + 1) * block_size - 1, n - 1)],更新 block_max[i] = max(arr[j]);
二、查询优化:分类处理完全覆盖块与边界残段
对于任意查询区间 [L, R],将其拆解为左残段、中间完整块、右残段三部分。中间完整块的最大值可直接从 block_max[] 中 O(1) 获取;左右残段则需线性扫描,因长度均不超过块大小,故单次扫描代价为 O(√n)。
1、计算左端点所在块号 left_block = L / block_size,右端点所在块号 right_block = R / block_size;
2、若 left_block == right_block,则整个区间位于同一块内,直接遍历 arr[L] 至 arr[R] 求最大值;
3、否则,初始化答案为 max(left_segment_max, right_segment_max),其中左残段为 [L, (left_block + 1) * block_size - 1],右残段为 [right_block * block_size, R];
4、对中间块区间 [left_block + 1, right_block - 1],遍历对应 block_max[i] 并更新全局最大值;
三、空间压缩:复用块索引与动态块大小调整
为降低内存访问延迟并提升缓存命中率,采用紧凑的一维数组存储块最大值,并将块大小设为最接近 √n 的 2 的幂次(如 block_size = 1 (std::log2(std::sqrt(n)))),使块边界地址具备位运算对齐特性,加快索引计算速度。
1、计算近似块大小 block_size = 1;当 block_size * block_size 时,执行 <code>block_size ;
2、使用位移替代除法:块号计算改写为 block_id = index >> log2_block_size;
3、块最大值数组声明为 std::vector<int> block_max(num_blocks)</int>,避免动态扩容带来的拷贝开销;
四、查询加速:预加载边界段起始地址与向量化比较
在查询前预先计算左右残段的起始指针与长度,利用现代CPU的SIMD指令集(如 SSE/AVX)对残段进行批量比较。由于残段长度上限为块大小(≤ √n),可安全启用 4/8 元素并行最大值计算,显著减少循环次数。
1、获取左残段首地址 auto left_ptr = &arr[L],长度 left_len = std::min(block_size - L % block_size, R - L + 1);
2、若 left_len >= 4,调用内联函数 _mm_max_epi32 对前 4 个元素做向量化求最大;
3、对剩余元素(
4、右残段同理处理,起始地址为 &arr[R - right_len + 1];
五、常数优化:避免分支预测失败与冗余取址
在查询主循环中消除条件跳转,将块内最大值查询逻辑展开为无分支代码路径。例如,通过位掩码与条件移动指令(cmov)替代 if-else,同时将数组访问模式改为单位步长连续读取,以匹配CPU预取器行为。
1、定义宏 #define MAX(a, b) ((a) > (b) ? (a) : (b)) 并确保编译器内联;
2、对中间块循环,使用 for (int i = l_blk + 1; i ,禁止使用 <code>i 等等价但触发额外比较的写法;
3、将 arr 和 block_max 声明为 const int* 类型,辅助编译器进行别名分析与寄存器分配;


















