【C++之map的应用】

news2024/12/24 20:49:12

C++学习笔记---021

  • C++之map的应用
    • 1、map的简单介绍
      • 1.1、基本概念
      • 1.2、map基本特性
    • 2、map的基本操作
      • 2.1、插入元素
      • 2.2、访问元素
      • 2.3、删除元素
      • 2.4、遍历map
      • 2.5、检查元素是否存在
      • 2.6、获取map的大小
      • 2.7、清空map
      • 2.8、基本样例
    • 3、map的基础模拟实现
    • 4、测试用例
      • 4.1、插入和遍历
      • 4.2、查找和删除
      • 4.3、统计次数
      • 4.4、[ ]的插入
      • 4.5、以[ ]再谈统计计数
      • 4.6、equal_range
    • 5、map的性能分析
    • 6、练习题
      • 6.1、前K个高频单词
      • 6.2、单词识别

C++之map的应用

前言:
前面篇章学习了C++对于set和multiset的基本应用的知识认识和了解,接下来继续学习,C++的map等知识。
/知识点汇总/

1、map的简单介绍

1.1、基本概念

在C++中,map是一个标准模板库(STL)中的关联容器,它包含可以重复的键值对集合,其中每个键都是唯一的,并与一个值相关联。
map通常以一个红黑树作为其内部数据结构,这确保了键值对的自动排序和高效的插入、删除和搜索操作。

格式:

std::map<KeyType, ValueType> variable_name;

1.2、map基本特性

1.键唯一性:map中的每个键都是唯一的,不允许有重复的键。
2.自动排序:map中的元素默认按照键的升序进行排序,这是因为它通常使用红黑树来实现。
3.关联容器:通过键来访问对应的值,而不是通过位置或索引。
4.动态大小:map的大小可以根据需要动态地增长或缩小。

2、map的基本操作

2.1、插入元素

方法一:

variable_name.insert(std::pair<KeyType, ValueType>(key, value));

方法二:

variable_name.insert({key, value}); // C++11及更高版本

方法三:

variable_name[key] = value;

2.2、访问元素

方法一:

ValueType& value_ref = variable_name[key];

方法二:

auto it = variable_name.find(key);  
if (it != variable_name.end()) {  
    ValueType& value_ref = it->second;  
    // 使用value_ref  
}

2.3、删除元素

方法一:

variable_name.erase(key); // 删除键为key的元素

方法二:

auto it = variable_name.find(key);  
if (it != variable_name.end()) {  
    variable_name.erase(it);  
}

2.4、遍历map

方法一:使用迭代器

for (auto it = variable_name.begin(); it != variable_name.end(); ++it) {  
    KeyType key = it->first;  
    ValueType value = it->second;  
    // 使用key和value  
}

方法二:基于范围的for循环(C++11及更高版本)

for (const auto& pair : variable_name) {  
    KeyType key = pair.first;  
    ValueType value = pair.second;  
    // 使用key和value  
}

2.5、检查元素是否存在

if (variable_name.find(key) != variable_name.end()) {  
    // 键存在  
}

2.6、获取map的大小

size_t size = variable_name.size();

2.7、清空map

variable_name.clear();

2.8、基本样例

#include <iostream>  
#include <map>  
int main()
{  
    std::map<std::string, int> ages;  
  
    ages["Alice"] = 30;  
    ages["Bob"] = 25;  
    ages.insert({"Charlie", 35});  
  
    for (const auto& pair : ages) 
    {  
        std::cout << pair.first << ": " << pair.second << std::endl;  
    }  
    ages.erase("Bob");    
    return 0;  
}

3、map的基础模拟实现

#include <iostream>  
#include <vector>  
#include <utility> // for std::pair  
  
template <typename Key, typename Value>  
class SimpleMap {  
private:  
    std::vector<std::pair<Key, Value>> elements;  
  
    // 简单的线性搜索函数  
    size_t findIndex(const Key& key) const {  
        for (size_t i = 0; i < elements.size(); ++i) {  
            if (elements[i].first == key) {  
                return i;  
            }  
        }  
        return std::vector<std::pair<Key, Value>>::npos; // 返回一个特殊值表示未找到  
    }  
  
public:  
    // 插入元素  
    void insert(const Key& key, const Value& value) {  
        size_t index = findIndex(key);  
        if (index == std::vector<std::pair<Key, Value>>::npos) {  
            elements.push_back(std::make_pair(key, value));  
        } else {  
            // 如果键已存在,可以选择更新值或不做任何操作(这里选择更新值)  
            elements[index].second = value;  
        }  
    }  
  
