map和set

目录

set

介绍

迭代器

使用

set

find

count

lower_bound

使用

upper_bound

使用

multiset

介绍

find

count 

equal_range

​编辑

oj题 

两个数组的交集

思路

差集思路

代码

map

介绍

元素类型 -- pair

使用

insert

​编辑

传参

显式构建对象

匿名对象

 make_pair

隐式类型转换 -- c++11新特性

无法插入相同key值

返回值 

[ ]

应用

原理

其他接口

oj题 

前k个高频单词

​编辑

思路

sort:

静态成员函数

仿函数

map版本:

代码

sort版本:

静态成员

仿函数

map版本: 

括号匹配

思路

代码

单词识别

思路

代码

复杂链表的复制

思路

代码


set

介绍

是标准库中的一个容器,它提供了一种有序的不重复的数据存储方式

map和set_第1张图片

基于红黑树实现,是key版本的搜索树(所以需要传入比较方式)

map和set_第2张图片

迭代器

不允许修改值,所以它的两种迭代器其实都是const迭代器

使用

set

find

map和set_第3张图片

如果没有找到,会返回它的end位置的迭代器

set s;
	s.insert(1);
	s.insert(1);
	s.insert(7);
	s.insert(2);
	s.insert(1);
	s.insert(3);
	for (auto c : s) {
		cout << c << " ";
	}
	cout << endl;

	if (s.find(3)!=s.end()) { //可以用判断是否找到
		cout << "success" << endl;
	}
count

map和set_第4张图片

返回该值在结构中出现的次数

但由于set不允许有重复元素,所以count返回的值要么是1,要么是0

所以可以用它来实现find的功能,判断某个元素在不在

if (s.count(3)) {
		cout << "success" << endl;
	}
lower_bound

map和set_第5张图片

它返回一个[>=当前值]的迭代器

如果没找到,就返回end位置的迭代器

使用

可以用来找到某个数字存在的左边界

map和set_第6张图片

图上代码中,itlow指向30

upper_bound

map和set_第7张图片

和上面的功能类似,但是是返回比当前值大的迭代器

如果没找到,就返回end位置的迭代器

使用

map和set_第8张图片

图上代码中,itup指向70(中序序列中第一个比60大的数字)

  • 这个性质可以完美的应用在erase中,它传入的范围是左闭右开
  • 所以可以删除某段区间上的所有数字 / 重复数字
  • 但去重这个特性在set中没啥用.本来set就自带去重,但在multiset中很有用

multiset

介绍
  • 是标准模板库中的一个容器类,它是一个有序的关联容器,允许存储多个具有相同值的元素
  • 和set高度相似,唯一不同的是它可以存储重复值

它就在set下:

map和set_第9张图片

map和set_第10张图片

find

返回中序序列中第一个该数字

void test2() {
	multiset s;
	s.insert(7);
	s.insert(1);
	s.insert(7);
	s.insert(7);
	s.insert(5);
	s.insert(3);
	for (auto c : s) {
		cout << c << " ";
	}
	cout << endl;

	auto it = s.find(7);
	while ( it != s.end()) { //从返回的7后继续打印
		cout << *it << " ";
		++it;
	}
	cout <<  endl;
}

map和set_第11张图片

count 

在multiset中,count才算是真正发挥了用武之地,可以用来统计某重复数字的出现次数

void test2() {
	multiset s;
	s.insert(7);
	s.insert(1);
	s.insert(7);
	s.insert(7);
	s.insert(5);
	s.insert(3);
	cout << s.count(7) << endl;
}

equal_range
map和set_第12张图片

可以找到某重复数字的区间范围,在multiset里才有用

typedef std::multiset::iterator It;
void test3() {
	multiset s;
	s.insert(7);
	s.insert(1);
	s.insert(7);
	s.insert(7);
	s.insert(5);
	s.insert(3);
	std::pair ret = s.equal_range(7); 
    //相当于将7这个数字的左右区间(右区间是开的)给了ret

	s.erase(ret.first, ret.second);

	for (auto tmp : s) {
		cout << tmp << " ";
	}
}

oj题 

两个数组的交集

349. 两个数组的交集 - 力扣(LeetCode)

map和set_第13张图片

思路

反正,无论找交集还是找差集,无序状态绝对是最难找的

  • 所以,首先要排序
  • 要注意 -- 重复元素不能算在交集里!!(看示例1中的结果)
  • 所以,可以考虑放在set里,自动排序+去重
  • 然后,从头遍历两个数组,如果两个值一大一小,那小的就不可能是交集中的元素(因为是有序的嗷),所以小的指针++
  • 就这么一直遍历就行,直到一方完毕
差集思路
  • 还是一样的,先排序!!很重要
  • 然后因为是有序的,所以小的肯定是差集中的元素(因为此时已经不相等了,那大的之后的元素更大,更不可能相等了,所以肯定是差集)
  • 然后小的指针++
代码
    vector intersection(vector& nums1, vector& nums2) {
        vector ans;
        set n1,n2;
        for(const auto& tmp:nums1){
            n1.insert(tmp);
        }
        for(const auto& tmp:nums2){
            n2.insert(tmp);
        }
        auto i=n1.begin(),j=n2.begin();
        while(i!=n1.end()&&j!=n2.end()){
            if(*i<*j){
                ++i;
            }
            else if(*j<*i){
                ++j;
            }
            else{
                ans.push_back(*i);
                ++i;
                ++j;
            }
        }
        return ans;
    }


map

介绍

map和set_第14张图片

  • 是一个关联容器,它提供了 键-值 对(也就是key_value)的存储和检索功能
  • 在插入新键值对时,它会自动进行排序,以保持键的有序性(内部使用自平衡的二叉搜索树(通常是红黑树)来实现)

  •  和set的接口很像
  • map和set_第15张图片
  • 它不像set那样,它的迭代器是正常的

元素类型 -- pair

  • key具有唯一性,且是const类型,但value可以随意修改
  • map和set_第16张图片
  • pair是一种类模板,first成员是map中的key,second成员是map中的value
  • map和set_第17张图片

使用

insert

map和set_第18张图片

insert有多种传参方式,要么传迭代器,要么传值

要注意!!!这里的值是pair类型的嗷!!!所以我们要传入pair类型的

传参

有多种方式可以传入pair对象

显式构建对象
    map res;
	pair tmp("sort", "排序");
	res.insert(tmp);
匿名对象
res.insert(pair("apple", "苹果"));
 make_pair

map和set_第19张图片

可以使用这个模板函数,构建需要的pair对象

res.insert(make_pair("one","一"));
隐式类型转换 -- c++11新特性
res.insert({ "banana", "香蕉" });

注意,这里是多参数的隐式类型转换,在c++11中才被允许使用

而且因为是多参数,需要带上{ }

无法插入相同key值
	map res;
	pair tmp("sort", "排序");
	res.insert(tmp);
	res.insert(pair("apple", "苹果"));
	res.insert({ "banana", "香蕉" });
	res.insert(make_pair("one","一"));
	res.insert(make_pair("one", "xxxx")); //插入相同key
	for (const auto& t : res) {
		cout << t.first << ":" << t.second << endl;
	}

map和set_第20张图片

不会插入,不会覆盖掉原value值

返回值 

map和set_第21张图片

它返回的是一个pair对象,且second成员可以用来判断传入key是否存在

first成员是插入元素的迭代器

[ ]

map和set_第22张图片

这个重载用的很多很多

注意看这个函数原型,传入key值,返回它对应的value值的引用

也就是说,可以通过这个接口,访问到特定key值的value / 修改它的value

void test1() {
	map res;
	pair tmp("sort", "排序");
	res.insert(tmp);
	res.insert(pair("apple", "苹果"));
	res.insert({ "banana", "香蕉" });
	res.insert(make_pair("one","一"));
	for (const auto& t : res) {
		cout << t.first << ":" << t.second << endl;
	}

	cout << endl;

	res["apple"] = "xxx";
	for (const auto& t : res) {
		cout << t.first << ":" << t.second << endl;
	}
}

map和set_第23张图片

应用

还记得我们之前在set的模拟实现中,有对传入的对象记过数字吗

  • 当时需要手动判断+插入/修改次数
  • 但在map中,有了[ ]重载,就不需要那么麻烦
void test2()
{
	string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map ans;
	for (auto e : arr){
		ans[e]++;
	}
	for (const auto& t : ans){
		cout << t.first << ":" << t.second << endl;
	}
}
  • 短短的一行代码,就可以完成插入+修改次数的功能
原理
  • 是因为[ ]的底层实际上是基于insert设计的
  • 它传入的key要是不存在,就insert,value值用传入类型的默认构造初始化
  • key要是存在,就返回一个pair对象,first成员就是该元素的迭代器
  • 然后,两种情况都会返回元素的value值的引用

map和set_第24张图片

大概就是这样的一个函数

其他接口

和set基本差不多,但就是要注意一点,记得map里元素类型是pair

oj题 

前k个高频单词

692. 前K个高频单词 - 力扣(LeetCode)

map和set_第25张图片

思路
  • 首先利用map的特性,统计出每个单词的出现次数
  • 然后!!要注意,他还有要求 -- 相同次数的按照字典序排列
  • 所以,要不然就是sort,要不然就是依次放进两个map里(map的排序具有稳定性)
sort:

自己传入比较方法

静态成员函数
  • 因为sort自身的比较函数实际上是没有this指针的,它只有两个参数
  • 但如果我们在类内定义一个函数作为其比较函数,实际上会有三个参数(看下面的介绍 -- 它的比较函数不允许修改参数)
  • 所以要定义成静态函数(直接可以用类名调用)

仿函数
  • 仿函数可以让对象像函数那样调用
  • 注意,仿函数的使用需要先定义对象
  • 有两种方法:显式定义 / 创建临时对象(因为仿函数基本只有重载的( )函数,所以参数为空,直接类名+( )即可创建临时对象)
