C++关联容器2——关联容器特有操作

目录

关联容器操作

关联容器迭代器

set 的迭代器是const的

遍历关联容器

关联容器和算法

添加元素

向map添加元素

检测insert的返回值

向multiset或 multimap添加元素

std::multiset 例子

std::multimap 例子

删除元素

std::set 和 std::multiset 示例

std::map 和 std::multimap 示例

map的下标操作

使用下标操作的返回值

访问元素

对map 使用find代替下标操作

在multimap或multiset中查找元素

一种不同的,面向迭代器的解决方法

equal_range函数


关联容器操作

除了http://t.****img.cn/osoJZ 中列出的类型,关联容器还定义了下表中列出的类型。这些类型表示容器关键字和值的类型。

关联容器额外的类型别名
key_type 此容器类型的关键字类型
mapped_type 每个关键字关联的类型;只适用于map
value_type 对于set,与key_type相同
对于map,为pair<const key_type, mapped_type>

对于set类型,key_type和value_type是一样的;set中保存的值就是关键字。

在一个map中,元素是关键字——值对。即,每个元素是一个pair对象,包含一个关键字和一个关联的值。

由于我们不能改变一个元素的关键字,因此这些pair的关键字部分是const的:

set<string>::value_type vl;// v1是一个string

set<string>::key_type v2;//v2是一个string

map<string, int>::value_type v3;// v3是一个pair<const string, int>

map<string, int>::key_type v4;// v4是一个string

map<string, int>::mapped_type v5;// v5是一个int

和关联容器与顺序容器一样,我们使用作用域运算符来提取一个类型的成员——例如,map<string,int>::key_type。

只有map类型(unordered_map、 unordered_multimap、multimap和map)才定义了mapped_type。

关联容器迭代器

当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type的值的引用。

对map而言,value_type 是一个pair类型,其first成员保存const的关键字,second成员保存值:

//获得指向wordcount中一个元素的迭代器
auto map_it = word_count.begin();

//*map_it是指向一个pair<const string,size_t>对象的引用
cout << map_it->first;//打印此元素的关键字

cout << " " << map_it->second;//打印此元素的值


map_it->first ="new key";//错误:关键字是const.的

++map_it->second;//正确:我们可以通过迭代器改变元素

必须记住,一个map的value_type是一个pair,我们可以改变pair的值,但不能改变关键字成员的值。

set 的迭代器是const的

虽然set类型同时定义了iterator和const iterator类型,但两种类型都只允许只读访问set中的元素。

与不能改变一个map元素的关键字一样,一个set中的关键字也是const的。

可以用一个set迭代器来读取元素的值,但不能修改:

set<int> iset = {0,1,2,3,4,5,6,7,8,9};

set<int>::iterator set_it = iset.begin();

if (set_it !=iset.end()){
*set_it = 42;
//错误:Set中的关键字是只读的

cout << *set_it << endl;//正确:可以读关键字
}

遍历关联容器

map 和set类型都支持begin和end操作。

与往常一样,我们可以用这些函数获取迭代器,然后用迭代器来遍历容器。

例如

#include <iostream>  
#include <map>  
  
int main() {  
    std::map<int, std::string> my_map = {{1, "one"}, {2, "two"}, {3, "three"}};  
  
    for (auto it = my_map.begin(); it != my_map.end(); ++it) {  
        std::cout << "Key: " << it->first << ", Value: " << it->second << std::endl;  
    }  
  
    return 0;  
}
#include <iostream>  
#include <set>  
  
int main() {  
    std::set<int> my_set = {1, 2, 3};  
  
    for (auto it = my_set.begin(); it != my_set.end(); ++it) {  
        std::cout << "Value: " << *it << std::endl;  
    }  
  
    return 0;  
}

本程序的输出是按字典序排列的。当使用一个迭代器通历一个map,multimap、set或multiset时,迭代器按关键字升序遍历元素

关联容器和算法

我们通常不对关联容器使用泛型算法。

关键字是const这一特性意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,而set 类型中的元素是const的,map中的元素是pair,其第一个成员是const的。

关联容器可用于只读取元素的算法。

但是,很多这类算法都要拽索序列。由于关联容器中的元素不能通过它们的关键字进行(快速)查找,因此对其使用泛型按索算法几乎总是个坏主意

