倍增法(binary lifting)和 ST 算法(Sparse Table)

倍增法(binary lifting)

概述

倍增法 和 二分法 的思想是 “相反”的,效率都很高。

二分法是每次将解存在的空间缩小一半,

倍增法是每次扩大一倍,快速扩展到大空间,线性的处理转化为对数级的处理,大大地优化时间复杂度。

故此,倍增法主要有两种应用场合:

  1. 区间问题中,从小区间倍增到大区间,求解和 区间查询 有关的问题。
    1. 【RMQ问题 (Range Maximum/Minimum Query) 区间最值查询】(ST 算法、后缀数组)
  2. 数值计算中,如空间内的元素满足倍增关系,或能借助倍增法计算,能用倍增法求解元素的精确值
    1. 【LCA 问题(Lowest Common Ancestor)最近公共祖先】。

倍增法原理

倍增 即“成倍增长”。实现倍增,有时即每步乘以二,如后缀数组,每次扩展字符长度。

在大多数题目中有更好地实现方法,即利用二进制本身的倍增特性,把一个数 N 用二进制展开。

二进制划分反映了一种快速增长的特性,第 i 位的权值等于前面所有权值的和加一

一个整数 n,它的二进制只有$ \log_2n $位,如果要从 0 增长到 n,可以以 $ 1,2,4,…2^k $ 为跳板,快速跳到 n,这些跳板有 k 个。

倍增法的局限性是需要提前计算出 这些跳板,即要求数据是静态不变的,不是动态变化的。如果数据发生变化则跳板需要重新计算,跳板就失去了意义,不能多次重复利用即不再能大大优化时间。

经典例题

国旗计划 洛谷 P4155 1. **倍增法 :**

(1)化圆为线,把题目给的圆圈断开变成一条线,更方便分析和处理。

注意.圆圈是首尾相接的,断开后为了保持原来的首尾关系,需要把原来的圆圈复制再接入尾部。

(环的处理)环上的问题一般改成链来做,其主要方法是输入完数据后再复制一遍加到原数组后面

(2)贪心法,首先考虑从一个区间出发,如何选择所有的区间。

(贪心策略)选择一个区间 i 后,下一个区间只能从 左端点小于或等于右端点的那些区间中选择,在这些区同中选择右端点最大的那个区间,是最优的。

(示例)如图,选择区间i后,下一个区间可以从A、B、C中选择,它们的左端点都在i内部。C是最优的,因为它的右端点最远。选定C之后,再用贪心策略找下一个区间。这样找下去,就得到了所需的最少区间

(3)倍增法&动态规划(类似 ST 算法)。通过利用子问题的结果,配合倍增,快速预处理出 跳板。

为了进行高效的"次查询,可以用类似ST算法中的倍增法,预计算出-些“跳板”,快速找到后面的区间。

  1. O(n)方法

ST 算法(Sparse Table)

ST 算法

Sparse Table(ST 算法)

ST(Sparse Table)算法是一种高效的静态查询数据结构,主要用于处理数组中区间最小值、最大值等查询问题。Sparse Table 的设计目标是优化对数组区间查询的时间复杂度,它非常适合用于那些元素不改变(静态)的情形。

Sparse Table 的基本概念

Sparse Table 通过预处理数组,构建一个二维表格,用于存储不同区间长度的最小值。这样,我们可以在查询时通过直接查表的方式,快速获取区间的最小值或最大值。

特点

  1. 静态数据结构:Sparse Table 是针对静态数组设计的,即数组中的元素不会改变。
  2. 查询时间复杂度为 O(1):通过预处理,查询任意区间的最小值或最大值的时间复杂度为常数级别 O(1)。
  3. 预处理时间复杂度为 O(n log n):需要通过预处理来构建 Sparse Table,时间复杂度为 O(n log n),其中 n 是数组的长度。
  4. 空间复杂度为 O(n log n):为了存储预处理信息,Sparse Table 占用的空间是 O(n log n)。

主要应用

Sparse Table 主要用于以下几种类型的区间查询问题:

  • 区间最小值查询(Range Minimum Query, RMQ)
  • 区间最大值查询
  • 区间 GCD 查询(最大公约数)

Sparse Table 的构建与查询

1. 构建 Sparse Table