    // 查找元素  
    bool find(const Key& key, Value& value) const {  
        size_t index = findIndex(key);  
        if (index != std::vector<std::pair<Key, Value>>::npos) {  
            value = elements[index].second;  
            return true;  
        }  
        return false;  
    }  
  
    // 删除元素  
    bool erase(const Key& key) {  
        size_t index = findIndex(key);  
        if (index != std::vector<std::pair<Key, Value>>::npos) {  
            elements.erase(elements.begin() + index);  
            return true;  
        }  
        return false;  
    }  
  
    // ... 可以添加其他成员函数,如size(), clear()等  
};  
  
int main() {  
    SimpleMap<std::string, int> myMap;  
    myMap.insert("Alice", 30);  
    myMap.insert("Bob", 25);  
  
    int value;  
    if (myMap.find("Alice", value)) {  
        std::cout << "Alice's age is " << value << std::endl;  
    }  
  
    myMap.erase("Bob");  
  
    // ... 其他操作  
  
    return 0;  
}

4、测试用例

4.1、插入和遍历

void test_map1()
{
	map<string, string> dict;
	pair<string, string> kv1("sort", "排序");//隐式类型转换
	dict.insert(kv1);
	dict.insert(pair<string, string>("left", "左边"));
	dict.insert(make_pair("right", "左边"));

	//pair<string, string> kv2 = {"sort", "排序"};//隐式类型转换
	dict.insert({"insert","插入"});//隐式类型转换
	//initializer_list
	map<string, string> dict2 = { {"sort", "排序"},{"left", "左边"},{"right", "左边"} };

	map<string, string>::iterator it = dict.begin();
	//auto it = dict.begin();
	while(it != dict.end())
	{
		//cout << *it << endl;//error
		//因为pair<string, string> --- 二元的

		//cout << (*it).first << " : " << (*it).second << endl;
		//等价
		cout << it->first << " : " << it->second << endl;
		//等价原型
		//cout << it.operator->()->first << " : " << it.operator->()->second << endl;
		++it;

		//iterator key不能修改,value可修改
		//const_iterator key和value都不能修改

	}
	cout << endl;

	//范围for
	for (auto& kv : dict)
	{
		cout << kv.first << ":" << kv.second << endl;
	}

	//了解一些C++17的写法 -- 编译器改配置即可
	//for (auto& [x,y] : dict)
	//{
	//	cout << x << ":" << y << endl;
	//}
}

4.2、查找和删除

void test_map2()
{
	// 创建一个空的map  
	std::map<std::string, int> myMap;
	// 插入元素  
	myMap["apple"] = 1;
	myMap["banana"] = 2;
	myMap["cherry"] = 3;
	// 遍历map  
	for (const auto& pair : myMap)
	{
		std::cout << pair.first << ": " << pair.second << std::endl;
	}
	// 查找元素  
	if (myMap.find("banana") != myMap.end())
	{
		std::cout << "Found banana with value: " << myMap["banana"] << std::endl;
	}
	else
	{
		std::cout << "Banana not found" << std::endl;
	}
	// 删除元素  
	myMap.erase("cherry");
	// 再次遍历map,以查看"cherry"是否已被删除  
	for (const auto& pair : myMap)
	{
		std::cout << pair.first << ": " << pair.second << std::endl;
	}
}

4.3、统计次数

