如何高效移除C++关联容器中的元素

2025-04-11 16:50

本文主要是介绍如何高效移除C++关联容器中的元素,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+...

一、简介

关联容器将键与值关联起来,包括:

  • std::map,具有唯一键;
  • std::multimap,可以有几个相同的键;
  • std::unordered_map,具有唯一键的哈希映射;
  • std::unordered_multimap,可以有几个相同键的哈希映射。

关联容器还包括集合(set):

  • std::set,包含唯一元素;
  • std::multiset,包含多个等价元素;
  • std::unordered_set,包含唯一元素的哈希集;
  • std::unordered_multiset,包含多个相同元素的哈希集。

集合包含在关联容器中,因为它们可以被视为将键和值融合到一个元素中。

二、移除给定位置的元素

如果通过迭代器位置知道关联容器元素的位置(position),那么从关联容器中删除元素就非常容易。例如:

// 移除该位置的条目。
a.erase(position);

// 删除第一个(包括在内)和最后一个(不包括在内)之间的所有元素。
a.erase(first, last);

这时候,指向被删除元素的迭代器失效,但指向容器的所有其他迭代器仍然有效。这是关联容器的不同之处。

三、移除与特定键值等价的元素

对于关联容器,不谈论“等于特定键值”,而是“等价于特定键值”。

如果知道要移除的元素的键值,移除操作非常简单:

a.erase(myKey);

这将移除所有键值与 myKey 等价的元素(对于multi容器)。

移除根据值而不是键值标识的元素:如果想移除一个 map (或其multi或哈希对应容器)中根据值而不是键值标识的元素,操作就不那么直观了。

需要移除所有满足特定条件的元素,即它们的等于某个值。

四、移除满足特定条件的元素

4.1、与序列容器的结构差异

为了根据特定条件移除序列容器中的元素,可以使用 std::remove_if。但在这里不能这样做。

在序列容器中,将要保留的元素向上移动是可行的,因为它们的值只是按顺序排列的(这是序列容器的定义)。

但关联容器有更强的约束:它们需要快速查找键值(对于非哈希容器,时间复杂度为 O(log(n));对于哈希容器,时间复杂度为 O(1))。为了达到这个目的,它们以更复杂的方式组织数据,通常非哈希容器使用树,而哈希容器使用表,其中精确的位置很重要。

因此,不能像 std::remove_if 那样简单地重新排列元素,否则会破坏内部结构。所以必须遵循接口。而接口中提供的是上面看到的 erase 方法。

4.2、遵循接口

移除满足特定条件的元素的一般思路是遍历容器,对每个元素检查条件,并移除返回 true 的元素。但问题是如何在遍历的同时移除元素?

考虑一下这种遍历的朴素版本:

template<typename AssociativeContainer, typename Predicate>
void erase_if(AssociativeContainer& container, Predicate shouldRemove)
{
    for (auto it = begin(container); it != end(container); ++it) {
        if (shouldRemove(*it)) {
            container.erase(it);
        }
    }
}

注意,这是一种非常罕见的情况,在这种情况下,对迭代器所知不多,只知道它们是迭代器。这是永远不应该出现的代码。

看看上面示例的这一行代码:

container.erase(it);

这会使 it 失效。然后看for循环的结尾位置:

for (auto it = begin(container); it != end(container); ++it)

在 it 失效后立即执行 ++it。这会导致未定义行为。

4.3、迭代器操作

需要找到一种方法,在移除元素之前递增迭代器。为此,有几种选择。在 C++98 中,可以使用后缀递增运算符,它将首先递增迭代器,然后将未递增迭代器的副本传递给 erase

templ编程ate<typename AssociativeContainer, typename Predicate>
void erase_if(AssociativeContainer& container, Predicate shouldRemove)
{
    for (auto it = begin(container); it != end(container);) {
        if (shouldRemove(*it))
            container.erase(it++);
        else
            ++it;
    }
}

但操作迭代器的危险行同样非常高。在 C++11 中,得到了一个风险更小的实现,因为 erase 返回移除元素后的迭代器。可以用这种方式重写代码:

template<typename AssociativeContainer, typename Predicate>
void erase_if(AssociativeContainer& container, Predicate shouldRemove)
{
    for (auto it = begin(container); it != end(container);) {
        if (shouldRemove(*it))
            it = container.erase(it);
        else
            ++it;
    }
}

为了确保此函数仅用于关联容器,C++标准js应该出现相关概念,但在此之前,可以显式地编写各种情况:

namespace details
{
    template<typename AssociativeContainer, typename Predicate>
    void erase_if_impl(AssociativeContainer& container, Predicate shouldRemove)
    {
        for (auto it = begin(container); it != end(container); /* nothing here, the increment in dealt with inside the loop */ )
        {
            if (shouldRemove(*it))
            {
                it = container.erase(it);
            }
            else
            {
                ++it;
            }
        }
    }
}

template<typename Key, typename Value, typename Comparator, typename Predicate>
void erase_if(std::map<Key, Value, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Value, typename Comparator, typename Predicate>
void erase_if(std::multimap<Key, Value, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Value, typename Comparator, typename Predicate>
void erase_if(std::unordered_map<Key, Value, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename V编程alue, typename Comparator, typename Predicate>
void erase_if(std::unordered_multimap<Key, Value, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Comparator, typename Predicate>
void erase_if(std::set<Key, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Comparator, typename Predicate>
void erase_if(std::multiset<Key, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Comparator, typename PredicatChina编程e>
void erase_if(std::unordered_set<Key, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

template<typename Key, typename Comparator, typename Predicate>
void erase_if(std::unordered_multiset<Key, Comparator>& container, Predicate shouldRemove)
{
    return details::erase_if_impl(container, shouldRemove);
}

五、总结

  • 移除给定位置的元素: 使用 erase(position) 或 erase(first, last) 方法,可以移除指定位置的元素或指定范围内的元素。
  • 移除与特定键值等价的元素: 使用 erase(myKey) 方法,可以移除所有键值与 myKey 等价的元素。
  • 移除满足特定条件的元素: 由于关联容器的内部结构,无法直接使用 std::remove_if 方法移除满足特定条件的元素。需要使用迭代器并手动遍历容器,检查每个元素是否满足条件,并使用 erase 方法移除满足条件的元素。

在移除元素时,需要注意迭代器失效的问题,并使用正确的迭代器操作方式来避免未定义行为。

本文还提供了 erase_if 函数的实现,该函数可以用于移除关联容器中满足特定条件的元素。该函数使用 erase 方法和迭代器操作来实现,并针对不同的关联容器类型进行了重载。

以上就是如何高效移除C++关联容器中的元素的详细内容,更多关于移除C++关联容器元素的资料请关注编程China编程(www.chinasem.cn)其它相关文章!

这篇关于如何高效移除C++关联容器中的元素的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154179

相关文章

基于Python实现高效PPT转图片工具

《基于Python实现高效PPT转图片工具》在日常工作中,PPT是我们常用的演示工具,但有时候我们需要将PPT的内容提取为图片格式以便于展示或保存,所以本文将用Python实现PPT转PNG工具,希望... 目录1. 概述2. 功能使用2.1 安装依赖2.2 使用步骤2.3 代码实现2.4 GUI界面3.效

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

C/C++错误信息处理的常见方法及函数

《C/C++错误信息处理的常见方法及函数》C/C++是两种广泛使用的编程语言,特别是在系统编程、嵌入式开发以及高性能计算领域,:本文主要介绍C/C++错误信息处理的常见方法及函数,文中通过代码介绍... 目录前言1. errno 和 perror()示例:2. strerror()示例:3. perror(

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

详解C++中类的大小决定因数

《详解C++中类的大小决定因数》类的大小受多个因素影响,主要包括成员变量、对齐方式、继承关系、虚函数表等,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录1. 非静态数据成员示例:2. 数据对齐(Padding)示例:3. 虚函数(vtable 指针)示例:4. 继承普通继承虚继承5.

C++中std::distance使用方法示例

《C++中std::distance使用方法示例》std::distance是C++标准库中的一个函数,用于计算两个迭代器之间的距离,本文主要介绍了C++中std::distance使用方法示例,具... 目录语法使用方式解释示例输出:其他说明:总结std::distance&n编程bsp;是 C++ 标准

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面