在插入项目或将它们添加到排序列表后对列表进行排序是否更快

时间:2008-10-03 21:02:20

标签: algorithm sorting list

如果我有一个排序列表(比如快速排序),如果我要添加很多值,最好暂停排序,将它们添加到最后,然后排序,或者使用二进制文件来放置添加它们时正确的项目。如果这些项目是随机的,或者已经或多或少地按顺序排列会有所不同吗?

13 个答案:

答案 0 :(得分:29)

如果您添加了足够多的项目,您可以从头开始有效地构建列表,那么您应该可以通过以后对列表进行排序来获得更好的性能。

如果项目大部分都是有序的,你可以调整增量更新和定期排序以利用它,但坦率地说,它通常不值得麻烦。 (你还需要注意一些事情,比如确保一些意想不到的顺序不能使你的算法花费更多更长,q.v。天真快速排序)

增量更新和常规列表排序都是O(N log N)但是你可以得到一个更好的常量因子,然后排序所有内容(我假设你有一些辅助数据结构,所以你的增量更新可以访问列表项比O(N)快......)。一般来说,一次性排序比增加维护顺序具有更多的设计自由度,因为增量更新必须始终保持完整的顺序,但一次性批量排序不会。

如果不出意外,请记住有很多高度优化的批量排序。

答案 1 :(得分:18)

通常使用heap要好得多。简而言之,它分割了推动者和拣选者之间维持秩序的成本。与大多数其他解决方案一样,这两个操作都是O(log n),而不是O(n log n)。

答案 2 :(得分:10)

如果您要添加群组,则可以使用合并排序。对要添加的项目列表进行排序,然后从两个列表中复制,比较项目以确定下一个项目被复制。如果调整目标阵列大小并从最后向后工作,您甚至可以就地复制。

此解决方案的效率为O(n + m)+ O(m log m),其中n是原始列表的大小,m是要插入的项目数。

编辑:由于这个答案没有得到任何爱,我想我会用一些C ++示例代码充实它。我假设排序列表保存在链表而不是数组中。这会将算法更改为更像插入而不是合并,但原理是相同的。

// Note that itemstoadd is modified as a side effect of this function
template<typename T>
void AddToSortedList(std::list<T> & sortedlist, std::vector<T> & itemstoadd)
{
    std::sort(itemstoadd.begin(), itemstoadd.end());
    std::list<T>::iterator listposition = sortedlist.begin();
    std::vector<T>::iterator nextnewitem = itemstoadd.begin();
    while ((listposition != sortedlist.end()) || (nextnewitem != itemstoadd.end()))
    {
        if ((listposition == sortedlist.end()) || (*nextnewitem < *listposition))
            sortedlist.insert(listposition, *nextnewitem++);
        else
            ++listposition;
    }
}

答案 3 :(得分:4)

原则上,创建树比排序列表更快。每个插入的树插入都是O(log(n)),导致整体O(n log(n))。在O(n log(n))中排序。

这就是为什么Java有TreeMap(除了List的TreeSet,TreeList,ArrayList和LinkedList实现之外)。

  • TreeSet以对象比较顺序保存事物。密钥由Comparable接口定义。

  • LinkedList按插入顺序保存内容。

  • ArrayList使用更多内存,对某些操作来说更快。

  • 类似地,TreeMap不需要按键排序。在插入过程中,地图按关键顺序构建,并始终按排序顺序维护。

但是,由于某种原因,TreeSet的Java实现比使用ArrayList和排序要慢得多。

[很难推测为什么它会显着变慢,但确实如此。一次通过数据应该稍快一点。这种事情通常是内存管理的成本超过算法分析。]

答案 4 :(得分:4)

我会说,让我们来试试吧! :)

我尝试使用quicksort,但使用quicksort对几乎排序的数组进行排序是......好吧,这不是一个好主意。我尝试了一个修改过的,切断7个元素并使用插入排序。仍然,可怕的表现。我切换到合并排序。它可能需要相当多的内存用于排序(它不是就地),但是在排序的数组上性能要好得多,在随机数组上几乎相同(初始排序几乎同时用于两者,快速排序只是稍快一点​​) )。

这已经显示了一件事:您的问题的答案很大程度上取决于您使用的排序算法。如果它在几乎排序的列表上表现不佳,那么在正确的位置插入将比在最后添加然后重新排序它快得多;并且合并排序可能对你没有选择,因为如果列表很大,它可能需要太多的外部存储器。 BTW我使用了自定义合并排序实现,它只使用1/2外部存储来实现天真的实现(这需要与数组大小本身一样多的外部存储空间)。