假设我们有一个长度为 n 的数组 arr,我们构建一个二维数组 st,其中 st[i][j] 表示从数组 arr[i] 开始,长度为 2^j 的区间的最小值(或最大值)。具体的构建过程如下:

  • st[i][0] 就是数组的原始值,即 st[i][0] = arr[i]
  • 对于更大的区间,使用动态规划来计算:
    • st[i][j] = min(st[i][j-1], st[i + 2^(j-1)][j-1])
    • 这里,st[i][j-1] 表示从 i 开始的长度为 2^(j-1) 的最小值(或最大值),而 st[i + 2^(j-1)][j-1] 表示从 i + 2^(j-1) 开始的长度为 2^(j-1) 的最小值(或最大值)。
2. 查询操作

对于一个给定的区间 [L, R],我们要查找其最小值或最大值。由于区间长度可能不是 2^k 的形式,因此我们将区间分成两个部分:

  • L 开始,查询长度为 2^k 的区间,最大长度不超过 R - L + 1
  • R 结束,查询另一个长度为 2^k 的区间。

具体查询步骤:

  • 找到区间 [L, R] 所对应的最大 k,使得 2^k <= R - L + 1
  • 然后查询两个区间,[L, L + 2^k - 1][R - 2^k + 1, R],从 st 数组中直接取值并返回结果。

示例代码

import math

构建 Sparse Table

def build_sparse_table(arr):
    n = len(arr)
    max_log = math.floor(math.log2(n)) + 1
    st = [[0] * max_log for _ in range(n)]
    
    # 初始化 st[i][0] = arr[i]
    for i in range(n):
        st[i][0] = arr[i]
    
    # 动态规划计算 st[i][j]
    for j in range(1, max_log):
        for i in range(n - (1 &lt;&lt; j) + 1):
            st[i][j] = min(st[i][j - 1], st[i + (1 &lt;&lt; (j - 1))][j - 1])
    
    return st

查询区间 [L, R] 的最小值

def query(st, L, R):
    length = R - L + 1
    k = int(math.log2(length))
    return min(st[L][k], st[R - (1 &lt;&lt; k) + 1][k])

示例

arr = [1, 3, 2, 7, 9, 11, 6, 5, 8]
st = build_sparse_table(arr)
print(query(st, 2, 5))# 查询区间 [2, 5] 的最小值

时间复杂度分析

  1. 构建 Sparse Table
    • 对于每个区间长度 2^j,我们需要遍历数组的一部分来填充 st[i][j]
    • 总体的时间复杂度为 O(n log n)
  1. 查询操作
    • 查询只涉及取两个值,并进行一次 min 操作,因此时间复杂度为 O(1)

优缺点

优点

  • 查询非常高效,适用于需要频繁查询的场景。
  • 预处理和查询的时间复杂度非常优秀(O(n log n)O(1))。

缺点

  • 由于是静态数据结构,更新操作非常麻烦,不适合动态数据变化的情况。
  • 空间复杂度较高,尤其是当数组长度很大时,可能需要大量内存。

总结

Sparse Table 是一种非常高效的静态查询数据结构,适用于处理区间最小值、最大值等问题。它通过预处理来加速查询,尤其在需要大量区间查询的情况下,能够提供极快的查询响应。然而,它不适合动态更新的场景,因为更新数据后需要重新构建表。

适用场景

  1. 静态数组 需要区间查询
  2. 大区间被两个小区间覆盖
  3. 小区间的重复覆盖不影响结果
  4. 可以用倍增法划分小区间 并计算

除了求 区间最值,还有区间最大公约数问题(RGQ, Range GCD Query)

RMQ 问题

RMQ(Range Minimum Query,区间最小值查询)问题是计算机科学中一个经典的算法问题,它的目标是在一个给定的数组或序列中,快速地找出任意区间(通常是子数组)内的最小值。RMQ问题在许多实际应用中都有广泛的应用,比如计算机图形学、数据压缩、网络流量分析等领域。

问题描述

给定一个数组 (A[1..n]) 和若干个查询,每个查询给定一个区间 ([l, r]),要求返回数组 (A) 在区间 ([l, r]) 上的最小值。多个查询的目标是要尽可能高效地计算每个区间的最小值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albeata

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值