结构体的排序函数以及priority_queue优先级、自定义的结构体struct作为关联性容器map、unordered_map的key

作为map的key

map是有序的,其中的插入元素按照key的大小进行排序,对于一般的数据类型,比如说int、string这两种常用的类型来说,可以直接作为map的key使用,但是对于自定义的struct结构体来说,就不可以直接使用其作为key进行插入,这是因为,编译时,无法进行比较大小。
具体原理不赘述。如果想使用自定义的struct作为key,可以这么修改。

#include <iostream>
#include <map>

using namespace std;

struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    bool operator < (const Point &p) const {
    
    
        return x < p.x || y < p.y;
        // if (x == p.x) {
    
    
        //     return y < p.y;
        // } else {
    
    
        //     return x < p.x;
        // }
    }
};

map<Point,bool> vis;

int main()
{
    
    
    Point temp=Point(1,2);
    vis[temp]=1;
    if(vis[Point(1,2)]) printf("yes");
    else printf("no");
    return 0;
}
//运行结果
yes

上述代码定义了一个结构体Point,并尝试将其作为key,建立一个Point—>bool的一个映射vis。
在Point中,使用了构造函数,可以直接构造一个Point类型的变量,最重要的是,重载了“<”运算符。这样,当时用自定义的Point作为map的key时,key就可以比较了。
实例

#include <iostream>
#include <map>

using namespace std;

struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    bool operator < (const Point &p) const {
    
    
        return x < p.x || y < p.y;
        // if (x == p.x) {
    
    
        //     return y < p.y;
        // } else {
    
    
        //     return x < p.x;
        // }
    }
};

map<Point,bool> vis;

int main()
{
    
    
    vis[Point(2,2)]=1;
    vis[Point(1,2)]=1;
    vis[Point(1,1)]=1;
    vis[Point(2,2)]=0;

    for(map<Point,bool>::iterator it=vis.begin();it!=vis.end();it++){
    
    
        printf("(%d,%d)->%d\n",(it->first).x,(it->first).y,it->second);
    }

    return 0;
}
//运行结果
(1,1)->1
(1,2)->1
(2,2)->0

上述例子中,使用struct定义了一个类型Point,在里面添加了构造函数、重载"<"运算符。使用其作为key,建立了一个从Point—>bool 类型的有序map,即vis。
在main()中,依次将(2,2)、(1,2)、(1,1)设为了1,即出现过,之后又将(2,2)设为了0。
在for()循环中,依次打印出各点以及对应的value,可以发现,各个点的输出顺序是按照x值递增,x相同时y递增的顺序来的。而且,(2,2)的value是最终赋予的0;
关于重载结构体的“<” 运算符,可以使用的几种方法为

//代码1
struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    bool operator < (const Point &p) const {
    
    
        return x < p.x || y < p.y;//先判断是否x与p.x相等
    }
};
//代码2
struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    bool operator < (const Point &p) const {
    
    
         if (x == p.x) {
    
    //注意x在前,,先判断是否x与p.x相等
             return y < p.y;
         } else {
    
    
            return x < p.x;
         }
    }
};

推荐使用代码1、2。为大量插入键值的时候不导致数据丢失,改成先判断x是否相等,如果是则返回y<y的结果,如果不是,返回x<x的结果就行
以上代码1、2是完全相同的意思。这不过1比2更简洁。
注意在重载运算符中的if()判断逻辑,如果当前x==p.x,则如果当前y小于p.y,则当前Point类型的变量在前。else是说,如果当前Point的x!=p.x,那么则当前Point的变量的x小,则当前Point类型变量在前。
也可以这么写

//代码3
struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    bool operator < (const Point &p) const {
    
    
         if (x != p.x) {
    
    //注意判断逻辑
             return x < p.x;
         } else {
    
    
             return y < p.y;
         }
    }
};

注意上面这种写法的判断逻辑。这次是先比较是否当前Point类型的变量的x是否!=p.x,如果是,则,当前Point类型的x小的话,则当前Point类型在前。

//代码4
struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    friend bool operator < (Point a,Point b){
    
    
        if(a.x!=b.x)
            return a.x<b.x;
        else
            return a.y<b.y;
    }

};

上面的代码4与pirority_queue中的重载运算符还不一样,在map中定义大小顺序,与实际意义相同,谁小谁在前,类似sort,这与priority_queue完全相反。不要用错。
使用priority_queue中的写法实例

#include <iostream>
#include <vector>
#include <algorithm>
#include <queue>
#include <map>
#include <unordered_set>

using namespace std;

struct Point
{
    
    
    int x;
    int y;
    Point(){
    
    }
    Point(int _x,int _y):x(_x),y(_y){
    
    }
    friend bool operator < (Point a,Point b){
    
    
        if(a.x!=b.x)
            return a.x>b.x;//如果用于priority_queue,则是谁小谁在前
        else
            return a.y>b.y;//如果用于priority_queue,则是谁小谁在前
    }

};

map<Point,bool> vis;

int main()
{
    
    
    vis[Point(2,2)]=1;
    vis[Point(1,2)]=1;
    vis[Point(1,1)]=1;
    vis[Point(2,2)]=0;

    for(map<Point,bool>::iterator it=vis.begin();it!=vis.end();it++){
    
    
        printf("(%d,%d)->%d\n",(it->first).x,(it->first).y,it->second);
    }
    return 0;
}
//运行结果
(2,2)->0
(1,2)->1
(1,1)->1

可以发现,完全与priority_queue中重载“<"运算符的作用相反。
而将if中的return逻辑做修改,改为类似sort的排序意义,即正确。即代码4的写法是按照x小在前,x相同,y小则在前的判断逻辑。
以上所谈,引自这里
以上所谈为有序map,而想在unordered_map中使用struct定义的结构体作为key,如此做是不可以的。

作为unordered_map的key

参考文档:
结构体有三个int元素的:STL: unordered_map 自定义键值类型的使用(C++)
可以自定义结构体元素的:用struct做unordered_map的key
详细地unordered_map 自定义键值类型

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

struct node{
    
    
    int x,y;
    node(){
    
    }
    node(int _x,int _y):x(_x),y(_y){
    
    }
};

struct myhashfun{
    
    
    std::size_t operator () (const node & key) const
    {
    
    
        using std::size_t;
        using std::hash;
        return  (hash<int> () (key.x) ^ ((hash<int> () (key.y) << 1))) ;
    }
};

struct myequal{
    
    
    bool operator () (const node& k1,const node& k2)const
    {
    
    
        return k1.x==k2.x&&k1.y==k2.y;
    }
};

int main()
{
    
    
	unordered_map<node,bool,myhashfun,myequal> vis;
	vis[node(1,2)]=1;
	vis[node(2,3)]=1;
	vis[node(3,3)]=1;
	unordered_map<node,bool,myhashfun,myequal>::iterator it;
	for(it=vis.begin();it!=vis.end();it++)
        printf("(%d,%d)->%d\n",(it->first).x,(it->first).y,it->second);
	return 0;
}
//运行结果
(2,3)->1
(3,3)->1
(1,2)->1

定义了一个struct结构体node,并建立了从node到bool型的映射。
太费费劲了,C++博大精深!!以后再说吧。我还是先用将两个整数通过to_string转为string,之后通过“+”或者“ ”(空格)拼接起来,使用string->bool的映射来统计是否出现过该点吧。
本着实验的精神,使用了三种方法建立映射,因为,需要在二维平面上将某些点设置为障碍点。
874. Walking Robot Simulation
第一种方法,使用《算法笔记》中提到的,将0<=x,y<=RANGE范围里面的点映射到一个独一无二的标记,本题中,所有点有正有负,其实范围为- 3 * 10000,这可以将所有点的x,y都加上3 * 10000,之后再使用x * RANGE+y,就可以将所有点都映射到了一个点上了。
第二种方法,将所有的点的x,y坐标,使用to_string()函数,转为字符串,之后使用“ ”拼接。进行字符串意义上的对比。
第三种方法,使用学到的将结构体作为unordered_map的key,建立(x,y)->bool的映射,这种想法很直观。在oj上提交了好几次,看一下效率。
在这里插入图片描述可以看到,其中方法二,即使用字符串匹配,效率最差。使用将结构体作为key来建立node->bool的映射,还得费劲写相关的函数,但是效率并不好,以为每一步都要进行计算的方法一,肯定效率不好,但是确实最好的。
总结,遇到平面中有范围的点的映射问题,如果范围大小允许使用方法一,则使用方法一,三维问题或者没有范围的问题,可以使用方法2,方法三写法复杂,效率也高不到那里去。
2021/1/28更新
在题目1128. Number of Equivalent Domino Pairs中,又遇到使用两个数据做映射的问题,一开始用的是将两个数据转为string,使用“ ”拼接,但是效率慢,考虑到数据都是[0,9]之间,所以可以使用数据映射,将(x,y),使用公式9*x+y可以映射到唯一的数。
在这里插入图片描述

效果差距明显,竟然快一半!

猜你喜欢

转载自blog.csdn.net/weixin_44321570/article/details/112959707