在实际编程中,如果我们真要对一个关联容器使用算法,要么是将它当作一个源序列。要么当作一个目的位置。例如,可以用泛型copy算法将元素从一个关联容器拷贝到另一个序列。类似的,可以调用 inserter将一个插入器绑定到一个关联容器。通过使用inserter,我们可以将关联容器当作一个目的位置来调用另一个算法。

添加元素

关联容器的insert成员向容器中添加一个元素或一个元素范围。由于map和set(以及对应的无序类型)包含不重复的关键字,因此插入一个己存在的元素对容器没有任何影响:

vector<int> ivec = (2,4,6,8,2,4,6,8);// ivec有8个元素

set<int> set2;// 空集合

set2.insert(ivec.cbegin(), ivec.cend());//set2有4个元素

set2.insert({1,3,5,7,1,3,5,7});//set2现在有8个元素

insert有两个版本,分别接受一对迭代器,或是一个初始化器列表,这两个版本的行为类似对应的构造函数——对于一个给定的关键字,只有第一个带此关键字的元素才被插入到容器中。

向map添加元素

对一个map进行insert操作时,必须记住元素类型是pair。

通常,对于想要插入的数据,并没有一个现成的pair对象。可以在insert 的参数列表中创建一个 pair:

 // 声明一个std::map,键为std::string类型,值为size_t类型  
    std::map<std::string, size_t> word_count; 

word_count.insert({word, 1});
word_count.insert (make_pair(word, 1));
word_count.insert (pair<string, size_t>(word, 1));
word_count.insert (map<string, size _t>::value_type(word, 1));


如我们所见,在新标准下,创建一个pair最简单的方法是在参数列表中使用花括号初始化。也可以调用make_pair或显式构造pair。

最后一个insert 调用中的参数:

map<string, size t>::value_type(s, 1)

构造一个恰当的pair类型,并构造该类型的一个新对象,插入到map中。

关联容器insert 操作
c.insert(v) v是value_type类型的对象;args用来构造一个元素
c.emplace(args)

对于map和set,只有当元素的关键字不在c中时才插入(或构造)元素。函数返回一个pair,包含一个迭代器,指向具有指定关键字的元素,以及一个指示插入是否成功的bool值。

对于multimap和multiset,总会插入(或构造)给定元素,并返回一个指向新元素的迭代器

c.insert(b, e)
c.insert(i1)
b和e是迭代器,表示一个c::value_type类型值的范围;i1是这种值的花括号列表。函数返回 void
对于map和set,只插入关键字不在c中的元素。对于multimap和multiset,则会插入范围中的每个元素
c.insert(p,v)
c.emplace (p, args)
类似insert(v)(或emplace(args)),但将迭代器p作为一个提示,指出从哪里开始搜索新元素应该存储的位置。返回一个迭代器,指向具有给定关键字的元素

检测insert的返回值

insert(或emplace)返回的值依赖于容器类型和参数。

对于不包含重复关键字的容器,添加单一元素的insert和emplace版本返回一个pair,告诉我们插入操作是否成功。

pair的first 成员是一个迭代器,指向具有给定关键字的元素;second成员是一个bool值,指出元素是插入成功还是已经存在于容器中。

如果关键字已在容器中,则insert 什么事情也不做,且返回值中的bool部分为false。

如果关键字不存在,元素被插入容器中,且bool值为true。

对于像std::mapstd::unordered_map这样的关联容器,insertemplace函数在插入元素时返回的是一个std::pair<iterator, bool>。这个pairfirst成员是一个指向插入元素(或已存在元素)的迭代器,而second成员是一个布尔值,指示是否成功插入了元素(即该元素之前是否不存在于容器中)。

以下是一个使用std::mapinsert函数的例子,它展示了如何检查插入操作是否成功以及如何处理返回值

#include <iostream>  
#include <map>  
#include <string>  
  
