C++ STL: map 和 set 的应用与实践

目录

  1. 引言
  2. 基础知识
  3. 应用场景
  4. 高级技巧
  5. 性能考量
  6. 总结
  7. 参考文献

引言

在 C++ 标准模板库 (STL) 中,mapset 是两种非常有用的容器,它们提供了高效的键值对存储和集合操作能力。map 用于存储键值对,而 set 用于存储唯一键的集合。这两种容器都基于红黑树实现,这意味着它们的大多数操作(如查找、插入和删除)的时间复杂度都是 O(log N)。本文将介绍 mapset 的基本用法以及在实际编程中的应用案例。


基础知识

map

map 是一个关联容器,用于存储键值对。键必须是唯一的,而值可以是任何类型。map 的元素按照键的升序排序。

示例代码
#include <iostream>
#include <map>

int main() {
    std::map<std::string, int> fruits;
    fruits["apple"] = 3;
    fruits["banana"] = 2;
    fruits["cherry"] = 5;

    for (const auto& fruit : fruits) {
        std::cout << fruit.first << ": " << fruit.second << std::endl;
    }

    return 0;
}

set

set 也是一个关联容器,但它只存储唯一的键。这些键默认按升序排序。

示例代码
#include <iostream>
#include <set>

int main() {
    std::set<int> numbers;
    numbers.insert(3);
    numbers.insert(1);
    numbers.insert(2);
    numbers.insert(4);

    for (const auto& number : numbers) {
        std::cout << number << " ";
    }
    std::cout << std::endl;

    return 0;
}

应用场景

词频统计

词频统计是自然语言处理中的一项常见任务。map 可以用来高效地统计文本中单词的出现频率。

示例代码
#include <iostream>
#include <map>
#include <sstream>
#include <string>

int main() {
    // 定义一个字符串,用于存储文本
    std::string text = "The quick brown fox jumps over the lazy dog";

    // 使用istringstream来分割文本中的单词
    std::istringstream iss(text);

    // 定义一个map,用于存储单词及其出现的次数
    std::map<std::string, int> wordCount;

    // 读取文本中的每一个单词
    std::string word;
    while (iss >> word) {
        // 对于每一个单词,将其出现次数加一
        ++wordCount[word];
    }

    // 输出每个单词及其出现的次数
    for (const auto& pair : wordCount) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

去重和排序

set 可以用来去重并自动排序一组数据。

示例代码
#include <iostream>
#include <set>
#include <vector>

int main() {
    // 定义一个包含重复数字的向量
    std::vector<int> numbers = {4, 1, 2, 4, 3, 2, 1};

    // 使用set来去重并排序这些数字
    std::set<int> uniqueNumbers(numbers.begin(), numbers.end());

    // 输出去重并排序后的数字
    for (const auto& number : uniqueNumbers) {
        std::cout << number << " ";
    }
    std::cout << std::endl;

    return 0;
}

动态排名

在比赛中,经常需要实时更新参赛者的排名。map 可以用来存储参赛者及其得分,并根据得分进行排序。

示例代码
#include <iostream>
#include <map>
#include <string>

int main() {
    // 定义一个map,用于存储参赛者的名字和他们的分数
    std::map<std::string, int> scores;
    scores["Alice"] = 100;
    scores["Bob"] = 80;
    scores["Charlie"] = 90;

    // 输出每个参赛者的名字和分数
    for (const auto& pair : scores) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

图算法中的应用

在图算法中,setmap 都是非常有用的工具。例如,在 Dijkstra 算法中,set 可以用来维护待处理的顶点集合,而 map 可以用来存储顶点到源顶点的距离。

示例代码
#include <iostream>
#include <map>
#include <set>
#include <utility>

// 定义边的数据结构
struct Edge {
    int destination; // 目的地顶点
    int weight;      // 边的权重
};

int main() {
    // 定义一个图,使用map来存储每个顶点及其相邻的顶点和权重
    std::map<int, std::vector<Edge>> graph;
    graph[0] = {{1, 1}, {2, 4}}; // 顶点0连接顶点1和2
    graph[1] = {{2, 2}};         // 顶点1连接顶点2
    graph[2] = {};               // 顶点2没有出边

    // 定义一个map,用于存储每个顶点到源顶点的距离
    std::map<int, int> distance;

    // 定义一个set,用于存储待处理的顶点
    std::set<std::pair<int, int>> unvisited;
    unvisited.insert({0, 0}); // 将源顶点0及其距离0加入待处理顶点集
    distance[0] = 0;          // 源顶点到自身的距离为0

    // Dijkstra算法的主要循环
    while (!unvisited.empty()) {
        // 获取待处理顶点集中距离最短的顶点
        auto minDist = *unvisited.begin();
        unvisited.erase(unvisited.begin()); // 从待处理顶点集中移除该顶点
        int currentNode = minDist.second;   // 获取当前顶点

        // 遍历当前顶点的所有出边
        for (const auto& edge : graph[currentNode]) {
            int newDistance = distance[currentNode] + edge.weight; // 计算新距离
            if (newDistance < distance[edge.destination]) {       // 如果新距离更短
                distance[edge.destination] = newDistance;          // 更新距离
                unvisited.insert({newDistance, edge.destination}); // 加入待处理顶点集
            }
        }
    }

    // 输出每个顶点到源顶点的距离
    for (const auto& pair : distance) {
        std::cout << pair.first << ": " << pair.second << std::endl;
    }

    return 0;
}

高级技巧

自定义比较器

mapset 支持自定义比较器,这样可以根据特定需求对元素进行排序。

示例代码
#include <iostream>
#include <set>
#include <string>

// 定义一个比较器,用于根据字符串的长度进行比较
struct CustomComparator {
    bool operator()(const std::string& lhs, const std::string& rhs) const {
        return lhs.length() < rhs.length(); // 如果左侧字符串长度小于右侧,则返回true
    }
};

int main() {
    // 定义一个set,使用自定义的比较器
    std::set<std::string, CustomComparator> words;
    words.insert("the");
    words.insert("quick");
    words.insert("brown");
    words.insert("fox");

    // 输出按照字符串长度排序后的单词
    for (const auto& word : words) {
        std::cout << word << " ";
    }
    std::cout << std::endl;

    return 0;
}

多值映射

multimapmap 的变体,它允许相同的键对应多个值。

示例代码
#include <iostream>
#include <multimap>
#include <string>

int main() {
    // 定义一个多值映射,允许相同的键对应多个值
    std::multimap<std::string, int> fruits;
    fruits.insert({"apple", 3});
    fruits.insert({"apple", 5});
    fruits.insert({"banana", 2});

    // 输出每个键及其对应的值
    for (const auto& fruit : fruits) {
        std::cout << fruit.first << ": " << fruit.second << std::endl;
    }

    return 0;
}

性能考量

mapset 的性能主要取决于底层使用的数据结构。由于它们基于红黑树实现,因此大多数操作的时间复杂度为 O(log N)。这意味着随着容器中元素数量的增加,操作时间的增长速度较慢。

在需要频繁插入和删除操作时,mapset 仍然能保持良好的性能。然而,在需要频繁访问元素时,如果元素的分布很密集,那么使用哈希表(如 unordered_mapunordered_set)可能会提供更好的性能。


总结

本文介绍了 C++ 中 mapset 的基本用法以及在实际编程中的应用案例。通过这些例子,我们看到了 mapset 在词频统计、去重和排序、动态排名以及图算法等方面的应用。此外,本文还讨论了自定义比较器和 multimap 的使用技巧,以及在性能方面的考量。

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

安大小万

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值