关于C++倍增算法

       

一、倍增算法核心思想

倍增算法(Binary Lifting)是一种基于二进制分解思想的预处理算法,主要用于优化树结构中的跳跃路径查询问题。其核心思想体现在两个关键阶段:

1. 预处理阶段
通过动态规划构建跳跃表(Jump Table),记录每个节点向上跳跃2^j层的祖先节点,形成二进制尺子式的快速通道。

2. 查询阶段
将目标跳跃距离分解为多个2的幂次跳跃,通过组合预存路径实现对数时间复杂度的查询。

算法优势​(对比传统O(n)遍历):

  • 查询时间复杂度:O(logn)
  • 支持动态规划复用预处理结果
  • 适用于多查询场景

二进制分解的直观解释
如同用不同面值的货币组合支付任意金额,倍增算法通过2^0, 2^1,...,2^k的跳跃步长组合,可以覆盖任意距离的跳跃需求。例如跳跃5层 = 2^2 + 2^0。


二、算法实现四步曲

步骤1:预处理表结构设计

const int MAXN = 1e5+5;       // 最大节点数
const int LOG = 17;           // log2(MAXN)的上界值

int up[MAXN][LOG];            // up[i][j]表示节点i的2^j级祖先
int depth[MAXN];              // 节点深度数组

参数选择原则
LOG取值应满足2^(LOG) > MAXN,例如MAXN=1e5时,log2(1e5)≈16.6,取LOG=17

步骤2:初始化基础状态

void dfs(int u, int parent) {
    up[u][0] = parent;        // 直接父节点即2^0级祖先
    for(int v : tree[u]) {
        if(v != parent) {
            depth[v] = depth[u] + 1;
            dfs(v, u);       // 递归初始化子树
        }
    }
}

初始化注意事项
根节点的up[root][0]应设为-1,需在调用dfs前进行初始化

步骤3:动态规划填充跳跃表

void preprocess(int n) {
    // 初始化无效祖先标记
    memset(up, -1, sizeof(up));
    
    // 动态规划填充跳跃表
    for(int j=1; j<LOG; ++j) {
        for(int i=1; i<=n; ++i) {
            if(up[i][j-1] != -1)
                up[i][j] = up[up[i][j-1]][j-1];
        }
    }
}

填表顺序原理
基于递推关系:2^j = 2^(j-1) + 2^(j-1),因此每个节点的2^j级祖先等于其2^(j-1)级祖先的2^(j-1)级祖先

步骤4:二进制分解查询(以LCA为例)

int lca(int u, int v) {
    // 对齐深度(假设u更深)
    if(depth[u] < depth[v]) swap(u, v);
    
    // 二进制分解提升较深节点
    for(int j=LOG-1; j>=0; --j) {
        if(depth[u] - (1<<j) >= depth[v])
            u = up[u][j];
    }
    
    // 特判直接祖先情况
    if(u == v) return u;
    
    // 同步提升寻找最近公共祖先
    for(int j=LOG-1; j>=0; --j) {
        if(up[u][j] != up[v][j]) {
            u = up[u][j];
            v = up[v][j];
        }
    }
    return up[u][0]; // 最终父节点即为LCA
}

三、关键优化技巧

  1. 空间压缩
    对于非二叉树,可采用链式前向星存储树结构

  2. 预处理加速
    预先计算log2表,将位运算转换为查表操作:

    int log_table[MAXN];
    void init_log() {
        log_table[1] = 0;
        for(int i=2; i<MAXN; ++i)
            log_table[i] = log_table[i/2] + 1;
    }
  3. 路径压缩
    结合并查集思想优化跳跃路径,适用于特定场景


四、复杂度分析

操作时间复杂度空间复杂度
预处理O(n logn)O(n logn)
单次查询O(logn)O(1)
暴力法对比O(n)O(1)

复杂度优势场景
当查询次数q达到1e5量级时,总时间复杂度从O(nq)优化为O(n logn + q logn),效率提升约10^4倍


五、算法扩展与变种

  1. 路径权值查询
    扩展跳跃表存储路径权值极值:

    int max_edge[MAXN][LOG]; // 存储路径最大边权
  2. 动态树结构
    结合欧拉序与线段树实现动态LCA查询

  3. 森林处理
    通过增加root标记处理多棵树的情况:

    bool is_same_tree(int u, int v) {
        return root[u] == root[v];
    }
  4. 跳跃终点预测
    预处理每个节点的2^j级祖先是否超出树高,避免无效跳跃

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值