int main() {  
    std::map<std::string, int> word_count;  
  
    // 插入一个单词,它之前不存在于map中  
    auto result = word_count.insert(std::pair<std::string, int>("apple", 1));  
    if (result.second) {  
        std::cout << "Inserted apple, count is now " << result.first->second << std::endl;  
    } else {  
        std::cout << "Apple already exists, not inserted." << std::endl;  
    }  
  
    // 尝试再次插入相同的单词  
    result = word_count.insert(std::pair<std::string, int>("apple", 2)); // 这不会改变apple的计数  
    if (result.second) {  
        std::cout << "Inserted apple again, count is now " << result.first->second << std::endl;  
    } else {  
        std::cout << "Apple already exists, not inserted." << std::endl;  
        // 尽管没有插入新元素,但result.first仍然指向已存在的apple元素  
        std::cout << "Existing count for apple is " << result.first->second << std::endl;  
    }  
  
    // 插入一个新单词  
    result = word_count.insert(std::pair<std::string, int>("banana", 1));  
    if (result.second) {  
        std::cout << "Inserted banana, count is now " << result.first->second << std::endl;  
    }  
  
    // 打印所有单词和它们的计数  
    for (const auto& pair : word_count) {  
        std::cout << pair.first << ": " << pair.second << std::endl;  
    }  
  
    return 0;  
}

在这个例子中,第一次插入"apple"时,result.secondtrue,表示插入成功。第二次尝试插入"apple"时,由于它已存在于map中,所以result.secondfalse,并且result.first指向已存在的"apple"元素。

注意:从C++11开始,您还可以使用更简洁的语法来插入元素,例如使用花括号初始化列表或std::make_pair(尽管对于map,直接使用{}通常更简洁)。此外,C++17引入了emplace函数,它允许您直接在容器中构造元素,这可能在某些情况下更有效率。

向multiset或 multimap添加元素

std::multiset和std::multimap是C++标准库中的关联容器,它们允许元素(对于std::multiset)或键值对(对于std::multimap)的重复。

与std::set和std::map不同,当你向std::multiset或std::multimap插入元素时,你不需要检查元素是否已存在,因为即使存在,新元素也会被插入。

以下是使用std::multiset和std::multimap添加元素的简单例子:

std::multiset 例子

 #include <iostream>  
 #include <set>  
   int main() {  
 std::multiset<int> numbers;  
   // 向multiset中添加元素  
 numbers.insert(10);  
 numbers.insert(20);  
 numbers.insert(10); // 允许重复  
 numbers.insert(30);  
   // 遍历multiset并打印元素  
 for (const auto& num : numbers) {  
 std::cout << num << ' ';  
 }  
 std::cout << std::endl;  
   return 0;  
 } 

输出可能是(但不一定是,因为multiset不保证顺序):

 10 10 20 30 

std::multimap 例子

 #include <iostream>  
 #include <map>  
   int main() {  
 std::multimap<std::string, int> word_counts;  
   // 向multimap中添加键值对  
 word_counts.insert({"apple", 1});  
 word_counts.insert({"banana", 2});  
 word_counts.insert({"apple", 1}); // 允许重复的键值对  
 word_counts.insert({"cherry", 1});  
   // 遍历multimap并打印键值对  
 for (const auto& pair : word_counts) {  
 std::cout << pair.first << ": " << pair.second << std::endl;  
 }  
   return 0;  
 } 



输出可能是(但不一定是,因为multimap不保证顺序):

 apple: 1  
 apple: 1  
 banana: 2  
 cherry: 1 

请注意,在上面的例子中,我们使用了范围for循环(C++11及更高版本)来遍历multiset和multimap中的元素。这种循环语法简洁且易于阅读。

删除元素

关联容器定义了三个版本的erase,如表所示。

从关联容器删除元素
c.erase(k) 从c中删除每个关键字为k的元素。返回一个size_type值,指出删除的元素的数量
c.erase(p) 从c中删除迭代器p指定的元素。巨必须指向c中一个真实元素,不能等于c.end()。返回一个指向p之后元素的迭代器,若p指向c中的尾元素,则返回c.end()
c.erase(b, e) 删除迭代器对b和e所表示的范围中的元素。返回e


与顺序容器一样,我们可以通过传递给erase 一个迭代器或一个迭代器对来删除一个元素或者一个元素范围。

这两个版本的erase 与对应的顺序容器的操作非常相似:指定的元素被删除,函数返回void。

关联容器提供一个额外的erase操作,它接受一个key_type参数。

