使用元素键迭代STL容器

时间:2014-06-26 22:37:30

标签: c++ algorithm sorting c++11 stl

我需要一些帮助来选择一个有效的算法来将一个元素从一个向量放入预先排序的桶中 - 或理想情况下输出迭代器范围(因为我认为它们是高效的)。下面的示例完全是人为设计的,但我们的想法是使用元素的键来确定输出桶。我不是要问如何按顺序排序,因为这是一个简单的调用(根据密钥工作和重新排序元素)的简单问题。

std::sort(testVec.begin(), testVec.end(), comparator);

我在coliru上放了一个live example,很容易修改和修复(好不容易,或者我不会问这个问题)。我也可以浏览这个排序列表中的元素,虽然键值是相同的,但是将它附加到一个新的桶中,但是我正在寻找更像STL的东西,现在上面的气味就像一点点最后的解决方案,最终的解决方案需要高效,因为testVec可能很大,而且对象的大小也很大。我宁愿不修改testvec - 所以它应该是不可变的。

理想情况下,我正在寻找一些吐出范围迭代器或类似效率的构造。实际的对象很大,所以传递引用或移动它们实际上是唯一的选择 - 我的实际对象(相当于MyStr)是可移动的。某种foreach键,应用键谓词或我无法弄清楚的东西是我正在寻找的东西。我硬编码下面的3个桶只是为了表明我需要达到的目标 - 这完全是一个黑客。

提前感谢您对此问题的任何帮助

#include <string>
#include <iostream>
#include <sstream>
#include <iterator>
#include <vector>
#include <algorithm>

struct MyStr
{
    int key;
    std::string strval;

    MyStr(int key, const std::string& rStrVal) 
        : key(key)
        , strval(rStrVal) 
    {}

    // let stream operators be friend functions instead of members!
    inline friend std::ostream& operator << (std::ostream& os, const MyStr& val) {
        os << "key[" << val.key << "], strval['" << val.strval << "']";
        return os;
    }

    bool operator < (const MyStr& str) const {
        return (key > str.key);
    }
};

int main()
{
    std::vector <MyStr> testVec = {
        MyStr(4, "key 4"), 
        MyStr(3, "key 3"), 
        MyStr(3, "key 3"), 
        MyStr(2, "key 2"), 
        MyStr(2, "key 2"), 
        MyStr(2, "key 2")
    };

    //auto comparator = [](const MyStr& lhs, const MyStr& rhs) {
    //    return lhs.key < rhs.key;
    //};

    std::vector <MyStr> foursBucket;
    std::vector <MyStr> threesBucket;
    std::vector <MyStr> twosBucket;

    auto ostriter = std::ostream_iterator<MyStr>(std::cout, ",");
    std::for_each(testVec.begin(), testVec.end(), 
        [&](const MyStr& next){
            switch (next.key) {
            case 4:
                foursBucket.push_back(next);
                break;
            case 3:
                threesBucket.push_back(next);
                break;
            case 2:
                twosBucket.push_back(next);
                break;
            }
        });
    std::cout << "Elements with Key Value 2" << std::endl;
    std::copy(twosBucket.begin(), twosBucket.end(), ostriter);
    std::cout << std::endl;
    std::cout << "Elements with Key Value 3" << std::endl;
    std::copy(threesBucket.begin(), threesBucket.end(), ostriter);
    std::cout << std::endl;
    std::cout << "Elements with Key Value 4" << std::endl;
    std::copy(foursBucket.begin(), foursBucket.end(), ostriter);
    std::cout << std::endl;
}

产生以下输出

Elements with Key Value 2
key[2], strval['key 2'],key[2], strval['key 2'],key[2], strval['key 2'],
Elements with Key Value 3
key[3], strval['key 3'],key[3], strval['key 3'],
Elements with Key Value 4
key[4], strval['key 4'],

正如您所看到的,结构非常简单,我已经展示了如何使用谓词对对象进行排序,但我不知道可以选择多少算法来有效地迭代

1 个答案:

答案 0 :(得分:2)

您正在寻找unordered_multimap。它是一个无序的关联容器,它将键值对放入桶中,具​​体取决于键的哈希值(以下示例中为int)。

std::unordered_multimap<int, std::string> 
    mymap{{4, "key 4"}, 
          {3, "key 3"}, 
          {3, "key 3"}, 
          {2, "key 2"}, 
          {2, "key 2"}, 
          {2, "key 2"},
         };

for(auto const& kv : mymap) {
    std::cout << "key: " << kv.first << " value: " << kv.second << '\n';
}

输出:

key: 2 value: key 2
key: 2 value: key 2
key: 2 value: key 2
key: 3 value: key 3
key: 3 value: key 3
key: 4 value: key 4

Live demo


在下面的评论中,您澄清了您收到vector<MyStr>输入,并且无法更改容器类型。在这种情况下,使用std::equal_range查找包含特定键的所有元素。

// comparator for equal_range
struct comp
{
    bool operator()(int key, MyStr const& m) const { return m.key < key; }
    bool operator()(MyStr const& m, int key) const { return key < m.key; }
};

// sort the vevctor
std::sort(testVec.begin(), testVec.end());

// search for all elements with key=2
auto range = std::equal_range(testVec.begin(), testVec.end(), 2, comp());

for(auto it = range.first; it != range.second; ++it) {
    std::cout << "key: " << it->key << " value: " << it->strval << '\n';
}

输出:

key: 2 value: key 2
key: 2 value: key 2
key: 2 value: key 2

Live demo


要迭代每个唯一键,最简单的方法是使用std::unique_copy创建一个只容纳具有唯一键的元素的新容器。然后遍历此容器并在每个键上使用equal_range

bool operator==(MyStr const& m1, MyStr const& m2) { return m1.key == m2.key; }

// sort the vevctor
std::sort(testVec.begin(), testVec.end());

std::vector<MyStr> unique_keys;
std::unique_copy(testVec.begin(), testVec.end(), std::back_inserter(unique_keys));

for(auto const& u : unique_keys) {
    std::cout << "Searching for key: " << u.key << '\n';
    auto range = std::equal_range(testVec.begin(), testVec.end(), u.key, comp());

    for(auto it = range.first; it != range.second; ++it) {
        std::cout << "key: " << it->key << " value: " << it->strval << '\n';
    }
}

Live demo

如果要复制的元素很昂贵,而且您宁愿避免创建一个包含唯一元素的新容器,那么您可以创建自己的输出迭代器来模仿std::back_insert_iterator。它的operator=实现将采用MyStr const&参数,但push_back仅将参数中的键引入唯一键容器,在这种情况下为vector<int>

您可以使用另一种方法(我不确定会起作用)来避免修改输入范围,并避免将元素复制到新范围,创建vector<MyStr *>,其中每个元素指向原始范围中的相应元素。然后重复上述所有步骤,除了不将vector::iterator传递给算法,而是使用boost::indirect_iterator。此迭代器将对容器中的指针应用额外级别的解除引用,然后算法应该像在vector<MyStr>上操作一样工作。