map版本:
  • 先让这些字符串按照字典序排列
  • (为了让按次数排序之后,相同次数的还能按字典序排列)
  • 如果先按次数排,那第二次排序的时候,就会让字典序的先排前面,而不会优先排列次数多的
  • 特别注意的是:第二次排序的时候,一定要使用multimap!!!不然次数一样的进不去!!!
代码
sort版本:
静态成员
    static bool compare(const pair& x,const pair& y){
            if(x.second>y.second){
                return true;
            }
            else if(y.second==x.second&&x.first topKFrequent(vector& words, int k) {
        map com;
        vector ans;
        for(auto tmp:words){
            ++com[tmp];
        }
        vector> tmp(com.begin(),com.end());
        sort(tmp.begin(),tmp.end(),compare);
        for(auto it=tmp.begin();itfirst);
        }
        return ans;
    }
仿函数
    struct compare{
        bool operator()(const pair& x,const pair& y){
            if(x.second>y.second){
                return true;
            }
            else if(y.second==x.second&&x.first topKFrequent(vector& words, int k) {
        map com;
        vector ans;
        for(auto tmp:words){
            ++com[tmp];
        }
        vector> tmp(com.begin(),com.end());
        sort(tmp.begin(),tmp.end(),compare());
        for(auto it=tmp.begin();itfirst);
        }
        return ans;
    }
map版本: 
    vector topKFrequent(vector& words, int k) {
        map com1;
        for(const auto& tmp:words){
            ++com1[tmp];
        }
        multimap> com2; //注意是multi版本的,不然次数一样的插入不进去
        for(auto& tmp:com1){
            com2.insert(make_pair(tmp.second,tmp.first));
        }
        auto it=com2.begin();
        vector ans;
        while(k--){
            ans.push_back(it->second);
            ++it;
        }
        return ans;
    }

括号匹配

20. 有效的括号 - 力扣(LeetCode)

map和set_第26张图片

思路

之前有写过这道题,但当时使用c手搓了个栈做的,巨麻烦

但是c++中,有自带的栈,就会好一点

但除此之外,map在这道题里也很好用

  • 将左括号和右括号关联起来
  • 然后遍历传入的字符串,左括号入栈,右括号匹配
代码
    bool isValid(string s) {
        map com;
        stack st;
        com['(']=')';
        com['[']=']';
        com['{']='}';
        for(const auto& tmp:s){
            if(com.count(tmp)==1){
                st.push(tmp); //入左括号
            }
            else{
                if(st.empty()){
                    return false;
                }
                char top=st.top();
                st.pop();
                if(com[top]!=tmp){ //不匹配
                    return false;
                }
            }
        }
        if(!st.empty()){
            return false;
        }
        return true;
    }

单词识别

单词识别_牛客题霸_牛客网 (nowcoder.com)

map和set_第27张图片

思路

很基础的一道题,就是需要处理一下输入

  • 标准输入会将空格视作结束符,所以可以使用cin作为输入函数
  • 还要注意,最后一个单词和' . '之间没有空格,所以需要注意最后标点的处理
  • 还要记得全部转成小写字符

最后,排一下序就行

代码
#include 
#include 
#include
#include
#include
#include

using namespace std;

string str_tolower(const string& str) {
    string arr(str);
    for (auto &c : arr) {
        c=tolower(c);
    }
    return arr;
}
struct compare{
    bool operator()(const pair& x,const pair& y){
        if(x.second>y.second){
            return true;
        }
        else if(y.second==x.second&&x.first ans;
    while(cin>>t){
        if(t[t.size()-1]=='.'){
            t.pop_back();
        }
        t=str_tolower(t);
        ++ans[t];
    }
    vector> tmp(ans.begin(),ans.end());
    sort(tmp.begin(),tmp.end(),compare());
    for(auto i : tmp){
        cout<

复杂链表的复制

剑指 Offer 35. 复杂链表的复制 - 力扣(LeetCode)

map和set_第28张图片

思路

首先先复制一条链表出来,主要要处理的就是random指针

  • 重点就是要把原结点和拷贝结点关联在一起
  • 构建拷贝结点的random,就直接利用原结点的random -> 找到此时的random拷贝结点,链接上就行
代码
Node* copyRandomList(Node* head) {
        map bri;
        Node* copyhead=nullptr,*copytail=nullptr,*cur=head,*node=nullptr;
        while(cur){ //构建新链表+关联结点
            node=new Node(cur->val);
            if(copyhead==nullptr){
                copyhead=copytail=node;
            }
            else{
                copytail->next=node;
                copytail=node;
            }
            bri[cur]=node;
            cur=cur->next;
        }
        node=head;copytail=copyhead;
        while(node){
            if(node->random==nullptr){ //注意要特殊判断一下
                copytail->random=nullptr;
            }
            else{ //直接指就行
                copytail->random=bri[node->random];
            }
            node=node->next;
            copytail=copytail->next;
        }
        return copyhead;
    }

你可能感兴趣的:(数据结构与算法,算法,c++,数据结构)