此版本删除所有匹配给定关键字的元素(如果存在的话),返回实际删除的元素的数量。

以下是使用关联容器erase方法的示例:

std::set 和 std::multiset 示例

#include <iostream>  
 #include <set>  
   int main() {  
 std::multiset<int> numbers;  
   // 插入元素  
 numbers.insert(10);  
 numbers.insert(20);  
 numbers.insert(10); // 允许重复  
   // 使用单个迭代器删除元素  
 auto it = numbers.find(10); // 找到一个10的迭代器  
 if (it != numbers.end()) {  
 numbers.erase(it); // 删除一个10  
 }  
   // 使用迭代器对删除一个范围内的元素(假设我们想要删除所有10)  
 // 注意:由于multiset允许重复,我们需要循环删除  
 while ((it = numbers.find(10)) != numbers.end()) {  
 numbers.erase(it);  
 }  
   // 使用key_type参数删除所有匹配关键字的元素  
 // 但在这个例子中,我们已经没有10了,所以不会删除任何元素  
 size_t count = numbers.erase(10); // 尝试删除所有10,返回删除的数量  
 std::cout << "Deleted " << count << " elements with key 10.\n";  
   // 打印剩余的元素  
 for (const auto& num : numbers) {  
 std::cout << num << ' ';  
 }  
 std::cout << std::endl;  
   return 0;  
 } 

std::map 和 std::multimap 示例

 #include <iostream>  
 #include <map>  
   int main() {  
 std::multimap<std::string, int> word_counts;  
   // 插入键值对  
 word_counts.insert({"apple", 1});  
 word_counts.insert({"banana", 2});  
 word_counts.insert({"apple", 1}); // 允许重复的键值对  
   // 使用单个迭代器删除一个键值对  
 auto it = word_counts.find("apple"); // 找到一个apple的迭代器  
 if (it != word_counts.end()) {  
 word_counts.erase(it); // 删除一个apple的键值对  
 }  
   // 使用key_type参数删除所有匹配关键字的键值对  
 size_t count = word_counts.erase("apple"); // 尝试删除所有apple,返回删除的数量  
 std::cout << "Deleted " << count << " elements with key 'apple'.\n";  
   // 打印剩余的键值对  
 for (const auto& pair : word_counts) {  
 std::cout << pair.first << ": " << pair.second << std::endl;  
 }  
   return 0;  
 } 

在上面的示例中,您可以看到erase方法的不同用法,包括接受单个迭代器、迭代器对和key_type参数。当使用key_type参数时,它会返回实际删除的元素数量。

map的下标操作

map和 unordered_map 容器提供了下标运算符和一个对应的at 函数,如表所示。

map 和unordered_map的下标操作
c[k] 返回关键字为k的元素;如果k不在c中,添加一个关键字为k的元素,对其进行值初始化
c.at(k) 访问关键字为k的元素,带参数检查;若k不在c中,抛出一个out_of_range异常

set类型不支持下标,因为set中没有与关键字相关联的“值”。

元素本身就是关键字,因此“获取与一个关键字相关联的值”的操作就没有意义了。

我们不能对一个multimap或一个unordered_multimap进行下标操作,因为这些容器中可能有多个值与一个关键字相关联。

类似我们用过的其他下标运算符,map下标运算符接受一个索引(即,一个关键字),获取与此关键字相关联的值。但是,与其他下标运算符不同的是,如果关键字并不在map中,会为它创建一个元素并插入到map中,关联值将进行值初始化

例如,如果我们编写如下代码

map <string, size_t> word_count; // empty map

//插入一个关键字为Anna的元素,关联值进行值初始化;然后将1赋予它
word count["Anna"]= 1;

将会执行如下操作:

  1. 在word_count 中搜索关键字为Anna的元素,未找到。
  2. 将一个新的关键字-值对插入到 word_count中。关键字是一个const string,
  3. 保存Anna。值进行值初始化,在本例中意味着值为0。
  4. 提取出新插入的元素,并将值1赋予它。

由于下标运算符可能插入一个新元素,我们只可以对非const的map使用下标操作。

对一个map使用下标操作,其行为与数组或vector上的下标操作很不相同:使用一个不在容器中的关键字作为下标,会添加一个具有此关键字的元素到map中。

使用下标操作的返回值

map的下标运算符与我们用过的其他下标运算符的另一个不同之处是其返回类型。

通常情况下,解引用一个迭代器所返回的类型与下标运算符返回的类型是一样的。但对map则不然:当对一个map进行下标操作时,会获得一个mapped_type对象;但当解引用一个map迭代器时,会得到一个value_type对象。

与其他下标运算符相同的是,map的下标运算符返回一个左值。由于返回的是一个左值,所以我们既可以读也可以写元素:

cout << word_count["Anna"];//用Anna作为下标提取元素;会打印出1

++word_count["Anna"];//提取元素,将其增1

cout << word_count["Anna"];//提取元素并打印它;会打印出2

与vector与string不同,map的下标运算符返回的类型与解引用map迭代器得到的类型不同。

如果关键字还未在map中,下标运算符会添加一个新元素,这一特性允许我们编写出异常简洁的程序。另一方面,有时只是想知道一个元素是否已在map中,但在不存在时并不想添加元素。在这种情况下,就不能使用下标运算符。

访问元素

关联容器提供多种查找一个指定元素的方法,如表所示。

在一个关联容器中查找元素的操作
c.find(k) 返回一个迭代器,指向第一个关键字为k的元素,若不在容器中,则返回尾后迭代器
c.count(k) 返回关键字等于k的元素的数量。对于不允许重复关键字的容器,返回值永远是0或1
c.lower_bound(k) 返回一个迭代器,指向第一个关键字不小于k的元素
c.upper_bound(k) 返回一个迭代器,指向第一个关键字大于k的元素
c.equal_range(k) 返回一个迭代器pair,表示关键字等于k的元素的范围。若k不存在,pair的两个成员均等于c.end()

lower_bound和upper_bound不适用于无序容器。
下标和at操作只适用于非const的map和unordered_ map。

应该使用哪个操作依赖于我们要解决什么问题。如果我们所关心的只不过是一个特定元素是否已在容器中,可能find 是最佳选择。

对于不允许重复关键字的容器,可能使用find还是count没什么区别。但对于允许重复关键字的容器,count 还会做更多的工作:如果元素在容器中,它还会统计有多少个元素有相同的关键字。如果不需要计数,最好使用 find:

set<int> iset = {0,1,2,3,4,5,6,7,8,9};

iset.find(1);// 返回一个迭代器,指向key == 1的元素

iset.find(11);//返回一个迭代器,其值等于iset.end()

iset.count(1);//返回1

iset.count(11);//返回0

对map 使用find代替下标操作

对map和unordered_map类型,下标运算符提供了最简单的提取元素的方法。

但是,如我们所见,使用下标操作有一个严重的副作用:如果关键字还未在map中,下标操作会插入一个具有给定关键字的元素。这种行为是否正确完全依赖于我们的预期是什么。

但有时,我们只是想知道一个给定关键字是否在map中,而不想改变map。这样就不能使用下标运算符来检查一个元素是否存在,因为如果关键字不存在的话,下标运算符会插入一个新元素。在这种情况下,应该使用 find:

#include <iostream>  
#include <map>  
  
int main() {  
    std::map<std::string, int> word_counts;  
  
    // 插入一些键值对  
    word_counts["apple"] = 1;  
    word_counts["banana"] = 2;  
  
    // 检查关键字"apple"是否存在  
    if (word_counts.find("apple") != word_counts.end()) {  
        std::cout << "The word 'apple' exists in the map.\n";  
    } else {  
        std::cout << "The word 'apple' does not exist in the map.\n";  
    }  
  
    // 检查关键字"cherry"是否存在  
    if (word_counts.find("cherry") == word_counts.end()) {  
        std::cout << "The word 'cherry' does not exist in the map.\n";  
    } else {  
        std::cout << "The word 'cherry' exists in the map.\n";  
    }  
  
    return 0;  
}

在这个例子中,我们首先使用find方法来检查"apple"是否存在于map中。由于我们之前插入了"apple",所以find方法返回的迭代器不等于end(),因此我们知道"apple"存在于map中。接着,我们检查"cherry",由于"cherry"没有在map中,所以find方法返回的迭代器等于end(),我们知道"cherry"不存在于map中。 

