You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Rust中BTreeMap的O(N)复杂度单调映射?

如何用单调闭包实现BTreeMap的O(N)时间映射

问题背景

我们需要实现一个monotonic_map函数,接收BTreeMap和一个单调闭包(闭包输出的键随原键的递增而严格递增或递减),在O(N)时间内完成映射。普通的迭代收集方法需要逐个插入元素,每个插入操作的时间复杂度为O(log N),总耗时O(N log N);但单调映射下,树的结构可以完全复用,只需遍历替换键值对,因此能做到O(N)的时间复杂度。

函数需求参考:

/*
f是单调的,即随键序递增或递减。
f随键序递增指:
   若k1 < k2,(fk1,fv1) = f(k1,v1)且(fk2,fv2) = f(k2,v2),
   则fk1 < fk2
*/
fn monotonic_map<K, V, F>(tree: BTreeMap<K,V>, f: F) -> Result<BTreeMap<K,V>, String> 
where 
    F: FnMut(K,V) -> (K,V),
    K: Ord + Clone,
    V: Clone,
{ 
    // O(N)计算逻辑
    ...
    // mapped_tree的键值对为原tree中每个(k,v)经f映射后的(fk,fv)
    if is_monotonic { Ok(mapped_tree) } else { Err("闭包不满足单调性要求".to_string()) }
}

实现方案

核心限制

Rust标准库的BTreeMap未暴露内部节点结构,无法直接修改键值同时保留树结构,因此必须借助第三方BTree库(如btree crate),这类库提供了底层节点遍历和修改的能力。

步骤1:验证闭包单调性

按顺序遍历原BTreeMap的键值对,依次应用闭包生成新键,检查新键序列是否保持严格的单一趋势(全递增或全递减):

  • 取第一个键值对应用闭包得到基准新键
  • 遍历后续键值对,比较新键与前一个新键的关系
  • 若出现趋势反转或重复键,直接返回错误

步骤2:执行O(N)映射

根据单调性的不同,选择对应的映射方式:

  • 递增映射:使用库提供的可变游标(CursorMut)遍历每个节点,直接替换键和值,树的结构完全不变,耗时O(N)
  • 递减映射:先收集所有映射后的键值对,反转得到递增序列后顺序插入新树。BTree对顺序插入有优化,总耗时O(N)(无需频繁节点分裂)

代码实现(基于btree crate)

首先在Cargo.toml中添加依赖:

[dependencies]
btree = "0.4.2"

完整实现代码:

use btree::BTree;
use std::cmp::Ordering;

#[derive(Debug, Clone)]
enum MonotonicTrend {
    Increasing,
    Decreasing,
}

fn monotonic_map<K, V, F>(mut tree: BTree<K, V>, mut f: F) -> Result<BTree<K, V>, String>
where
    K: Ord + Clone,
    V: Clone,
    F: FnMut(K, V) -> (K, V),
{
    // 处理空树
    if tree.is_empty() {
        return Ok(tree);
    }

    // 第一步:验证单调性
    let mut iter = tree.iter().peekable();
    let (first_k, first_v) = iter.next().unwrap();
    let (mut prev_fk, _) = f(first_k.clone(), first_v.clone());
    let mut trend: Option<MonotonicTrend> = None;

    for (k, v) in iter {
        let (fk, _) = f(k.clone(), v.clone());
        match prev_fk.cmp(&fk) {
            Ordering::Less => {
                if trend == Some(MonotonicTrend::Decreasing) {
                    return Err("闭包同时出现递增和递减趋势,不满足单调性".into());
                }
                trend = Some(MonotonicTrend::Increasing);
            }
            Ordering::Greater => {
                if trend == Some(MonotonicTrend::Increasing) {
                    return Err("闭包同时出现递增和递减趋势,不满足单调性".into());
                }
                trend = Some(MonotonicTrend::Decreasing);
            }
            Ordering::Equal => {
                return Err("闭包生成重复键,不满足严格单调要求".into());
            }
        }
        prev_fk = fk;
    }

    let trend = trend.unwrap_or(MonotonicTrend::Increasing); // 单元素默认递增

    // 第二步:执行O(N)映射
    match trend {
        MonotonicTrend::Increasing => {
            // 直接遍历修改节点内容,树结构不变
            let mut cursor = tree.cursor_mut();
            while let Some((k, v)) = cursor.key_value_mut() {
                let (new_k, new_v) = f(std::mem::take(k), std::mem::take(v));
                *k = new_k;
                *v = new_v;
                cursor.move_next();
            }
            Ok(tree)
        }
        MonotonicTrend::Decreasing => {
            // 收集映射后的键值对,反转后顺序插入(O(N)时间)
            let mut mapped_pairs: Vec<_> = tree.into_iter().map(|(k, v)| f(k, v)).collect();
            mapped_pairs.reverse();
            
            let mut new_tree = BTree::new();
            for (k, v) in mapped_pairs {
                new_tree.insert(k, v);
            }
            Ok(new_tree)
        }
    }
}

// 测试用例
fn main() {
    // 递增映射:键+1,值添加后缀
    let mut tree = BTree::new();
    tree.insert(1, "a");
    tree.insert(2, "b");
    tree.insert(3, "c");
    println!("递增映射结果: {:?}", monotonic_map(tree, |k, v| (k + 1, format!("{}_inc", v))));

    // 递减映射:键取反,值添加后缀
    let mut tree = BTree::new();
    tree.insert(1, "a");
    tree.insert(2, "b");
    tree.insert(3, "c");
    println!("递减映射结果: {:?}", monotonic_map(tree, |k, v| (-k, format!("{}_dec", v))));

    // 非单调映射:键交替加减
    let mut tree = BTree::new();
    tree.insert(1, "a");
    tree.insert(2, "b");
    tree.insert(3, "c");
    println!("非单调映射结果: {:?}", monotonic_map(tree, |k, v| if k % 2 == 0 { (k + 1, v) } else { (k - 1, v) }));
}

关键说明

  • 递增映射时,直接修改节点内容,完全复用原树结构,时间复杂度严格O(N)
  • 递减映射时,顺序插入反转后的递增序列,BTree的顺序插入操作可做到O(N) amortized时间(无需频繁节点分裂和旋转)
  • 单调性验证步骤为O(N),仅需一次遍历

内容的提问来源于stack exchange,提问作者FreD

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 08:53:13