void test_map3()
{
	// 统计水果出现的次数
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
   "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& e : arr)
	{
		auto it = countMap.find(e);
		if (it != countMap.end())
		{
			it->second++;
		}
		else
		{
			//const pair<string,int>& val = {e,1};
			//pair<string,bool> insert(const value_type& val);
			//key存在,插入失败,返回->pair<存在的key所在节点的迭代器,flase>
			//key不存在,插入成功,返回->pair<新插入key所在节点的迭代器,true>
			countMap.insert({ e,1 });
		}
	}
	for (auto& kv : countMap)
	{
		//auto& [x, y] = kv;
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
}

4.4、[ ]的插入

补充:[ ]的底层拆解理解

 V& operator[](const k& key)
 {
		return (*((this->insert(make_pair(k,mapped_type()))).first)).sencond;
 }
V& operator[](const k& key)
{
	//不管是插入成功还是失败,pair中iterator始终指向key所在节点的iterator
	pair<iterator, bool> ret = this->insert(make_pair(key, v()));
	iterator it = ret.first;
	retrun it->second;
}
void test_map4()
{
	map<string, string> dict;
	dict.insert({ "string","字符串" });
	//插入
	dict["right"];
	//插入+修改
	dict["left"] = "左边";
	//查找(已存在就是查找)
	cout << dict["string"] << endl;
	//修改
	dict["right"] = "右边";

	string str;
	cin >> str;
	//size_type count(const key_type& k) const;返回个数
	//计数特定元素的个数
	if (dict.count(str))
	{
		cout << "在" << endl;
	}
	else
	{
		cout << "不在" << endl;
	}
	//multimap与map还有一个区别,multimao没有[]
}

4.5、以[ ]再谈统计计数

void test_map5()
{
	// 统计水果出现的次数
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜",
   "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& e : arr)
	{
		countMap[e]++;//插入+修改
	}
	for (auto& kv : countMap)
	{
		//auto& [x, y] = kv;
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;

	//map<int, string> sortMap;
	multimap<int, string> sortMap;
	for (auto& kv : countMap)
	{
		//数据丢失,本质原因就是multimap和map的区别,因为对于map中已经已经存在的key会被覆盖造成丢失。
		//sortMap[kv.second] = kv.first;
		sortMap.insert({ kv.second ,kv.first });
	}
	cout << endl;
	for (auto& kv : sortMap)
	{
		//auto& [x, y] = kv;
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
}

4.6、equal_range

equal_range
获取相等元素的范围
返回一个范围的边界,该范围包括容器中具有等价于k的键的所有元素。
它返回一个范围,该范围包含所有与给定键相等的元素。
作用:
equal_range返回一个包含两个迭代器的pair:
first迭代器指向范围中的第一个元素(如果存在)。
second迭代器指向范围之后的第一个元素(即大于给定键的第一个元素),或者如果范围内没有更多的元素,则指向容器的end()。
这个函数的主要用途是当你需要查找一个键的所有出现,或者当你需要在一个有序范围内进行迭代时。

void test_map7()
{
	map<char, int> mymap;

	mymap['a'] = 10;
	mymap['b'] = 20;
	mymap['c'] = 30;
	mymap['c'] = 40;
	mymap['c'] = 50;
	mymap['f'] = 60;
	pair<map<char, int>::iterator, map<char, int>::iterator> ret;
	ret = mymap.equal_range('c');

	cout << "lower bound points to: ";
	cout << ret.first->first << " => " << ret.first->second << '\n';

	cout << "upper bound points to: ";
	cout << ret.second->first << " => " << ret.second->second << '\n';
}

5、map的性能分析

时间复杂度

1.插入(Insertion):在 std::map 中插入一个元素的时间复杂度是 O(log n),其中 n 是 map 中元素的数量。这是因为红黑树在插入时需要重新平衡树结构以保持其性质。
2.查找(Search):查找一个元素的时间复杂度同样是 O(log n)。在红黑树中,查找操作通过树的层次结构来减少搜索空间,从而实现对数时间复杂度。
3.删除(Deletion):删除一个元素的时间复杂度也是 O(log n)。删除操作同样需要保持红黑树的性质,这可能需要重新平衡树结构。

空间复杂度

std::map 的空间复杂度是线性的,即 O(n),其中 n 是 map
中元素的数量。这是因为每个元素都需要在树中存储一个节点,并且这些节点需要额外的空间来存储键和值,以及指向其子节点的指针。

6、练习题

6.1、前K个高频单词

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

思路:用<单词,单词出现次数>构建键值对,然后将vector中的单词放进去,统计每个单词出现的次数

#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <utility>
#include <algorithm>

using namespace std;

class Solution {
public:
    class KVCompare {
    public:
        // 在set中进行排序时的比较规则
        bool operator()(const pair<string, int>& left, const pair<string, int>& right)
        {
            return left.second > right.second; // 升序:>  降序:<
        }
    };
    vector<string> topKFrequent(vector<string>& words, int k) {
        // 用<单词,单词出现次数>构建键值对,然后将vector中的单词放进去,统计每个单词出现的次数
        map<string, int> countMap;
        for (auto& e : words)
        {
            countMap[e]++;
        }
        vector<pair<string, int>> v(countMap.begin(), countMap.end());
        //sort(v.begin(), v.end(), KVCompare());
        stable_sort(v.begin(), v.end(), KVCompare());
        vector<string> vRet;
        for (size_t i = 0; i < k; i++) {
            vRet.push_back(v[i].first);
        }
        return vRet;
    }
};

int main()
{
    Solution solution;

    vector<string> words = { "the", "sky", "is", "blue", "the", "sun", "is", "bright", "the", "sun", "is", "shiny", "then", "the", "sky", "became", "even", "more", "blue" };
    int k = 4;

    vector<string> topK = solution.topKFrequent(words, k);

    // 输出结果  
    cout << "Top " << k << " frequent words are: ";
    for (const auto& word : topK) {
        cout << word << " ";
    }
    cout << endl;
    return 0;
}

输出结果:
在这里插入图片描述

6.2、单词识别

输入一个英文句子,把句子中的单词(不区分大小写)按出现次数按从多到少把单词和次数在屏幕上输出来,次数一样的按照单词小写的字典序排序输出,要求能识别英文单词和句号。

思路:思路是先截取单词,通过map记录单词个数,再转存vector进行排序。

#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef pair<string, int> Word;
bool cmp(Word w1, Word w2)
{
    return w1.second > w2.second;
}
int main()
{
    map<string, int> mp;
    string s;
    while (getline(cin, s))
    {
        for (int i = 0, j = 0; i < s.size(); i++)
        {
            if (s[i] == ' ' || s[i] == '.')
            {
                string t = s.substr(j, i - j);
                if (isupper(t[0]))
                    t[0] = tolower(t[0]);
                j = i + 1;
                mp[t]++;
            }
        }
        vector<Word> v(mp.begin(), mp.end());
        sort(v.begin(), v.end(), cmp);
        for (int i = 0; i < v.size(); i++)
            cout << v[i].first << ":" << v[i].second << endl;
    }
    return 0;
}

输出结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1646818.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python查询PostgreSQL数据库

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; Python与PostgreSQL的连接 需要了解如何在Python中连接到PostgreSQL数据库。这通常涉及到使用一个库&#xff0c;如psycopg2&#xff0c;它是Python中用于PostgreSQL的最流行的适配器。安装psycopg2非常简单&#x…

[Java EE] 多线程(八):CAS问题与JUC包

&#x1f338;个人主页:https://blog.csdn.net/2301_80050796?spm1000.2115.3001.5343 &#x1f3f5;️热门专栏:&#x1f355; Collection与数据结构 (90平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm1001.2014.3001.5482 &#x1f9c0;Java …

用 Go map 要注意这个细节,避免依赖他!

有的小伙伴没留意过 Go map 输出、遍历顺序&#xff0c;以为它是稳定的有序的&#xff0c;会在业务程序中直接依赖这个结果集顺序&#xff0c;结果栽了个大跟头&#xff0c;吃了线上 BUG。 有的小伙伴知道是无序的&#xff0c;但却不知道为什么,有的却理解错误&#xff1f; 今…

Flutter笔记:Widgets Easier组件库 - 使用标签(Tag)

Flutter笔记 Widgets Easier组件库 - 使用标签&#xff08;Tag&#xff09; - 文章信息 - Author: 李俊才 (jcLee95) Visit me at CSDN: https://jclee95.blog.csdn.netMy WebSite&#xff1a;http://thispage.tech/Email: 291148484163.com. Shenzhen ChinaAddress of this …

滑动窗口 | 1652. 拆炸弹 |LeetCode

文章目录 题目介绍暴力(可以过力扣竟然。不愧是简单题)&#xff1a;滑动窗口 祝你天天开心 题目介绍 你有一个炸弹需要拆除&#xff0c;时间紧迫&#xff01;你的情报员会给你一个长度为 n 的 循环 数组 code 以及一个密钥 k 。 为了获得正确的密码&#xff0c;你需要替换掉每…

【嵌入式笔试题】进程线程笔试题

非常经典的笔试题。 1.进程&线程(16道) 1.1异步IO和同步IO区别? 答案:如果是同步IO,当一个IO操作执行时,应用程序必须等待,直到此IO执行完。 相反,异步IO操作在后台运行,IO操作和应用程序可以同时运行,提高系统性能,提 高IO流量。 解读:在同步文件IO中,线…

IntelliJ IDEA 2024 for Mac:Java开发者的强大助手

IntelliJ IDEA 2024 for Mac是Java开发者不可或缺的强大助手&#xff0c;它凭借卓越的性能和丰富的功能&#xff0c;赢得了广大开发者的青睐。 作为集成开发环境&#xff08;IDE&#xff09;的佼佼者&#xff0c;IDEA 2024提供了全面的代码编辑和智能提示功能。它不仅能实时分析…

视频素材哪个软件好用?8个短视频素材高清无水印

在今日这个视觉表现至关重要的时代&#xff0c;获取合适的视频素材成为制作任何类型视频内容的基石。从企业宣传片到社交媒体短视频&#xff0c;高质量的视频素材能够显著提升内容的吸引力和专业度。这里列出了一些全球顶尖的视频素材平台&#xff0c;每一个都能为您的视频项目…

高效转化,智能私信软件策略揭秘

在数字营销的浪潮中&#xff0c;智能私信软件策略正成为提升转化率的重要工具。这种软件以其个性化、自动化的特点&#xff0c;正在重新定义与客户的互动方式&#xff0c;让企业能够更加高效地吸引并留住潜在客户。 智能私信软件的核心在于其高度的定制化和人性化设计。通过大数…

CasaOS玩客云安装memos开源云笔记并实现随时随地远程记笔记

文章目录 前言1. 使用Docker部署memos2. 注册账号与简单操作演示3. 安装cpolar内网穿透4. 创建公网地址5. 创建固定公网地址 前言 本文主要介绍如何在CasaOS玩客云&#xff0c;使用Docker本地部署21.6K stars的热门开源云笔记服务memos&#xff0c;并结合cpolar内网穿透工具打…

Llama3-Tutorial之XTuner微调Llama3个人小助手

Llama3-Tutorial之XTuner微调Llama3个人小助手 使用XTuner微调llama3模型。 参考&#xff1a; https://github.com/SmartFlowAI/Llama3-Tutorial 1. web demo部署 参考上一节内容已经完成web demo部署&#xff0c;进行对话测试, 当前回答基于llama3官方发布的模型进行推理生成&…

HCIP-Datacom-ARST必选题库_BGP【道题】

1.关于summary automatic命令和BGP聚合的描述,错误的是? 该命令用于实现自动聚合,其优先级高于手动聚合 配置该命令后,BGP将按自然网段聚合路由 该命令用来使能对本地引入的路由进行自动聚合 配置该命令后,BGP只向对等体发送聚合后的路由 1.关于summary automatic命令和BGP聚…

基于51单片机的手动数字时钟设计

基于51单片机的手动数字时钟 &#xff08;仿真&#xff0b;程序&#xff09; 功能介绍 具体功能&#xff1a; 1.八位数码管显示时分秒&#xff0c;格式为XX-XX-XX&#xff1b; 2.六个按键控制时、分、秒的加减&#xff1b; 3.复位按键重新计时&#xff1b; ​演示视频&am…

文件加密软件排行榜前五:好用的文件加密软件推荐

后台有很多老板留言&#xff0c;说最近机密数据外泄的事情频发&#xff0c;让自己开始有了危机意识&#xff0c;想要提前针对企业安全问题采取措施&#xff0c;比方说选一款适合防泄密软件&#xff0c;但是不知道如何选择。 下面介绍几款软件&#xff0c;让大家了解一下市面上常…

Python中的函数定义(def)详解

Python中的函数定义&#xff08;def&#xff09;详解 在编程语言中&#xff0c;函数是组织代码的一种方式&#xff0c;它们可以帮助我们将复杂的程序拆分为简单、易管理的部分。在Python中&#xff0c;函数的定义使用def关键字。 什么是函数&#xff1f; 函数是一段完成特定…

腾讯云服务器 宝塔面板部署小程序和后台教程

文章目录 目录 文章目录 安装流程 小结 概要部署流程技术细节小结 概要 本次的部署准备了3个域名&#xff0c;都是从二级域名映射出3个三级域名&#xff0c;域名注册可以在3大互联网官网购买一个域名就行。并且备案审核这些比较花费时间一般需要15工作日 部署流程 宝塔面板的…

设计模式Java实现-工厂模式

✨这里是第七人格的博客✨小七&#xff0c;欢迎您的到来~✨ &#x1f345;系列专栏&#xff1a;设计模式&#x1f345; ✈️本篇内容: 工厂模式✈️ &#x1f371;本篇收录完整代码地址&#xff1a;https://gitee.com/diqirenge/design-pattern &#x1f371; 楔子 记得刚…

JVM之内存分配的详细解析

内存分配 两种方式 不分配内存的对象无法进行其他操作&#xff0c;JVM 为对象分配内存的过程&#xff1a;首先计算对象占用空间大小&#xff0c;接着在堆中划分一块内存给新对象 如果内存规整&#xff0c;使用指针碰撞&#xff08;Bump The Pointer&#xff09;。所有用过的内…

快速入门!学习鸿蒙App开发的终极指南!

鸿蒙&#xff08;HarmonyOS&#xff09;是华为推出的一款分布式操作系统&#xff0c;旨在为不同设备提供统一的操作体验。鸿蒙App开发可以让应用程序在多个设备上实现流畅运行。本文将介绍鸿蒙App开发的终极指南&#xff0c;帮助您快速入门。 开发环境搭建 鸿蒙App开发过程需要…

R语言Rstudio突然无法启动?如何解决

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…