【问题标题】:STL sorted set where the conditions of order may change顺序条件可能发生变化的 STL 排序集
【发布时间】:2010-09-19 08:51:47
【问题描述】:

我有一个定义了自定义排序的 C++ STL 集。

这个想法是,当项目被添加到集合中时,它们自然会按照我想要的顺序排列。

但是,我刚刚意识到,排序谓词会随着时间的推移而改变。

大概,集合中的项目将不再按顺序排列。

真的有两个问题:

  1. 然后物品出现故障是否有害?我是否正确地说,可能发生的最坏情况是新条目可能会被放入错误的位置(实际上我可以忍受)。或者,这会导致崩溃、丢失条目等吗?

  2. 有没有办法“刷新”集合的顺序?您似乎无法在集合上使用 std::sort() 。我能想到的最好的办法是将内容转储到临时容器中并重新添加它们。

有什么想法吗?

谢谢,

约翰

【问题讨论】:

  • 你的意思是上午 11 点的 pred(a, b) 与上午 10 点的 pred(a, b) 不同吗?

标签: c++ stl set


【解决方案1】:

1) 有害 - 不。导致崩溃 - 不。最糟糕的确实是一个未排序的集合。

2) “刷新”与重新添加一样!

【讨论】:

  • 我很想知道为什么这被否决(不,我不是苦涩的;))。我只是想知道是什么弄错了。
  • 即使不是我投了反对票,我敢打赌这是因为你的答案很不正确。这种不正确的原因可以在其他答案中找到。
  • 其他答案说它不会导致崩溃。他们给出的“有害”的每一个理由都归结为未排序的集合——这就是我所说的。他们还说要重新排序,您必须重新插入。再说一次,这就是我所说的。所以,我再说一遍,告诉我为什么我错了:)拜托。
【解决方案2】:

这可能会导致条目丢失,当在set 中搜索元素时,使用了排序运算符,这意味着如果一个元素被放置在根的左侧,而现在排序运算符说它在右边,那么将不再找到元素。

【讨论】:

    【解决方案3】:

    set 使用排序来查找项目。如果按照ordering1插入N个item,按照ordering2插入一个item,集合无法判断item是否已经在里面。

    这将违反类不变量,即每个项目只存在一次。

    所以它确实有害。

    【讨论】:

      【解决方案4】:

      这实际上取决于实现。
      STL 实现可以并且通常会假设用于排序的谓词是稳定的(否则,不会定义“sorted”)。当您更改谓词实例的行为时,至少可以构造一个有效的 STL 实现来格式化您的硬盘驱动器。

      所以,是的,您需要将这些项目重新插入到一个新集合中。

      或者,您可以构建自己的容器,例如用于二进制搜索的向量 + 排序 + 下界。然后,当谓词行为发生变化时,您可以重新排序。

      【讨论】:

        【解决方案5】:

        我同意其他答案,这将以一些奇怪且难以调试的方式中断。如果你走刷新路线,你只需要复制一次。使用新的排序策略创建一个 tmp 集,将原始集合中的每个元素添加到 tmp 集中,然后执行

         orig.swap(tmp);
        

        这将交换集合的内部结构。

        如果是我,我会将其封装在一个处理所有细节的新类中,以便您可以根据需要更改实现。根据您的访问模式和排序顺序更改的次数,前面提到的向量、排序、下界解决方案可能更可取。

        【讨论】:

          【解决方案6】:

          如果您可以使用无序集合,那么您为什么首先将它们添加到集合中?

          我能想到的唯一情况是您只想确保在添加列表时列表是唯一的。如果是这种情况,那么您可以使用临时集来保护添加:

          if (ts.insert (value).second) {
              // insertion took place
              realContainer.push_back (value);
          }
          

          另一种方法是,根据您修改集合中条目的频率,您可能可以测试该条目是否位于不同的位置(通过使用集合比较功能)以及该位置的位置将移动然后删除旧条目并重新添加新条目。

          正如其他人所指出的那样 - 无序的集合真的很难闻 - 我也猜想它可能会根据标准得到未定义的行为

          【讨论】:

            【解决方案7】:

            虽然这并不能完全满足您的需求,但boost::multi_index 为您提供了类似的功能。由于模板的工作方式,您将永远无法“更改”容器的排序谓词,它在编译时是一成不变的,除非您使用排序向量或类似的东西,you 是保持不变量的那个,你可以在任何给定时间随意排序。

            然而,Multi_index 为您提供了一种同时基于多个排序谓词对一组元素进行排序的方法。然后,您可以选择容器的视图,这些视图的行为类似于按您当时关心的谓词排序的 std::set。

            【讨论】:

              【解决方案8】:

              这是一个简单的测试:

              struct comparer : public std::binary_function<int, int, bool>
              {
                static enum CompareType {CT_LESS, CT_GREATER} CompareMode;
                bool operator()(int lhs, int rhs) const
                {
                  if(CompareMode == CT_LESS)
                  {
                    return lhs < rhs;
                  }
                  else
                  {
                    return lhs > rhs;
                  }
                }
              };
              
              comparer::CompareType comparer::CompareMode = comparer::CT_LESS;
              
              typedef std::set<int, comparer> is_compare_t;
              
              void check(const is_compare_t &is, int v)
              {
                is_compare_t::const_iterator it = is.find(v);
                if(it != is.end())
                {
                  std::cout << "HAS " << v << std::endl;
                }
                else
                {
                  std::cout << "ERROR NO " << v << std::endl;
                }
              }
              
              int main()
              {
                is_compare_t is;
                is.insert(20);
                is.insert(5);
                check(is, 5);
                comparer::CompareMode = comparer::CT_GREATER;
                check(is, 5);
                is.insert(27);
                check(is, 27);
                comparer::CompareMode = comparer::CT_LESS;
                check(is, 5);
                check(is, 27);
                return 0;
              }
              

              因此,基本上,如果您打算find曾经插入过的元素,则不应更改用于插入和查找的谓词。

              【讨论】:

                【解决方案9】:

                使用 STL 执行此操作的唯一安全方法是使用更改的谓词创建一个新集合。例如,当您需要使用新谓词对集合进行排序时,您可以这样做:

                std::set<int> newset( oldset.begin(), oldset.end(), NewPred() );
                

                【讨论】:

                  【解决方案10】:

                  只是跟进:

                  在运行此代码时,Visual Studio C 调试库开始抛出异常,抱怨“

                  因此,更改排序顺序似乎是一件坏事。谢谢大家!

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-04-10
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2021-10-02
                    • 1970-01-01
                    相关资源
                    最近更新 更多