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

更新Item速度后如何重排带自定义比较器的set?求更优方案

问题与解决方案:修改Item速度后维护有序集合

当前方案的问题分析

你在编程竞赛中使用unordered_map存储Item,同时用set<Item*, cmp>保存指针以快速获取速度最快的元素,但修改Item的speed后,set的顺序不会自动更新。这是因为set是基于红黑树实现的有序容器,元素插入后会根据比较器固定位置,不允许直接修改影响排序的键值,否则会破坏容器内部结构。此外,原方案还有隐藏风险:unordered_map扩容时,内部元素的内存地址会变化,导致set中存储的指针变为野指针。

当前方案下的修复方法

要在不清空set的情况下维护顺序,必须遵循先删除、再修改、重新插入的流程:

// 假设要修改MP中key为2的Item的speed
Item* target = &MP[2];
// 先从set中删除旧指针(此时speed未修改,set能正确找到元素)
S.erase(target);
// 修改speed值
target->speed = 5;
// 将修改后的指针重新插入set,set会重新排序
S.insert(target);

⚠️ 注意:必须在修改speed前执行erase,否则修改后set无法通过比较器定位到该元素,删除操作会失败。

更优解决方案推荐

方案1:用set存储排序键对(避免指针问题)

放弃存储指针,改用set存储{speed, id}键值对,利用id的唯一性避免排序冲突。这种方式完全规避指针失效风险,操作更安全:

struct Item{
    int speed;
    int id;
};

// 自定义比较器,与原逻辑一致:speed降序,speed相同则id升序
struct Cmp {
    bool operator()(const pair<int, int>& a, const pair<int, int>& b) const {
        if (a.first != b.first) {
            return a.first > b.first;
        }
        return a.second < b.second;
    }
};

unordered_map<int, Item> MP;
set<pair<int, int>, Cmp> S;

int main(){
    MP[2] = {2, 20};
    MP[1] = {1, 10};
    MP[3] = {3, 30};
    
    // 初始化set
    for (auto& [key, item] : MP) {
        S.insert({item.speed, item.id});
    }
    
    // 修改MP中key为2的Item的speed
    auto& target_item = MP[2];
    // 删除旧的键对
    S.erase({target_item.speed, target_item.id});
    // 修改speed
    target_item.speed = 5;
    // 插入新的键对
    S.insert({target_item.speed, target_item.id});
    
    // 获取速度最快的元素
    auto fastest = *S.begin();
    // 输出:speed=5, id=20
    cout << "Fastest speed: " << fastest.first << ", id: " << fastest.second << endl;
}

该方案的插入、删除、查询操作均为O(logn),适合大多数编程竞赛场景。

方案2:优先队列+延迟删除(适合频繁修改场景)

如果需要频繁修改speed,可以用优先队列(大顶堆)结合延迟删除技巧,修改操作仅需更新unordered_map,无需调整队列:

struct Item{
    int speed;
    int id;
};

// 优先队列比较器:speed降序,speed相同则id升序
struct PQCmp {
    bool operator()(const pair<int, int>& a, const pair<int, int>& b) const {
        if (a.first != b.first) {
            return a.first < b.first; // 小值优先出堆,实现大顶堆效果
        }
        return a.second > b.second; // id小的优先出堆
    }
};

unordered_map<int, Item> MP;
priority_queue<pair<int, int>, vector<pair<int, int>>, PQCmp> pq;

int main(){
    // 假设MP的key为id,方便通过id查找
    MP[10] = {1, 10};
    MP[20] = {2, 20};
    MP[30] = {3, 30};
    
    // 初始化优先队列
    for (auto& [id, item] : MP) {
        pq.push({item.speed, id});
    }
    
    // 修改id为20的Item的speed
    MP[20].speed = 5;
    
    // 获取当前速度最快的有效元素
    while (!pq.empty()) {
        auto [top_speed, top_id] = pq.top();
        if (MP[top_id].speed == top_speed) {
            // 找到有效元素
            cout << "Fastest speed: " << top_speed << ", id: " << top_id << endl;
            break;
        } else {
            // 弹出无效的旧元素
            pq.pop();
        }
    }
}

该方案修改操作是O(1),查询最大元素时需清理无效队列元素,适合修改频繁、查询次数相对较少的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 18:25:28