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

Ste*_*eve 64 sorting algorithm list

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

com*_*orm 33

如果你添加了足够的项目,你有效地从头开始构建列表,你应该能够通过排序列表后获得更好的性能.

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

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

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


Jav*_*ier 20

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

  • 如果列表是某种优先级队列,那么这是特别好的建议.谷歌在这种情况下表现不佳. (4认同)

Mar*_*som 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • @MilesRout,根本不是真的.`m log m> m`,所以最好你可以简化它是'O(n +(m log m))`. (3认同)

S.L*_*ott 5

原则上,创建树比对列表进行排序要快。对于每个插入,树插入均为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和sort慢得多。

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

  • 我会谨慎地说一棵树比一棵树快。它实际上取决于输入的大小和所使用的树实现。 (2认同)
  • 运行一些速度测试,您会发现情况并非如此。TreeSet 与 ArrayList 相比,ArrayList 在添加 500k 随机数、排序和将它们转储到另一个列表时快了大约 2 倍。如果我们不将它们转储到另一个列表,则 ArrayList 会以 ~1.6 倍的优势获胜。 (2认同)

Mec*_*cki 5

我想说,我们来测试一下吧!:)

我尝试过使用快速排序,但是使用快速排序对几乎排序的数组进行排序......好吧,这并不是一个好主意。我尝试了一种修改后的方法,截断 7 个元素并使用插入排序。尽管如此,表现还是很糟糕。我切换到合并排序。它可能需要相当多的内存来进行排序(它不是就地排序),但是排序数组的性能要好得多,而随机数组的性能几乎相同(初始排序两者花费的时间几乎相同,快速排序仅稍快一些) )。

这已经表明了一件事:您问题的答案在很大程度上取决于您使用的排序算法。如果它在几乎已排序的列表上性能较差,则在正确的位置插入会比在末尾添加然后重新排序要快得多;合并排序可能不适合您,因为如果列表很大,它可能需要太多的外部内存。顺便说一句,我使用了自定义合并排序实现,它仅使用原始实现的 1/2 外部存储(需要与数组大小本身一样多的外部存储)。

如果合并排序不可行并且快速排序也肯定不可行,那么最好的替代方案可能是堆排序。

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