使用std :: map的两个键的最佳方法是什么?

时间:2009-07-11 00:09:28

标签: c++ stl

我有一个std :: map,我用来存储x&的值y坐标。我的数据非常稀疏,所以我不想使用数组或向量,这会导致大量的内存浪费。我的数据范围从-250000到250000,但我最多只有几千点。

目前我正在使用两个坐标(即“12x45”)创建一个std :: string并将其用作关键字。这似乎不是最好的方法。

我的其他想法是使用int64并将两个int32推入其中并将其用作密钥。

或者使用具有两个坐标的类。对要用作密钥的类有什么要求?

最好的方法是什么?我宁愿不使用地图地图。

10 个答案:

答案 0 :(得分:110)

使用std :: pair< int32,int32>关键:

std::map<std::pair<int,int>, int> myMap;

myMap[std::make_pair(10,20)] = 25;
std::cout << myMap[std::make_pair(10,20)] << std::endl;

答案 1 :(得分:31)

我通常会解决这样的问题:

struct Point {
    int x;
    int y;
};

inline bool operator<(const Point& p1, const Point& p2) {
    if (p1.x != p2.x) {
        return p1.x < p2.x;
    } else {
        return p1.y < p2.y;
    }
}

答案 2 :(得分:5)

  

要用作密钥的类的要求是什么?

映射需要能够判断一个键的值是否小于另一个键的值:默认情况下,这意味着(key1&lt; key2)必须是有效的布尔表达式,即键类型应该实现'less比'运营商。

map模板还实现了一个重载的构造函数,它允许你传入对key_compare类型的函数对象的引用,它可以实现比较运算符:这样,也可以将比较实现为这个外部函数对象的方法而不是需要被烘焙到你的钥匙的任何类型。

答案 3 :(得分:4)

Boost有一个使用一个或多个索引的地图容器。

Multi Index Map

答案 4 :(得分:1)

这会将多个整数键填充为一个大整数,在本例中为_int64。它比较为_int64,AKA long long(最丑陋的类型声明。短暂的短短,只会略微不那么优雅。10年前它被称为vlong。好多了。那么多“进步”)所以没有比较功能是必要的。

#define ULNG  unsigned long
#define BYTE  unsigned char
#define LLNG  long long 
#define ULLNG unsigned long long

// --------------------------------------------------------------------------
ULLNG PackGUID(ULNG SN,  ULNG PID, BYTE NodeId) {
    ULLNG CompKey=0;

    PID = (PID << 8) + NodeId;
    CompKey = ((ULLNG)CallSN << 32) + PID;

    return CompKey;
}

提供这个答案之后,我怀疑这对你有用,因为你需要两个独立且不同的键来在2维X,Y中导航。

另一方面,如果您已经拥有XY坐标,并且只想将值与该关联关联,那么这非常有效,因为_int64比较与英特尔X86芯片上的任何其他整数比较需要相同的时间 - 1个时钟。

在这种情况下,此合成密钥与三重复合密钥的比较速度是3倍。

如果使用它来创建一个人口稀疏的电子表格,我会使用2个不同的树进行RX,一个嵌套在另一个树中。使Y维度成为“老板”,并在继续X维度之前首先搜索Y空间以解决问题。电子表格比它们的宽度更高,并且您始终希望任何复合键中的第一个维度具有最大数量的唯一值。

这种安排将为Y维度创建一个地图,该地图将具有X维度的地图作为其数据。当你到达Y维度的叶子时,你开始在电子表格中搜索它的X维度。

如果您想创建一个非常强大的电子表格系统,请以相同的方式添加Z维度,并将其用作组织单位的示例。这是一个非常强大的预算/预测/会计系统的基础,允许管理单位有大量的血淋淋的详细帐户来跟踪管理费用等,并且没有这些帐户占用空间用于有自己种类的行单位要跟踪的细节。

答案 5 :(得分:1)

对于您的用例,我认为std::pair中的建议David Norman's answer是最好的解决方案。但是,由于C++11,您也可以使用std::tuple。如果有两个以上的键,例如,如果您具有3D坐标(即xyz),则元组非常有用。然后,您不必nest pairsdefine a comparator for a struct。但是对于您的特定用例,代码可以编写如下:

int main() {
    using tup_t = std::tuple<int, int>;
    std::map<tup_t, int> m;

    m[std::make_tuple(78, 26)] = 476;
    tup_t t = { 12, 45 }; m[t] = 102;

    for (auto const &kv : m)
        std::cout << "{ " << std::get<0>(kv.first) << ", "
                          << std::get<1>(kv.first) << " } => " << kv.second << std::endl;
    return 0;
}

输出:

{12,45} => 102
{78,26} => 476

注意:由于C++17使用元组变得更加容易,特别是如果要同时访问多个元素。 例如,如果您使用structured binding,则可以如下打印元组:

for (auto const &[k, v] : m) {
    auto [x, y] = k;
    std::cout << "{ " << x << ", " << y << " } => " << v << std::endl;
}

Code on Coliru

答案 6 :(得分:0)

使用std :: pair。如果你有很多这样的映射,甚至可以更好地使用QHash<QPair<int,int>,int>

答案 7 :(得分:-1)

首先,抛弃字符串并使用2个整数,这可能是你现在已经完成的。理所当然地认为树是实现稀疏矩阵的最佳方法。通常看起来像磁铁一样糟糕的实施。

仅供参考,三重复合键也可以使用,我也假设有一对。

虽然它制作了一些丑陋的子脚本,所以一点宏观魔法会让你的生活更轻松。我将此保留为通用目的,但如果为特定地图创建宏,则在宏中输入参数是一个好主意。 TresKey12已经过测试并正常运行。 QuadKeys也应该有效。

注意:只要您的关键部分是基本数据类型,您就不需要再编写任何内容了。 AKA,无需担心比较功能。 STL让你满意。只需编码并让它翻录即可。

using namespace std;    // save some typing
#define DosKeys(x,y)      std::make_pair(std::make_pair(x,y))
#define TresKeys12(x,y,z) std::make_pair(x,std::make_pair(y,z))
#define TresKeys21(x,y,z) std::make_pair(std::make_pair(x,y),z))

#define QuadKeys(w,x,y,z) std::make_pair(std::make_pair(w,x),std::make_pair(y,z))


map<pair<INT, pair<ULLNG, ULLNG>>, pIC_MESSAGE> MapMe;
MapMe[TresKey12(Part1, Part2, Part3)] = new fooObject;

如果有人想给我留下深刻印象,请告诉我如何为TresKeys创建一个不依赖于嵌套对的比较运算符,这样我就可以使用包含3个成员的单个struct并使用比较功能

PS: TresKey12给我带来了一个问题,一个地图声明为对,z因为它使x,对,这两个不好玩。对于DosKeys或QuadKeys来说不是问题。如果周五炎热的夏天,您可能会发现在DosEquis中输入意外的副作用   ...错误.. DosKeys多次,是对墨西哥啤酒的渴望。买者自负。正如Sheldon Cooper所说,“没有奇思妙想的生活是什么?”。

答案 8 :(得分:-1)

希望你会发现它很有用:

map<int, map<int, int>> troyka = { {4, {{5,6}} } };
troyka[4][5] = 7;

答案 9 :(得分:-1)

性能稍低但允许更容易索引的最佳结果的替代方案

std::map<int, std::map<int,int>> myMap;

myMap[10][20] = 25;
std::cout << myMap[10][20] << std::endl;