c++ Map和题目

c++ map

1.dev-c++ 使用c++11的设置方法

在这里插入图片描述

2.map基本用法

简介

构造:

函数原型功能
map<T1, T2> mp;map默认构造函数。
map(const map &mp);拷贝构造函数。

赋值:(可以写赋值等号=)

函数原型功能
map& operator=(const map &mp);重载等号操作符。

map是STL的一个关联容器,它提供一对一的映射。

  • 第一个可以称为关键字(key),每个关键字只能在map中出现一次;first
  • 第二个可能称为该关键字的值(value);second

map以模板(泛型)方式实现,可以存储任意类型的数据,包括使用者自定义的数据类型。

map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能, ( 后面有个题目会见识到 )

map和multimap区别:

  • map不允许容器中有重复key值元素
  • multimap允许容器中有重复key值元素

使用步骤

1.引入头文件#include<map>
2.定义一个map

这里只来一个简单的定义(样例) map<int, string> mymap;

定义:map<类型1,类型2> 名字; 类型1,2可以是自定义类型,自定义类型的话有注意事项,下面会有介绍;

这样就定义了一个map了,键值对就是int—>string,mymap里面的元素就是这样的键值对。

3.使用

遍历

我们先不管怎么插入的,直接来看看遍历:

#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
	map<int, string> mymap;
	//数组形式插入
	mymap[1] = "田小锋";
	mymap[0] = "田小锋二号";
	mymap[-1] = "田小锋三号";
	
	//普通遍历
	cout << "普通遍历\n";
    //嫌太长了,可以typedef map<int, string>::iterator Iter;起个别名
	for (map<int, string>::iterator it = mymap.begin(); it != mymap.end(); it++ ) {
		cout << it->first << "-->" << it->second << endl;
	}
	
	cout << endl;
	//c++11遍历
	cout << "c++11遍历\n";
	for(auto &temp : mymap ) {
		cout << temp.first << "-->" << temp.second << endl;
	}
	
	return 0;
}

在这里插入图片描述

map的迭代器指向的是一个键值对,it->first 是key,it->second是value.

插入

根据上面的例子,可知,map是根据key来排序的( 所以我们的key一定要可以比较 )

1.数组形式插入

map<int, string> mymap;
//数组形式插入
mymap[1] = "田小锋";
mymap[0] = "田小锋二号";
mymap[-1] = "田小锋三号";
mymap[1] = "txf"; //按顺序来这条语句会覆盖上面的"田小锋";


map<string, string> mymap;
//数组形式插入
mymap["txf"] = "田小锋";
mymap["txf02"] = "田小锋二号";
mymap["txf03"] = "田小锋三号";
mymap["txf"] = "txf"; //同理

2.insert

insert插入的是pair

pair、map<template1, template2>::value_type()、make_pair()函数

//看看make_pair函数
template pair make_pair(T1 a, T2 b) { 
    return pair(a, b); 
}
//本质还是一个pair,
//pair是将2个数据组合成一个数据,当有这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存
//当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,
#include <iostream>
#include <map>
#include <string>
using namespace std;

typedef map<string, string>::value_type Value;
int main()
{
	map<string, string> mymap;

	//1
	mymap.insert(pair<string,string>("txf","田小锋"));
	mymap.insert(pair<string,string>("txf1","田小锋1"));
	mymap.insert(pair<string,string>("txf11","田小锋2"));
	mymap.insert(pair<string,string>("txf0","田小锋3"));
	
	//2
	mymap.insert(map<string, string>::value_type("txf007","小锋007"));
	mymap.insert(map<string, string>::value_type("txf007","小锋007"));//不生效,key重复
	mymap.insert(map<string, string>::value_type("txf007","小锋008888"));//insert不会更新key相同的
	
	mymap.insert(Value("txfqwer","小锋玩亚索"));//看上面起的别名

	//3
	mymap.insert(make_pair("txfnb","小锋牛逼"));
    
	//普通遍历
	cout << "普通遍历\n";
    //嫌太长了,可以typedef map<int, string>::iterator Iter;起个别名
	for (map<string, string>::iterator it = mymap.begin(); it != mymap.end(); it++ ) {
		cout << it->first << "-->" << it->second << endl;
	}

	cout << endl;
	//c++11遍历
	cout << "c++11遍历\n";
	for(auto &temp : mymap ) {
		cout << temp.first << "-->" << temp.second << endl;
	}

	return 0;
}