如果合并排序是没有选项而且快速排序不是肯定的选项,那么最好的选择可能是堆排序。

我的结果是:在最后简单地添加新元素然后重新排序数组比将它们插入正确位置快几个数量级。但是,我的初始数组有10个mio元素(已排序),我正在添加另一个mio(未排序)。因此,如果将10个元素添加到10 mio的数组中,正确插入它们比重新排序所有内容要快得多。因此,您的问题的答案还取决于初始(排序)数组的大小以及您想要添加多少新元素。

答案 5 :(得分:1)

大致相同。将项目插入到排序列表中的是O(log N),并且对列表中的每个元素N执行此操作(因此构建列表)将是O(N log N),这是快速排序的速度(或合并排序)这更接近这种方法。)

如果您将它们插入前面,那么它将是O(1),但是之后进行快速排序,它仍然是O(N log N)。

我会采用第一种方法,因为它有可能稍快一些。如果列表的初始大小N远大于要插入的元素数X,则插入方法为O(X log N)。插入列表头部后的排序为O(N log N)。如果N = 0(IE:您的列表最初为空),按排序顺序插入或后续排序的速度是相同的。

答案 6 :(得分:1)

如果列表是a)已经排序,b)动态性质,那么插入排序列表应该总是更快(找到正确的位置(O(n))和插入(O(1)))。

但是,如果列表是静态的,那么必须对列表的其余部分进行随机抽取(O(n)找到正确的位置,O(n)将事情向下滑动)。

无论哪种方式,插入排序列表(或二进制搜索树之类的东西)应该更快。

O(n)+ O(n)应始终快于O(N log n)。

答案 7 :(得分:0)

您应该先添加它们,然后使用基数排序,这应该是最佳的

http://en.wikipedia.org/wiki/Radix_sort#Efficiency

答案 8 :(得分:0)

如果这是.NET并且项目是整数,那么将它们添加到词典会更快(或者如果您在.Net 3.0或更高版本上使用HashSet,如果您不介意丢失重复项)这会让您自动化排序

我认为字符串的工作方式也一样。美丽是你通过这种方式进行O(1)插入和排序。

答案 9 :(得分:0)

(如果您所谈论的列表与C#List<T>相似。)将一些值添加到具有多个值的排序列表中的正确位置将需要更少的操作。但是,如果要添加的值的数量变大,则需要更多。

我建议不要使用列表,而是使用一些更合适的数据结构。例如,像二叉树一样。排序数据结构,插入时间最短。

答案 10 :(得分:0)

将项目插入已排序的列表需要O(n)次,而不是O(log n)次。花费O(log n)时间,你必须找到放置它的地方。但是你必须转移所有元素 - 花费O(n)时间。因此,在保持排序时插入是O(n ^ 2),在插入所有内容然后排序为O(n log n)

根据您的排序实现,如果插入数量远小于列表大小,则可以比O(n log n)更好。但如果是这种情况,无论哪种方式都没关系。

如果插入的数量很大,那么插入all和sort解决方案也是如此,否则它可能无关紧要。

答案 11 :(得分:0)

在较高的层面上,这是一个非常简单的问题,因为您可以将排序视为迭代搜索。如果要将元素插入有序数组,列表或树中,则必须搜索要插入的点。然后你把它放进去,希望成本低廉。因此,您可以将排序算法视为仅仅采取一堆事物,并逐个搜索正确的位置并插入它们。因此,插入排序(O(n * n))是迭代线性搜索(O(n))。树,堆,合并,基数和快速排序(O(n * log(n)))可以被认为是迭代二进制搜索(O(log(n)))。如果基础搜索是有序哈希表中的O(1),则可以进行O(n)排序。 (这方面的一个例子是将52张卡片分成52个盒子进行分类。)

所以你的问题的答案是,一次插入一个东西,而不是保存它们然后对它们进行排序不应该在很大的意义上做出很大的改变。你当然可以有不变的因素来处理,而这些因素可能很重要。

当然,如果n很小,比如10,整个讨论都很愚蠢。

答案 12 :(得分:-2)

将项目插入到排序列表中的是O(log n),而对列表进行排序的是O(n log N) 这表明首先排序然后插入

总是更好

但是记住大'O'只关注速度与项目数量的缩放,可能是对于你的应用程序,中间的插入是昂贵的(例如,如果它是一个向量),所以追加和排序后可能更好。