选择具有唯一性并保持插入顺序的STL容器

发布于 2024-07-17 14:07:10 字数 182 浏览 4 评论 0原文

在以下情况下,我无法决定使用哪个 STL 容器:

  1. 我想保留元素的插入顺序
  2. 容器中的元素必须是唯一的。

有没有现成的容器可用于此? 我不想使用向量,然后在每次执行 push_back 之前执行 std::find

I am unable to decide which STL container to use in the following case:

  1. I want to preserve the order of insertion of the elements
  2. The elements in the container have to be unique.

Is there any readymade container available for this? I don't want to use a vector and then perform a std::find before doing a push_back every time.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

冬天旳寂寞 2024-07-24 14:07:10

Boost MultiIndex 应该能够完全满足您的要求 - 您只需使用一个排序索引即可满足“按插入顺序排序”的要求,以及 hashed_uniqueordered_unique 索引以获得唯一性要求。

Boost MultiIndex should be able to do exactly what you want - you can just use one sequenced index to get the "ordered by insertion order" requirement, and either a hashed_unique or ordered_unique index to get the uniqueness requirement.

GRAY°灰色天空 2024-07-24 14:07:10

可能有一种很好的内置方法可以做到这一点,但一种相当简单的方法是同时使用 hash_map 和列表。 每次插入之前检查 hash_map,然后插入到两者中。 您可能希望将其封装在一个类中。

There might be a good built in way to do this, but one rather simple way is to use both a hash_map and a list. Check the hash_map before each insertion, then insert into both. You'll want to encapsulate this in a class, probably.

情深如许 2024-07-24 14:07:10

没有标准库容器可以直接提供您想要的内容。 我将从 std::vector 开始,编写一个自由函数来执行插入操作,为您执行 find 和 push_back 操作。 如果这就足够了,就不要再继续了。 如果您遇到性能问题,请考虑更复杂的解决方案。

No standard library container gives you what you want directly. I would start with a std::vector and write a free function to do the insert which does the find and the push_back for you. If this suffices, go no further. If you have performance problems, think about a more complicated solution.

新人笑 2024-07-24 14:07:10

您可以这样做:

  • 使用两个成员围绕您的元素类创建一个包装器:您的元素和索引。 我们称之为“InsertedElement”。 索引将是插入顺序。

  • 为此类定义比较运算符,它仅考虑您的元素,但不考虑索引。 这将确保元素的唯一性,同时记住它们的插入顺序。

  • 将 std::set 和插入计数器包装在另一个类中。 然后,当您想要插入新元素时,可以:

  • 它已经存在,无需执行任何操作。
  • 它不会:将其插入到地图中,同时为其指定当前最大索引 + 1。

类似于:

class CMagicContainer
{
  public:
    std::set<InsertedElement> collection;
    int indexGenerator;

    ...
};

You could do this:

  • Create a wrapper around your element class with two members: your element, and an index. Let's call it 'InsertedElement'. The index will be the insertion order.

  • Define comparison operator for this class, which only takes into account your element, but not the index. This will ensure the uniqueness of elements, while remembering their insertion order.

  • Wrap a std::set and an insertion counter in another class. Then, when you want to insert a new element, either:

  • It already exists, nothing to do.
  • It does not: insert it in the map while giving it the current max index + 1.

Something like:

class CMagicContainer
{
  public:
    std::set<InsertedElement> collection;
    int indexGenerator;

    ...
};
花期渐远 2024-07-24 14:07:10

在您选择的容器(例如列表、向量、双端队列)上实现一个包装类,并重写insertion/push_back 方法以在插入元素之前检查插入的元素是否唯一。

不幸的是,我不知道任何 STL 容器符合您的标准。

Implement a wrapper class over the container you choose (e.g. list, vector, deque), and rewrite the insertion/push_back methods to check that the inserted element is unique before inserting the element.

Unfortunately,I don't know any STL container to match your criteria.

扶醉桌前 2024-07-24 14:07:10

正如其他人所说,没有任何一个 STL 容器可以做到这一点。 我喜欢尼尔·巴特沃斯的回答。 另一种选择是同时使用集合和向量。 当你去插入时,检查该元素是否在集合中。 如果是,则无法插入,因为这会违反您的唯一性要求。 如果不是,请将其添加到集合中,然后将其插入向量中。 这很容易实现并且可以包装到单个类中。 代价是增加了内存开销。 但是,您可以通过在单个向量上进行查找来换取增加的计算时间。 这完全取决于您在问题域中处理的数据量以及您的时间和内存限制(如果有)。

As others have said, no single STL container can do this. I like Neil Butterworth's answer. Another option would be to use both a set and a vector. When you go to insert, check to see if the element is in the set. If it is, you can't insert as that would violate your uniqueness requirement. If it isn't, add it to the set and then insert it into the vector. This is easy to implement and can be wrapped into a single class. The tradeoff is increased memory overhead. But you trade that for increased computation time by doing the find yourself on the single vector. It all depends on how much data you are working with in your problem domain and your time and memory constraints (if any).

岛歌少女 2024-07-24 14:07:10

如果您已经安装了 boost,我喜欢这个选项。 否则,为什么不只使用列表或向量并在插入时添加检查 (find(k) == std::npos) 呢? 我想在一个非常非常大的列表上它可能会变得有点慢,但在大多数情况下它会工作得很好。

If you already have boost installed, I like that option. Otherwise, why not just use a list or vector and add a check (find(k) == std::npos) on insertion? I suppose it could get kind of slow on a really, reallly large list, but for most cases it would work just fine.

各自安好 2024-07-24 14:07:10

嗯,我曾经有过类似的情况。 我想到的一件事是“我可以使用多键”吗? 我用谷歌搜索了一段时间,找到了一个使用 std::set 的示例。 所以,如果你无法使用 boost(我推荐它,没有必要重新发明轮子); 你可以尝试类似下面的例子。 我认为你可以使用辅助键作为插入位置(自动增量)。 从我在互联网上找到的例子; 我根据我的需要定制了它。 这是同一版本的修改版本。

Cavaet:它使用宏。 我知道它们通常是邪恶的,但我想这种用途是可以的。

#include <set>
#include <functional>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>

