簡體   English   中英

在 std::map 中使用兩個鍵的最佳方法是什么?

[英]What is the best way to use two keys with a std::map?

我有一個std::map用於存儲 x 和 y 坐標的值。 我的數據非常稀疏,所以我不想使用數組或向量,這會導致大量內存浪費。 我的數據范圍從-250000到250000,但我最多只有幾千個點。

目前我正在創建一個帶有兩個坐標(即"12x45" )的std::string並將其用作鍵。 這似乎不是最好的方法。

我的其他想法是使用 int64 並將兩個 int32 推入其中並將其用作密鑰。

或者使用具有兩個坐標的類。 對用作鍵的類有什么要求?

做這個的最好方式是什么? 我寧願不使用地圖地圖。

使用 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;

我通常是這樣解決這類問題的:

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;
    }
}

Boost 有一個使用一個或多個索引的地圖容器。

多索引映射

對用作鍵的類有什么要求?

映射需要能夠判斷一個鍵的值是否小於另一個鍵的值:默認情況下,這意味着 (key1 < key2) 必須是一個有效的布爾表達式,即鍵類型應該實現“小於”運算符。

map 模板還實現了一個重載構造函數,它允許您傳入對 key_compare 類型的函數對象的引用,該函數對象可以實現比較運算符:以便比較可以作為此外部函數對象的方法來實現,而不是需要烘焙到您的密鑰的任何類型。

這會將多個整數鍵填充為一個大整數,在本例中為 _int64。 它比較為 _int64,AKA long long(有史以來最丑陋的類型聲明。short short short short,只會稍微不那么優雅。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 比較與 Intel X86 芯片上的任何其他整數比較花費的時間相同 - 1 個時鍾。

在這種情況下,此合成密鑰的比較速度是三重復合密鑰的 3 倍。

如果使用它來創建一個人口稀少的電子表格,我會使用 2 個不同的樹進行 RX,一個嵌套在另一個中。 使 Y 維度成為“boss”,在進行 X 維度之前,首先搜索 Y 空間以進行解析。 電子表格的高度大於寬度,並且您總是希望任何復合鍵中的第一個維度具有最大數量的唯一值。

這種安排將為 Y 維度創建一個映射,該映射將具有 X 維度的映射作為數據。 當您到達 Y 維度中的葉子時,您開始在電子表格中的列中搜索它的 X 維度。

如果您想創建一個非常強大的電子表格系統,請以相同的方式添加 Z 維度,並將其用於例如組織單位。 這是一個非常強大的預算/預測/會計系統的基礎,它允許管理單位擁有大量詳細的賬戶來跟蹤管理費用等,而不會讓這些賬戶占用有自己種類的線單位的空間要跟蹤的詳細信息。

我認為對於您的用例, std::pair ,正如大衛諾曼的回答所建議的那樣,是最好的解決方案。 但是,從C++11 開始,您也可以使用std::tuple 如果您有兩個以上的鍵,元組很有用,例如,如果您有 3D 坐標(即xyz )。 然后您不必嵌套對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使用元組變得更容易,特別是如果您想同時訪問多個元素。 例如,如果您使用 結構化綁定,則可以按如下方式打印元組:

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

Coliru 上的代碼

使用 std::pair。 如果你有很多這樣的映射QHash<QPair<int,int>,int>最好使用QHash<QPair<int,int>,int>

希望你會發現它有用:

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

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

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

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

首先,放棄字符串並使用 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制作TresKeys ,以便我可以使用具有 3 個成員的單個struct並使用比較函數。

PS: TresKey12 給了我聲明為pair,z 的地圖的問題,因為它使x,pair,而這兩個不能很好地發揮作用。 DosKeys 或 QuadKeys 不是問題。 不過,如果這是一個炎熱的夏季星期五,您可能會發現在 DosEquis 中輸入一個意想不到的副作用......錯誤.. DosKeys 很多次,是對墨西哥啤酒的渴望。 買者自負。 正如謝爾頓·庫珀所說,“沒有奇思妙想的生活是什么?”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM