如何在排序的向量中插入值?

时间:2013-04-05 21:04:04

标签: c++ sorting vector stl insertion-sort

ALL,

这个问题是this one的延续。 我认为STL错过了这个功能,但它只是我的恕我直言。

现在,问题。

请考虑以下代码:

class Foo
{
public:
    Foo();
    int paramA, paramB;
    std::string name;
};

struct Sorter
{
    bool operator()(const Foo &foo1, const Foo &foo2) const
    {
         switch( paramSorter )
         {
             case 1:
                 return foo1.paramA < foo2.paramA;
             case 2:
                 return foo1.paramB < foo2.paramB;
             default:
                 return foo1.name < foo2.name;
         }
    }

    int paramSorter;
};

int main()
{
    std::vector<Foo> foo;
    Sorter sorter;
    sorter.paramSorter = 0;
        // fill the vector
    std::sort( foo.begin(), foo.end(), sorter );
}

在任何给定的时刻,矢量都可以重新排序。 该类还具有在分拣机结构中使用的getter方法。

在向量中插入新元素的最有效方法是什么?

我的情况是:

我有一个网格(电子表格),它使用类的排序向量。在任何给定时间,矢量都可以重新排序,网格将相应地显示排序数据。

现在我需要在矢量/网格中插入一个新元素。 我可以插入,然后重新排序,然后重新显示整个网格,但这对于大网格来说非常低效。

任何帮助都将不胜感激。

6 个答案:

答案 0 :(得分:52)

问题的简单答案:

template< typename T >
typename std::vector<T>::iterator 
   insert_sorted( std::vector<T> & vec, T const& item )
{
    return vec.insert
        ( 
            std::upper_bound( vec.begin(), vec.end(), item ),
            item 
        );
}

带谓词的版本。

template< typename T, typename Pred >
typename std::vector<T>::iterator
    insert_sorted( std::vector<T> & vec, T const& item, Pred pred )
{
    return vec.insert
        ( 
           std::upper_bound( vec.begin(), vec.end(), item, pred ),
           item 
        );
}

其中Pred是类型T的严格排序谓词。

为此,输入向量必须已经在此谓词上排序。

执行此操作的复杂性为O(log N) upper_bound搜索(查找插入位置),但最多为O(N)插件本身。

为了更好的复杂性,如果不存在任何重复项,则可以使用std::set<T>;如果可能存在重复项,则可以std::multiset<T>使用vector。这些将自动为您保留排序顺序,您也可以在这些上指定自己的谓词。

您还可以做其他更复杂的事情,例如:管理新添加的项目的setmultiset / sorted vector / vector,然后在有足够数量时合并这些项目。任何类型的迭代都需要在两个集合中运行。

使用第二个向量具有保持数据紧凑的优势。在这里你的新增&#34;项O(M)相对较小,因此插入时间为M,其中O(N)是此向量的大小,可能比插入大O(N+M)更可行矢量每次。合并将为O(NM),这比O(N+M) + O(M²)更好,它将一次插入一个,因此总共M插入{{1}}元素然后合并。< / p>

您可能也会将插入向量保持在其容量状态,因此当您增长时,您将不会进行任何重新分配,只需移动元素。

答案 1 :(得分:24)

如果您需要始终对矢量进行排序,首先您可以考虑使用std::setstd::multiset是否不会简化您的代码。

如果您确实需要一个已排序的向量并希望快速插入一个元素,但又不想强制执行排序条件以便始终满足,那么您可以先使用std::lower_bound()来查找位置在应该以对数时间插入元素的排序范围内,然后使用vector的{​​{3}}成员函数在该位置插入元素。

如果性能问题,请考虑std::liststd::vector进行基准测试。对于小项目,由于缓存命中率较高,std::vector已知更快,但insert()操作本身在列表上的计算速度更快(无需移动元素)。

答案 2 :(得分:8)

只需注意,您也可以根据需要使用upper_boundupper_bound将确保与其他条目等效的新条目将显示在其序列的 end 中,lower_bound将确保等同于其他条目的新条目将出现在开头他们的序列。对于某些实现(可能是可以共享“位置”但不是所有细节的类)可能很有用!)

两者都会向您保证,向量仍会根据元素的<结果进行排序,但插入lower_bound将意味着移动更多元素。

示例:

insert 7 @ lower_bound of { 5, 7, 7, 9 } => { 5, *7*, 7, 7, 9 }
insert 7 @ upper_bound of { 5, 7, 7, 9 } => { 5, 7, 7, *7*, 9 }

答案 3 :(得分:1)

而不是插入和排序。你应该找一个然后插入

保持矢量排序。 (排序一次)。当你必须插入

  1. 找到第一个与您要插入的元素相比较较大的元素。

  2. 在该位置之前插入一个插件。

  3. 这样矢量保持排序。

    这是一个如何运作的例子。

    start {} empty vector
    
    insert 1 -> find first greater returns end() = 1 -> insert at 1 -> {1}
    insert 5 -> find first greater returns end() = 2 -> insert at 2 -> {1,5}
    insert 3 -> find first greater returns 2 -> insert at 2 -> {1,3,5}
    insert 4 -> find first greater returns 3 -> insert at 3 -> {1,3,4,5}
    

答案 4 :(得分:0)

当你想在排序顺序之间切换时,你可以使用多个索引数据结构,每个数据结构都按排序顺序保存(可能是某种平衡树,比如std :: map,它将排序键映射到vector-indices或者std :: set来存储指向你的指针 - 但是使用不同的比较函数。)

这是一个执行此操作的库:http://www.boost.org/doc/libs/1_53_0/libs/multi_index/doc/index.html

对于每个更改(插入新元素或更新键),您必须更新所有索引数据结构,或将它们标记为无效。

如果没有“太多”排序顺序而不是“太多”数据结构更新,则此方法有效。否则 - 运气不好,每次想要改变订单时都必须重新排序。

换句话说:您需要的索引越多(加速查找操作),您需要的更新操作就越多。当然,每个指数都需要记忆。

为了使索引的数量保持较小,您可以使用一些查询引擎,它结合了多个字段的索引来支持多个字段的更复杂的排序顺序。像SQL查询优化器一样。但这可能有点过分......

示例:如果您有两个字段a和b,则可以支持4个排序顺序:

  1. 一个
  2. B'/ LI>
  3. 首先是a b
  4. 先是b然后是
  5. 有2个指数(3和4)。 通过更多字段,排序顺序的可能组合变得更快,更快。但您仍然可以使用“几乎按照您的意愿”排序的索引,并在查询期间根据需要对您无法捕获的其余字段进行排序。对于整个数据的排序输出,这没有多大帮助。但是如果你只想查找一些元素,那么第一个“缩小范围”可能会有所帮助。

答案 5 :(得分:-1)

假设您确实想要使用向量,并且排序标准或键不会更改(因此已插入元素的顺序始终保持不变): 在末尾插入元素,然后一次将其移到前面一步,直到前面的元素不大。

它不能更快​​地完成(关于渐近复杂度,或“大O符号”),因为你必须移动所有更大的元素。这就是STL没有提供这个的原因 - 因为它对矢量效率低下,如果你需要它就不应该使用它们。

编辑:另一个假设:比较元素并不比移动它们贵得多。见评论。

编辑2:由于我的第一个假设不成立(您想要更改排序标准),请废弃此答案并查看我的另一个:https://stackoverflow.com/a/15843955/1413374