#define MULTIKEYDEF(NAME,TYPE) \
    inline TYPE const & NAME() const { return d_##NAME; } \
    inline void NAME(TYPE const & t) { d_##NAME = t; } \
    TYPE d_##NAME; \
class T##NAME \
    : public std::unary_function<Tmultikey*,bool> { \
private: \
    TYPE d_compare; \
public: \
    T##NAME(TYPE t) : d_compare(t) {} \
    T##NAME(T##NAME const & self) \
    : d_compare(self.d_compare) {} \
    bool operator()(Tmultikey * mk) { \
    return d_compare == mk->##NAME(); \
    } \
   inline TYPE const& name() const { return d_compare; } \
    }

class Tmultikey {
public:
    // Actual keys
    // Can be accessed through d_primary and d_secondary,
    // or primary() and secondary()
    MULTIKEYDEF(primary , std::string);
    MULTIKEYDEF(secondary, unsigned int);
    // Mandatory
    bool operator < (Tmultikey const& mk) const {
        if(primary() < mk.primary()) return true;
        else if(primary() == mk.primary()) {
            return secondary() < mk.secondary();
        }
        return false;
    }
    // Constructor
    Tmultikey(std::string p, unsigned int s)
        : d_primary(p), d_secondary(s) {}

    // Eraser for std::set
    template<typename Compare>
        static void erase(std::set<Tmultikey> & c, Compare op) {
            typename std::set<Tmultikey>::iterator pos = c.begin();
            while(pos != c.end()) {
                if(op(&(*pos))) {
                    c.erase(pos++);
                } else ++pos;
            }
        }
      // Eraser for std::set
      template<typename Compare>
      static std::set<Tmultikey>::iterator find_ex(std::set<Tmultikey> & c, Compare op) {
         typename std::set<Tmultikey>::iterator pos = c.begin();
         while(pos != c.end()) {
            if(op(&(*pos))) {
               break;
            } else ++pos;
         }
         return pos;
      }
};

int main(int argc, char* argv[])
{
    std::set<Tmultikey> mkset;

    mkset.insert(Tmultikey("1",5));
    mkset.insert(Tmultikey("6",4));
    mkset.insert(Tmultikey("3",7));
    mkset.insert(Tmultikey("1",6));

   std::set<Tmultikey>::iterator bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }

    Tmultikey::erase(mkset,Tmultikey::Tsecondary(4));
    //Tmultikey::erase(mkset,Tmultikey::Tprimary("1"));

   std::cout<<"After erase ....\n";
   bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }
   bg = mkset.find(Tmultikey("3",7));
   if (bg != mkset.end())
   {
      std::cout<<"Absolute Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   //bg = Tmultikey::find_ex(mkset,Tmultikey::Tprimary("1"));
   bg = Tmultikey::find_ex(mkset,Tmultikey::Tsecondary(5));
   if (bg != mkset.end())
   {
      std::cout<<"Partial Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   else {
      std::cout<<"Partial Find: FAILED\n";
   }

   return 0;
}

哈特哈,

Well, i once had a similar situation. One thing that came in my mind was 'can i use multi-keys'? I googled for a while, and found a sample using std::set. So, if you do not have access to boost (i recommend it, there is no point re-inventing the wheel); you can try something like the below example. I think you can use the secondary key as the position of insertion (auto-increment). From the example that i found on internet; i tailored it for my needs. This one is a modified version of the same.

Cavaet: It is using macros. I know they are evil in general, but this use is o.k. i guess.

#include <set>
#include <functional>
#include <iostream>
#include <algorithm>
#include <iterator>
#include <string>

#define MULTIKEYDEF(NAME,TYPE) \
    inline TYPE const & NAME() const { return d_##NAME; } \
    inline void NAME(TYPE const & t) { d_##NAME = t; } \
    TYPE d_##NAME; \
class T##NAME \
    : public std::unary_function<Tmultikey*,bool> { \
private: \
    TYPE d_compare; \
public: \
    T##NAME(TYPE t) : d_compare(t) {} \
    T##NAME(T##NAME const & self) \
    : d_compare(self.d_compare) {} \
    bool operator()(Tmultikey * mk) { \
    return d_compare == mk->##NAME(); \
    } \
   inline TYPE const& name() const { return d_compare; } \
    }

class Tmultikey {
public:
    // Actual keys
    // Can be accessed through d_primary and d_secondary,
    // or primary() and secondary()
    MULTIKEYDEF(primary , std::string);
    MULTIKEYDEF(secondary, unsigned int);
    // Mandatory
    bool operator < (Tmultikey const& mk) const {
        if(primary() < mk.primary()) return true;
        else if(primary() == mk.primary()) {
            return secondary() < mk.secondary();
        }
        return false;
    }
    // Constructor
    Tmultikey(std::string p, unsigned int s)
        : d_primary(p), d_secondary(s) {}

    // Eraser for std::set
    template<typename Compare>
        static void erase(std::set<Tmultikey> & c, Compare op) {
            typename std::set<Tmultikey>::iterator pos = c.begin();
            while(pos != c.end()) {
                if(op(&(*pos))) {
                    c.erase(pos++);
                } else ++pos;
            }
        }
      // Eraser for std::set
      template<typename Compare>
      static std::set<Tmultikey>::iterator find_ex(std::set<Tmultikey> & c, Compare op) {
         typename std::set<Tmultikey>::iterator pos = c.begin();
         while(pos != c.end()) {
            if(op(&(*pos))) {
               break;
            } else ++pos;
         }
         return pos;
      }
};

int main(int argc, char* argv[])
{
    std::set<Tmultikey> mkset;

    mkset.insert(Tmultikey("1",5));
    mkset.insert(Tmultikey("6",4));
    mkset.insert(Tmultikey("3",7));
    mkset.insert(Tmultikey("1",6));

   std::set<Tmultikey>::iterator bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }

    Tmultikey::erase(mkset,Tmultikey::Tsecondary(4));
    //Tmultikey::erase(mkset,Tmultikey::Tprimary("1"));

   std::cout<<"After erase ....\n";
   bg = mkset.begin();
   for (;bg != mkset.end(); ++bg)
   {
      std::cout<<(*bg).primary()<<std::endl;
   }
   bg = mkset.find(Tmultikey("3",7));
   if (bg != mkset.end())
   {
      std::cout<<"Absolute Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   //bg = Tmultikey::find_ex(mkset,Tmultikey::Tprimary("1"));
   bg = Tmultikey::find_ex(mkset,Tmultikey::Tsecondary(5));
   if (bg != mkset.end())
   {
      std::cout<<"Partial Find:"<<(*bg).primary()<<" "<<(*bg).secondary()<<std::endl;
   }
   else {
      std::cout<<"Partial Find: FAILED\n";
   }

   return 0;
}

HTH,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文