Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/142.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/sorting/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何在排序向量中插入值?_C++_Sorting_Vector_Stl_Insertion Sort - Fatal编程技术网

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

C++ 如何在排序向量中插入值?,c++,sorting,vector,stl,insertion-sort,C++,Sorting,Vector,Stl,Insertion Sort,全部, 这个问题是问题的继续。 我认为STL忽略了这个功能,但这只是我的想法 现在,我来回答这个问题 考虑以下代码: class Foo { public: Foo(); int paramA, paramB; std::string name; }; struct Sorter { bool operator()(const Foo &foo1, const Foo &foo2) const { switch( par

全部,

这个问题是问题的继续。 我认为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方法

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

我的情况是:

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

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


任何帮助都将不胜感激。

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

关于渐近复杂性,或者大O表示法,它不能做得更快,因为您必须移动所有更大的元素。这就是为什么STL不提供这个功能的原因——因为它在向量上效率低下,如果需要的话,不应该使用它们

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


< >编辑2:由于我的第一个假设不想改变分类标准,请删除这个答案,看看我的另一个:

如果您需要始终保持向量排序,首先您可以考虑使用STD::SET还是STD::MultSube不会简化代码。 如果您确实需要一个排序向量,并且希望快速将元素插入其中,但又不希望始终满足排序标准,则可以首先使用查找排序范围中的位置,在对数时间内元素应插入的位置,然后使用vector的成员函数在该位置插入元素


如果性能是一个问题,考虑基准STD::列表vs STD::向量。对于小项目,std::vector由于缓存命中率较高而被认为更快,但插入操作本身在列表上的计算速度更快,无需移动元素。

而无需插入和排序。您应该先查找,然后插入

保持向量排序。分类一次。当你必须插入

找到第一个与要插入的元素比较大的元素

在该位置之前插入

这样向量保持排序

下面是一个例子

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}

当您想要在排序顺序之间切换时,可以使用多个索引数据结构,每个索引数据结构都保持排序顺序—可能是某种平衡树,如std::map,它将排序键映射到向量索引,或者std::set,它存储指向对象的指针—但具有不同的比较函数

这是一个图书馆,它可以做到这一点:

对于新元素的每次更改、插入或键的每次更新,都必须更新所有索引数据结构,或将它们标记为无效

如果排序顺序不太多,数据结构的更新也不太多,那么这种方法就行了。否则-运气不好,每次你想更改顺序时都必须重新排序

换句话说:加快查找操作所需的索引越多,更新操作所需的时间就越多。当然,每个索引都需要内存

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

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

A. B 先是a然后是b 先是b然后是a 有2个指数3。和4。。 字段越多,排序顺序的可能组合就越大、越快。但是,您仍然可以使用一个索引,该索引几乎按照您的需要进行排序,并且在查询过程中,根据需要对无法使用该索引捕获的其余字段进行排序。对于整个数据的排序输出,这并没有多大帮助。但是,如果您只想查找某些元素,首先缩小范围会有很大帮助。

请注意,您也可以根据需要使用上限。上界将确保与其他条目相同的新条目出现在其序列的末尾,下界将确保与其他条目相同的新条目出现在其序列的开头。对于某些实现可能很有用,例如可以共享位置但不是所有细节的类


这两种方法都可以确保向量按照问题的简单答案排序:

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上的ed谓词

要使其工作,输入向量必须已在此谓词上排序

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 
        );
}
这样做的复杂性在于上限搜索查找插入的位置,但对于插入本身来说,这是一个很大的问题

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

您还可以做其他各种更复杂的事情,例如,管理一个向量和一个新添加项的集合/多集合/排序向量,然后在有足够的项时将它们合并。对集合进行任何类型的迭代都需要同时运行这两个集合

使用第二个向量具有保持数据紧凑的优点。在这里,新添加的项向量将相对较小,因此插入时间将为OM,其中M是该向量的大小,并且可能比每次插入大向量更可行。合并将在+M上进行,这比每次插入一个元素的ONM要好,因此总的来说,插入M个元素然后合并将在+M+OM²上进行



您可能也会保持插入向量的容量,这样随着您的增长,您就不会进行任何重新分配,只移动元素。

如果没有重复项,我认为您应该使用set。或std::否则列出。向量似乎不适合需要频繁排序的内容。std::set基于红黑树,重新插入的复杂性为Ologn。您可能需要考虑这个插入的相关树结构到排序数组问题,如RB树、AVL树等。@ SARDUSTSTY排序是很好的,我认为主要的缺点是插入时会导致元素的移位,特别是当它导致内存重新分配时。很好的选择是退出队列-因为它整合了vectorsearch和listinsert的所有优点,所有这些都取决于数据属性和操作。听起来像是二进制搜索。您可以使用二进制搜索来找到要插入的位置。不过没什么帮助。您仍然需要按步骤创建可以插入图元的间隙。如果移动便宜而比较昂贵,则会有帮助。移动通常便宜得多,尤其是当元素为int时,在C++11中,甚至移动对象也可能便宜。但请记住,这是不高效的,因为查找和插入都在操作上。如果元素的数量大于元素的数量,那么我的答案中的算法会更有效率。@Sebastian:事实上,在声明之前,你可能需要对其进行分析。效率并不完全受算法复杂性的限制。此外,在本例中的查找将是Olog n,因为您可以使用二进制搜索。@stardust\我不能一次排序。我的向量可以在任何给定的时刻由不同的类字段重新调用。如果以按名称排序的向量开始,用户可能会按paramA或paramB排序,然后插入新元素。问题是我不知道将使用哪台分拣机。@Benjamin:在我的回答的评论中讨论了这一发现。是的,渐进复杂性不是一切。但这是重要的第一步。@Igor实际上,如果你从空向量开始,你甚至不需要排序一次。重要的是插入的位置。如果在正确的位置插入,则始终对向量进行排序。无需在任何时候对其进行排序。想一想,我能在不同的排序标准上重新排序std::set吗?尤其是那些具有非唯一值的集?@Igor:不,不能根据不同的标准对std::set重新排序。但是您可以将内容移动到一个新的std::set,并使用不同的标准进行排序。谢谢您的回答。我只是在谷歌上搜索了insert到排序向量中,最后使用了std::set。另外,请注意您关于性能的评论:std::vector几乎在所有情况下都会获胜:@H2CO3:我很高兴它起到了作用。实际上,Bjarne曾多次提到STD::向量应该是C++中容器的默认选择。还应该注意,在插入元素时,集合和多集都不会使引用和迭代器无效;矢量可能会。它们之间的速度差异取决于比较或复制是否更昂贵以及矢量有多大。上界/下界函数为OlogN,插入为ON。考虑这种病理情况:将0插入{ 0, 0, 0,…/*很多零点*/,0 }注意,逻辑上使用低端绑定和上限绑定没有区别,因为它只在插入的元素已经存在时有所不同。如果是的话,上界会稍微好一点,因为你把它插得更靠近后面,这意味着移动的元素更少。