【C++之map的应用】

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.mfbz.cn/a/597072.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unreal游戏GPU性能优化检测模式全新上线

UWA已经在去年推出了针对于Unity项目的GPU性能优化工具&#xff0c;通过对GPU渲染性能、带宽性能以及各种下探指标&#xff0c;帮助Unity项目研发团队定位由GPU导致的发热耗电问题。这个需求在Unreal团队中也极为强烈&#xff0c;因此UWA将该功能移植到针对Unreal项目的GOT Onl…

react + xlsx 表格导出功能 全部实现

需求 : 在react中将表格多样化导出 , 既可以全部导出所有表格数据 , 也可以选择性导出 导出可以选择三种样式 选择了全部 , 不能选其他 全部导出 部分导出 1 导出按钮下拉弹出三种导出格式 <Dropdownmenu{{items: [{label: (<aonClick{() > {setFormat(xlsx)}}>…

零基础编程学python:如何从零开始学习并使用Python编程语言

零基础编程学python&#xff1a;如何从零开始学习并使用Python编程语言 Python是一种非常流行的编程语言&#xff0c;由于其简单的语法和强大的功能&#xff0c;使其成为初学者和专业开发者的首选。无论您是数据科学家、网络开发者还是自动化工程师&#xff0c;Python都能提供必…

Excel利用数据透视表将二维数据转换为一维数据(便于后面的可视化分析)

一维数据&#xff1a;属性值都不可合并&#xff0c;属性值一般在第一列或第一行。 二维数据&#xff1a;行属性或列属性是可以继续合并的&#xff0c;如下数据中行属性可以合并为【月份】 下面利用数据透视表将二维数据转换为一维数据&#xff1a; 1、在原来的数据上插入数据透…

(论文阅读-优化器)Selectivity Estimation using Probabilistic Models

目录 摘要 一、简介 二、单表估计 2.1 条件独立Condition Independence 2.2 贝叶斯网络Bayesian Networks 2.3 查询评估中的贝叶斯网络 三、Join选择性估计 3.1 两表Join 3.2 概率关系模型 3.3 使用PRMs的选择性估计 四、PRM构建 4.1 评分标准 4.2 参数估计 4.3 结…

Adobe Illustrator 2024 for Mac:矢量图形设计软件

Adobe Illustrator 2024 for Mac是一款专为Mac用户设计的行业标准矢量图形设计软件。该软件以其卓越的性能和丰富的功能&#xff0c;为设计师和艺术家们提供了一个全新的创意空间。 作为一款矢量图形软件&#xff0c;Adobe Illustrator 2024 for Mac支持创建高质量的矢量图形&a…

Docker 的网络实现

简介 标准的 Docker 支持以下 4 类网络模式&#xff1a; 1&#xff09;host 模式&#xff1a;使用 --nethost 指定 2&#xff09;container 模式&#xff1a;使用–netcontainer:NAME_or_ID 指定 3&#xff09;none模式&#xff1a;使用 --netnone 指定 4&#xff09;bridge 模…

BEV下统一的多传感器融合框架 - FUTR3D

BEV下统一的多传感器融合框架 - FUTR3D 引言 在自动驾驶汽车或者移动机器人上&#xff0c;通常会配备许多种传感器&#xff0c;比如&#xff1a;光学相机、激光雷达、毫米波雷达等。由于不同传感器的数据形式不同&#xff0c;如RGB图像&#xff0c;点云等&#xff0c;不同模态…

JavaScript注释规范

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃 &#xff0c;大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端基础路线”&#xff0c;可获…

基于C++基础知识的循环语句

一、while循环 while循环语句形式如下&#xff1a; while(表达式){语句 } 循环每次都是执行完语句后回到表达式处重新开始判断&#xff0c;重新计算表达式的值&#xff0c;一旦表达式的值为假就退出循环。用花括号括起来的多条简单语句&#xff0c;花括号及其包含的语句被称…

ContEA阅读笔记

Facing Changes: Continual Entity Alignment for Growing Knowledge Graphs 面对变化&#xff1a;不断增长的知识图谱的持续实体对齐 Abstract 实体对齐是知识图谱(KG)集成中一项基本且重要的技术。多年来&#xff0c;实体对齐的研究一直基于知识图谱是静态的假设&#xff…

Day 41 343.整数拆分 96.不同的二叉搜索树

整数拆分 给定一个正整数 n&#xff0c;将其拆分为至少两个正整数的和&#xff0c;并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 示例 1: 输入: 2输出: 1解释: 2 1 1, 1 1 1。 示例 2: 输入: 10输出: 36解释: 10 3 3 4, 3 3 4 36。说明: 你可以假设 …

Java基础教程 - 5 数组

更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 更好的阅读体验&#xff1a;点这里 &#xff08; www.doubibiji.com &#xff09; 5 数组 前面我们保存数据…

前端基础学习html(1)

1.标题标签.h1,h2...h6 2.段落标签p 换行标签br 3.加粗strong(b) /倾斜em(i) /删除 del(s) /下划线ins(u) 4.盒子&#xff1a;div //一行一个 span//一行多个 5.img :src alt title width height border 图片src引用&#xff1a;相对路径 上级/同级/中级 绝对路径&#xff…

直播话术核心逻辑,学了轻松提高销量!沈阳直播运营培训

直播话术到底该怎么说&#xff1f; 产品话术说得好&#xff0c;直播间一次就能卖出去上万件产品&#xff1b;产品话术说不好&#xff0c;直播间半个月也卖不出去10件产品。 我们上次就有跟大家说过产品话术的具体流程&#xff0c;但发现还有更多朋友居然还是不能够很好地完成一…

2024/5/6 QTday1

自由发挥应用场景&#xff0c;实现登录界面。 要求&#xff1a;尽量每行代码都有注释。 #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口相关设置this->resize(350,470);this->setFixedSize(350,470);//窗口标题this-&g…

一个简单的仓库出入库管理软件的流程是什么样的?有哪些功能?

身为仓库文员&#xff0c;我深知仓库管理对于公司运营的重要性。仓库是公司物资的中转站&#xff0c;其管理的好坏直接关系到公司的运营效率和成本控制。然而&#xff0c;传统的仓库管理方式往往存在着效率低下、易出错等问题&#xff0c;为了解决这些问题&#xff0c;我们需要…

uboot图形界面配置

文章目录 一、环境安装二、配置默认项2.图形界面 三、图形配置项的来源1.mainmenu主界面 一、环境安装 &#x1f4a6;uboot 或 Linux 内核可以通过输入“make menuconfig”来打开图形化配置界面&#xff0c;menuconfig是一套图形化的配置工具&#xff0c;需要 ncurses 库支持。…

2024年电工杯数学建模竞赛A题B题思路代码分享

您的点赞收藏是我继续更新的最大动力&#xff01; 欲获取更多电工杯学习资料&#xff0c;可点击如下卡片链接 点击链接加入群聊【2024电工杯】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&k_PrjarulWZU8JsAOA9gnj_oHKIjFe195&authKeySbv2XM853pynlnXiv6M58…

解决github的remote rejected|git存储库的推送保护

前言 git存储库的推送保护。当你试图推送代码到GitHub仓库时&#xff0c;由于存在与主分支&#xff08;master&#xff09;相关的仓库规则违规行为&#xff0c;推送会被拒绝了。这种保护机制帮助确保只有经过授权和符合规定的代码才能被合并到主分支&#xff0c;从而保护了主分…
最新文章