簡體   English   中英

如何在 C++ STL 中實現具有三個鍵的映射?

[英]How to implement a map with three keys in C++ STL?

所以,我有一堂課

class table()
{
  public:
  string product, region, province ,price;
};

所以我有一個上面顯示的類的對象數組,我的目標是編寫一個輸入為產品、地區、省的函數,該函數在數組中搜索它並返回價格。

現在為了用最少的時間進行有效搜索,我想到了在 C++ 中使用 stl 映射,但我需要在我的映射中有 3 個鍵,以價格作為值,所以我使用了它。

struct key
{
string product, region, province;
};

我的地圖定義是這樣的。

unordered_map<key,string> mp;
  1. 在地圖中擁有三個鍵甚至實用嗎?
  2. 有 3 個鍵會影響地圖的搜索時間復雜度嗎?

我對計算機科學領域還是很陌生,所以請原諒我在這個問題上的任何錯誤,而且如果我對地圖的想法完全錯誤,朝着正確的方向稍微推動對我來說會非常有幫助。 感謝您的時間。

如果您使用的是地圖,那么您可以使用tuple<type1, type2....>

map<tuple<int, int , int> , string> foo;

foo.insert(make_tuple(1,2,3), "bar");
foo[make_tuple(1,3,1)] = "bar1";
foo[{2,2,3}] = "bar2";

C++元組

是的,您可以使用std::unordered_map做到這一點,而且它並不像乍看起來那么難。 所有std::unordered_map關心的是:

  1. 鍵類型必須可以使用默認哈希算法或作為模板參數類型或構造實例提供給容器的哈希算法進行哈希處理。
  2. 鍵類型必須支持等價檢查(即,使用類似於散列器模板參數參數的自定義“等於”類型或默認的std::equals來比較兩個鍵實例的等價性,它使用operator ==和兩個實例)。

這比解釋更容易理解。 下面是一個簡單的例子:

#include <iostream>
#include <algorithm>
#include <string>
#include <cstdlib>
#include <tuple>
#include <unordered_map>

struct MyKey
{
    struct Hash
    {
        // jacked up derivation of bernstiens string hasher
        std::size_t operator()(MyKey const& key) const
        {
            std::size_t hash = 5381u;
            for (auto c : key.s1)
                hash = (hash << 5) + hash + c;
            for (auto c : key.s2)
                hash = (hash << 5) + hash + c;
            for (auto c : key.s3)
                hash = (hash << 5) + hash + c;
            return hash;
        }
    };

    std::string s1, s2, s3;

    // equivalence determination.
    bool operator ==(MyKey const& key) const
    {
        return std::tie(s1, s2, s3) == std::tie(key.s1, key.s2, key.s3);
    }
};

int main()
{
    std::unordered_map<MyKey, int, MyKey::Hash> ht;
    ht.insert(std::make_pair<MyKey>({ "one", "two", "three" }, 42));
    ht.insert(std::make_pair<MyKey>({ "four", "five", "six" }, 1999));
    ht.insert(std::make_pair<MyKey>({ "seven", "eight", "nine" }, 1999));

    auto it = ht.find(MyKey{ "one", "two", "three" });
    if (it != ht.end())
        std::cout << it->second << '\n';

    it = ht.find(MyKey{ "four", "five", "six" });
    if (it != ht.end())
        std::cout << it->second << '\n';

    it = ht.find(MyKey{ "seven", "eight", "nine" });
    if (it != ht.end())
        std::cout << it->second << '\n';

    // shoudl NOT find anything
    it = ht.find(MyKey{ "one", "three", "two" });
    if (it != ht.end())
        std::cout << it->second << '\n';
}

要回答您問題中最重要的部分,不,這不會影響搜索時間。 顯然,從三個字符串生成哈希值比從一個字符串生成哈希值需要更長的時間,但是一旦獲得該值,搜索機制與任何其他 unordered_map 相同。 如果發現沖突,等效性檢查就會啟動,同樣,這是不可避免的,但不會影響整體性能配置文件的大 O 復雜性。

std::map更容易上手

struct key {
  std::string product, region, province;
  auto as_tie() const{
    return std::tie(product, region, province);
  }
  friend bool operator<( key const& lhs, key const& rhs ){
    return lhs.as_tie()<rhs.as_tie();
  }
};

現在key用作std::map的鍵。 在這里,我讓 std 元組(通過 std tie)實現我的<運算符。

對於無序映射,您需要專門化 std hash 並提供== (或將 hasher/equality 函數對象傳遞給模板)。

在 C++20 中,你可以

auto operator<=>(key const&)const =default;

而不是領帶的東西,讓地圖工作。

3 部分鍵不會改變地圖的大 O 復雜性,無論是否有序。

無序映射仍然需要自定義哈希。

首先,您希望能夠對多個關鍵字段進行散列以提供一個高質量的散列值,並且 - 以 boost hash_combine的風格 - 您可以這樣做:

template <class T>
inline void hash_combine(std::size_t& seed, T const& v) {
    seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

...結合通用tuple哈希函數...

struct hash_tuple {
    auto operator()(const auto& tuple) const {
        std::size_t seed = 0;
        std::apply([&](const auto& ...element){(..., hash_combine(seed, element));}, tuple);
        return seed;
    }
};

以上將適用於任何長度的元組,以及任何本身可散列的包含類型。

然后,您可以使用unordered_map從鍵字段的std::tuple到 price double ,如下所示:

using key = std::tuple<std::string, std::string, std::string>;
std::unordered_map<key, double, hash_tuple> m;
// ...then just use the map...
m[{"abc", "def", "ghi"}] = 3.14;

...或者,您可能想要一個包含鍵和價格的struct ...

struct X {
    std::string a, b, c;
    double price_;
    auto tie_keys() const { return std::tie(a, b, c); }
    bool operator==(const X& rhs) const {
        return tie_keys() == rhs.tie_keys();
    }
    friend std::ostream& operator<<(std::ostream& os, const X& x) {
        return os << x.a << ' ' << x.b << ' ' << x.c << ' ' << x.price_;
    }
};

...並將它們存儲在unordered_set<> ...

auto hash_fn = [](const X& x) { return hash_tuple{}(x.tie_keys()); };
std::unordered_set<X, decltype(hash_fn)> m2;
// ...and use it...
m2.emplace("ab", "cde", "fg", 3.14);
m2.emplace("hij", "kl", "mn", 2.71);
for (const auto& x : m2)
    std::cout << x << '\n';

你當然需要各種標題......

#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <string>
#include <tuple>

有 3 個鍵會影響地圖的搜索時間復雜度嗎?

如果您有一個不錯的哈希函數,則不會。

暫無
暫無

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

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