在multimap或multiset中查找元素

在一个不允许重复关键字的关联容器中查找一个元素是一件很简单的事情——元素要么在容器中,要么不在。

但对于允许重复关键字的容器来说,过程就更为复杂:在容器中可能有很多元素具有给定的关键字。如果一个multimap或multiset中有多个元素具有给定关键字,则这些元素在容器中会相邻存储。

在允许重复关键字的关联容器(如std::multimapstd::multiset)中查找具有给定关键字的元素时,如果容器中存在多个这样的元素,它们会按照它们插入的顺序相邻存储。这意味着你可以通过迭代器在容器中遍历所有具有相同关键字的元素。

以下是一个使用std::multimap的示例,其中展示了如何查找并遍历所有具有给定关键字的元素:

#include <iostream>  
#include <map>  
  
int main() {  
    std::multimap<std::string, int> word_occurrences;  
  
    // 插入一些键值对,注意关键字可以重复  
    word_occurrences.insert({"apple", 1});  
    word_occurrences.insert({"banana", 2});  
    word_occurrences.insert({"apple", 3}); // 关键字"apple"再次出现  
    word_occurrences.insert({"cherry", 1});  
    word_occurrences.insert({"apple", 4}); // 关键字"apple"第三次出现  
  
    // 查找关键字"apple"  
    auto range = word_occurrences.equal_range("apple");  
  
    // range.first 是指向第一个"apple"的迭代器  
    // range.second 是指向第一个非"apple"的迭代器(即范围之后的位置)  
    for (auto it = range.first; it != range.second; ++it) {  
        std::cout << "Word: " << it->first << ", Occurrence: " << it->second << std::endl;  
    }  
  
    return 0;  
}

在这个例子中,std::multimap::equal_range函数被用来查找所有具有给定关键字的元素。它返回一个包含两个迭代器的std::pair,其中first成员是指向第一个具有给定关键字的元素的迭代器,而second成员是指向第一个具有不同关键字的元素(即范围之后的位置)的迭代器。然后,你可以通过遍历这个范围来访问所有具有相同关键字的元素。

输出将是:

Word: apple, Occurrence: 1  
Word: apple, Occurrence: 3  
Word: apple, Occurrence: 4

 这显示了所有具有关键字"apple"的std::pair元素及其对应的值。

一种不同的,面向迭代器的解决方法

我们还可以用lower_bound和upper_bound来解决此问题。

这两个操作都接受个关键字,返回一个迭代器。

如果关键字在容器中,lower_bound返回的迭代器将指向第一个具有给定关键字的元素,而upper_bound返回的迭代器则指向最后一个匹配给定关键字的元素之后的位置。

如果元素不在multimap中,则 lower_bound和upper_bound会返回相等的迭代器——指向一个不影响排序的关键字插入位置。

因此,用相同的关键字调用lower_bound和upper_bound会得到一个迭代器范围,表示所有具有该关键字的元素的范围

当然,这两个操作返回的迭代器可能是容器的尾后迭代器。如果我们查找的元素具有容器中最大的关键字,则此关键字的upper_bound返回尾后迭代器。如果关键字不存在,且大于容器中任何关键字,则lower_bound返回的也是尾后迭代器。

lower_bound返回的迭代器可能指向一个具有给定关键字的元素,但也可能不指向。如果关键字不在容器中,则lower_bound会返回关键字的第一个安全插入点——不影响容器中元素顺序的插入位置。

下面是一个使用 lower_bound 和 upper_bound 来遍历 std::multimap 中所有具有给定关键字的元素的例子: 

#include <iostream>  
#include <map>  
  