3.插入自定义类型

前面说了,自定义类型的时候,key一定要能比较,先来看不比较的,value是我们自定义的数据类型

//例子一
#include <iostream>
#include <map>
#include <string>
using namespace std;

typedef struct{
	int no;
	string name;
}MyType;

int main()
{
	map<int, MyType> mymap;

	MyType a{1,"田小锋"};
	mymap[1] = a;

	cout << "c++11遍历\n";
    /*
    value是一个结构体,没有重写<<运算符,所以目前这个例子只能这么写
    */
	for(auto &temp : mymap ) {
		cout << temp.first << "-->" << temp.second.no <<":" << temp.second.name << endl;
	}

	return 0;
}


//下面是重写了<<运算符的,可以略过不看哈,重写<<运算符
#include <iostream>
#include <map>
#include <string>
using namespace std;

class MyType
{
	public:
		MyType(){no = 0, name="";}
		MyType(int no, string name){
			this->no = no;
			this->name = name;
		}
		int getNo() {
			return no;
		}
		string getName() {
			return name;
		}
		
		friend ostream& operator<< (ostream &out, MyType &p ) {
			out << p.getNo() << ":" << p.getName();
			return out;
		}
	private:
		int no;
		string name;
};

int main()
{
	map<int, MyType> mymap;

	MyType a(1,"田小锋"), b(2,"田小锋二号");
	
	mymap[1] = a;
	mymap[2] = b;
	mymap.insert(make_pair(3, MyType(3,"田小锋三号")));

	cout << "c++11遍历\n";
	for(auto &temp : mymap ) {
		cout << temp.first << "-->" << temp.second << endl;
	}

	return 0;
}

在这里插入图片描述

这里来说说当我们的key是自定义类型的时候:

重写<运算符,经验之谈就别问为什么了

#include <iostream>
#include <map>
#include <string>
using namespace std;

//定义了一个结构体
typedef struct{
	int a;
}Myint;

//前面说了,key默认是排序的,这里我写的是l.a 如果 > r.a
//就返回true,说明key大的排在了前面
bool operator < (const Myint l, const Myint r ) {
	return l.a > r.a;
}

int main()
{
	map<Myint, string> mymap;
	Myint t0 = {1};
	Myint t1 = {2};
	Myint t2 = {3};
	mymap[t0] = "田小锋0";
	mymap[t1] = "田小锋1";
	mymap[t2] = "田小锋2";
	
	cout << "c++11遍历\n";
	for(auto &temp : mymap ) {
		cout << temp.first.a << "-->" << temp.second << endl;
	}

	return 0;
}
typedef struct myint{
	int a;
	bool operator< ( const myint &r) {
		return a > r.a;
	}
}Myint;

上面的例子这样重写会报错,[Error] passing 'const myint' as 'this' argument of 'bool myint::operator<(myint)' discards qualifiers [-fpermissive]
大意是:将'const myint'作为'bool myint::operator<(myint)''this'参数传递将丢弃限定符[-fppermissive]
    
源码这样的:
    /// One of the @link comparison_functors comparison functors@endlink.
    template<typename _Tp>
    struct less : public binary_function<_Tp, _Tp, bool>
    {
        bool
            operator()(const _Tp& __x, const _Tp& __y) const  //注意这里,const限定符
        { return __x < __y; }
    };

所以上面要这么写:
typedef struct myint{
	int a;
	bool operator< ( const myint &r) const {
		return a > r.a;
	}
}Myint;

我反正是这么理解的,可能有不对之处,望大家指出来。

上面演示的是结构体,类类型的话就跟这个差不多了,我就不演示了,下面抄一个别人的,定义一个类来重写比较自定义类型的

typedef struct tagStudentInfo
{
    int nID;
    string strName;
    
}StudentInfo,*PStudentInfo;
class sort
{
public:
    bool operator()(tagStudentInfo const &_A,tagStudentInfo const &_B) const
    {
        if(_A.nID<_B.nID) 
            return true;
        return false;
    }
}
int main()
{
    map<StudentInfo,int,sort> mapStudent;
    StudentInfo studentInfo;
    studentInfo.nID=1;
    studentInfo.strName="student_one";
    mapStudent.insert(pair<StudentInfo, int>(studentInfo,80));
    return 0;
}
//选自CSDN 小C博客

总结:自定义类型左key一定要能比较,即重写<运算符!!!!

判断是否插入成功,我这里偷个懒:

在这里插入图片描述

查找

1.count

传入key,查看key的个数,(很明显有就是只有一个,map的key不能重复)

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main()
{
	map<string, string> mymap;
	
	mymap["t0"] = "田小锋0";
	mymap["t1"] = "田小锋1";
	mymap["t2"] = "田小锋2";
	
    //有就是1,没有就是0
	int i = mymap.count("t0"), j = mymap.count("45");
	cout << i << "\n" << j << endl;

	return 0;
}

2.find

定位到key出现的位置,该方法返回一个迭代器。
当数据找到时,返回数据所在位置的迭代器;
否则,返回的迭代器等于end方法返回的迭代器。

在这里插入图片描述

#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef map<string, string>::iterator MapIt;
int main()
{
	map<string, string> mymap;
	
	mymap["t0"] = "田小锋0";
	mymap["t1"] = "田小锋1";
	mymap["t2"] = "田小锋2";
	
//    MapIt it = mymap.find("t4");
	MapIt it = mymap.find("t0");
	if ( it != mymap.end() ) cout << "找到了" << it->first << "->" << it->second << endl;
	else cout << "找不到\n";
	return 0;
}
删除

erase

在这里插入图片描述

三种形式:传入key根据key删除; 根据迭代器删除; 根据迭代器的区间删除;

#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef map<string, string>::iterator MapIt;
int main()
{
	map<string, string> mymap;
	
	mymap["t0"] = "田小锋0";
	mymap["t1"] = "田小锋1";
	mymap["t2"] = "田小锋2";
	mymap["t3"] = "田小锋3";
	mymap["t4"] = "田小锋4";
	
	//key
	mymap.erase("t0");
	int i = mymap.erase("t5");//找不到,所以i为0
	cout << i << endl;
	
	for (auto &temp : mymap ) {
		cout << temp.first << "->" << temp.second << endl;
	}
	return 0;
}
//迭代器删除
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef map<string, string>::iterator MapIt;
int main()
{
	map<string, string> mymap;
	
	mymap["t0"] = "田小锋0";
	mymap["t1"] = "田小锋1";
	mymap["t2"] = "田小锋2";
	mymap["t3"] = "田小锋3";
	mymap["t4"] = "田小锋4";
	//下面的会异常,因为找不到,
	//it指向的是end(),没有元素
//	MapIt it = mymap.find("t5");
//	mymap.erase(it);
	MapIt it = mymap.find("t0");
    if ( it != mymap.end()) //判断一下就行了,t0肯定是能找到的
		mymap.erase(it);
	
	for (auto &temp : mymap ) {
		cout << temp.first << "->" << temp.second << endl;
	}
	return 0;
}

//区间删除
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef map<string, string>::iterator MapIt;
int main()
{
	map<string, string> mymap;
	
	mymap["t0"] = "田小锋0";
	mymap["t1"] = "田小锋1";
	mymap["t2"] = "田小锋2";
	mymap["t3"] = "田小锋3";
	mymap["t4"] = "田小锋4";

	MapIt s = mymap.find("t1");
	MapIt e = mymap.find("t3");
	//c++传统,左闭右开
	mymap.erase(s,e);  //区间是[位置s,位置e),
	
	for (auto &temp : mymap ) {
		cout << temp.first << "->" << temp.second << endl;
	}
	return 0;
}
总结常用方法:
方法用途
insert()插入元素,返回一个东西,我没写
find()查找元素,返回迭代器
count()查找个数
erase()删除元素,key,迭代器
size()返回元素个数
empty()判断map是否为空
begin()返回开始的迭代器
end()返回最后一个元素后面的迭代器
swap();交换两个map
#include <iostream>
#include <map>
#include <string>
using namespace std;
typedef map<string, string>::iterator MapIt;
//交换
int main()
{
	map<string, string> mymap0, mymap2;
	
	mymap0["t0"] = "田小锋0";
	mymap2["t2"] = "田小锋2";
	mymap0.swap(mymap2);
	
	cout << "mymap0\n";
	for (auto &temp : mymap0 ) {
		cout << temp.first << "->" << temp.second << endl;
	}
	
	cout << "mymap2\n";
	for (auto &temp : mymap2 ) {
		cout << temp.first << "->" << temp.second << endl;
	}
	return 0;
}

题目实战:

1.pta数据结构与算法:

实现QQ新帐户申请和老帐户登陆的简化版功能。最大挑战是:据说现在的QQ号码已经有10位数了。

输入格式:

输入首先给出一个正整数N(≤105),随后给出N行指令。每行指令的格式为:“命令符(空格)QQ号码(空格)密码”。其中命令符为“N”(代表New)时表示要新申请一个QQ号,后面是新帐户的号码和密码;命令符为“L”(代表Login)时表示是老帐户登陆,后面是登陆信息。QQ号码为一个不超过10位、但大于1000(据说QQ老总的号码是1001)的整数。密码为不小于6位、不超过16位、且不包含空格的字符串。

输出格式:

针对每条指令,给出相应的信息:

1)若新申请帐户成功,则输出“New: OK”;
2)若新申请的号码已经存在,则输出“ERROR: Exist”;
3)若老帐户登陆成功,则输出“Login: OK”;
4)若老帐户QQ号码不存在,则输出“ERROR: Not Exist”;
5)若老帐户密码错误,则输出“ERROR: Wrong PW”。

输入样例:

5
L 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
N 1234567890 myQQ@qq.com
L 1234567890 myQQ@qq
L 1234567890 myQQ@qq.com

输出样例:

ERROR: Not Exist
New: OK
ERROR: Exist
ERROR: Wrong PW
Login: OK
#include <iostream>
#include <map>
#include <string>
using namespace std;

map<string, string> mydata;
int n;
string qq, pwd;

char choice;

int main()
{
	cin >> n;
	while (n--) {
		cin >> choice >> qq >> pwd;
		if ( choice == 'L' ) {
			if ( mydata.count(qq) == 0 ) {
				cout << "ERROR: Not Exist\n";
			}
			else {
				if ( mydata[qq] != pwd ) {
					cout << "ERROR: Wrong PW\n";
				}
				else {
					cout << "Login: OK\n";
				}
			}
		}
		else {
			if ( mydata.count(qq) == 1 ) {
				cout << "ERROR: Exist\n";
			}
			else {
				cout << "New: OK\n";
				pair<string,string> p(qq, pwd);
				mydata.insert( p );
			}
		}
	}
	return 0;
}

2.洛谷寄包柜

题目描述

超市里有 n(n≤10^5)个寄包柜。每个寄包柜格子数量不一,第 i 个寄包柜有 ai(ai≤10^5)个格子,不过我们并不知道各个 ai 的值。对于每个寄包柜,格子编号从 1 开始,一直到 ai。现在有 q(q≤10^5) 次操作:

  • 1 i j k:在第 i个柜子的第 j 个格子存入物品 k(0≤k≤10^9)。当 k=0时说明清空该格子。
  • 2 i j:查询第 i 个柜子的第 j个格子中的物品是什么,保证查询的柜子有存过东西。

已知超市里共计不会超过 10^7个寄包格子,ai 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然也有可能某些寄包柜中一个格子都没有。

输入格式

第一行 2 个整数 n 和 q,寄包柜个数和询问次数。

接下来 q 个整数,表示一次操作。

输出格式

对于查询操作时,输出答案。

输入输出样例

输入 #1

5 4
1 3 10000 114514
1 1 1 1
2 3 10000
2 1 1

输出 #1

114514
1
#include <bits/stdc++.h>
#include <cstdio>
using namespace std;
map<int,int> m[100005];
int main()
{
	int n, q, c, a, b, num;
	cin >> n >> q;
	while ( q-- ) {
		cin >> c >> a >> b;
		if ( c == 1 )
		{
			cin >> num;
			m[a][b] = num;
		}
		else {
			cout << m[a][b] << '\n';
		}
	}
	return 0;
}

看图就知道了:(map查找是很快的),一维map就是一个红黑树,二维map就是下图这样的嘛,差不多是这个样子。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值