int main() {  
    std::multimap<std::string, int> word_occurrences;  
  
    // 插入一些键值对  
    word_occurrences.insert({"apple", 1});  
    word_occurrences.insert({"banana", 2});  
    word_occurrences.insert({"apple", 3});  
    word_occurrences.insert({"cherry", 1});  
    word_occurrences.insert({"apple", 4});  
  
    // 查找关键字"apple"  
    auto lower = word_occurrences.lower_bound("apple");  
    auto upper = word_occurrences.upper_bound("apple");  
  
    // 遍历范围  
    for (auto it = lower; it != upper; ++it) {  
        std::cout << "Word: " << it->first << ", Occurrence: " << it->second << std::endl;  
    }  
  
    // 假设我们要查找一个不存在的关键字"orange"  
    lower = word_occurrences.lower_bound("orange");  
    upper = word_occurrences.upper_bound("orange");  
  
    // 因为"orange"不在multimap中,所以lower和upper将指向相同的位置  
    if (lower == upper) {  
        std::cout << "The word 'orange' does not exist in the multimap.\n";  
    }  
  
    return 0;  
}
Word: apple, Occurrence: 1  
Word: apple, Occurrence: 3  
Word: apple, Occurrence: 4  
The word 'orange' does not exist in the multimap.

在这个例子中,我们首先使用 lower_bound 和 upper_bound 来查找关键字 "apple" 的范围,并遍历这个范围以打印出所有 "apple" 的出现。接着,我们尝试查找一个不存在的关键字 "orange",由于它不在 multimap 中,所以 lower_bound 和 upper_bound 返回的迭代器是相等的,这表示 "orange" 不存在。

lower_bound 返回的迭代器确实可能指向一个具有给定关键字的元素,或者指向一个如果插入新元素则不会影响排序的位置(即第一个大于给定关键字的元素的位置,或者如果关键字大于所有现有关键字,则指向尾后迭代器)。同样,upper_bound 返回的迭代器指向的范围之后的位置。如果关键字不存在于容器中,则 lower_bound 和 upper_bound 将返回相同的迭代器。

如果lower bound和upper bound返回相同的迭代器,则给定关键字不在容器中。

equal_range函数

最后一种方法是三种方法中最直接的:不必再调用 upper_bound和lower_bound,直接调用equal_range即可。

此函数接受一个关键字,返回一个迭代器pair。

若关键字存在,则第一个迭代器指向第一个与关键字匹配的元素,第二个迭代器指向最后一个匹配元素之后的位置。

若未找到匹配元素,则两个迭代器都指向关键字可以插入的位置。

std::multimap 和 std::multiset 的 equal_range 成员函数确实是最直接的方法来查找一个范围,其中包含了所有具有给定关键字的元素。这个方法封装了 lower_bound 和 upper_bound 的功能,并返回了一个迭代器对(std::pair<iterator, iterator>),使得查找和遍历具有相同关键字的元素变得更加简洁。

下面是一个使用 equal_range 的例子:

#include <iostream>  
#include <map>  
  
int main() {  
    std::multimap<std::string, int> word_occurrences;  
  
    // 插入一些键值对  
    word_occurrences.insert({"apple", 1});  
    word_occurrences.insert({"banana", 2});  
    word_occurrences.insert({"apple", 3});  
    word_occurrences.insert({"cherry", 1});  
    word_occurrences.insert({"apple", 4});  
  
    // 查找关键字"apple"  
    auto range = word_occurrences.equal_range("apple");  
  
    // 遍历范围  
    for (auto it = range.first; it != range.second; ++it) {  
        std::cout << "Word: " << it->first << ", Occurrence: " << it->second << std::endl;  
    }  
  
    // 假设我们要查找一个不存在的关键字"orange"  
    range = word_occurrences.equal_range("orange");  
  
    // 因为"orange"不在multimap中,所以range.first和range.second将指向相同的位置  
    if (range.first == range.second) {  
        std::cout << "The word 'orange' does not exist in the multimap.\n";  
    }  
  
    return 0;  
}
Word: apple, Occurrence: 1  
Word: apple, Occurrence: 3  
Word: apple, Occurrence: 4  
The word 'orange' does not exist in the multimap.

在这个例子中,我们首先使用 equal_range 来查找关键字 "apple" 的范围,并遍历这个范围以打印出所有 "apple" 的出现。接着,我们尝试查找一个不存在的关键字 "orange",由于它不在 multimap 中,所以 equal_range 返回的迭代器对中的两个迭代器是相同的,这表示 "orange" 不存在。

使用 equal_range 通常比分别调用 lower_bound 和 upper_bound 更简洁,因为它直接返回了一个包含所需范围信息的迭代器对。

上一篇:沪深websocket level2/level1行情推送接入示例


下一篇:DS:链表的分类