【问题标题】:Choosing a good sorting algorithm [closed]选择一个好的排序算法[关闭]
【发布时间】:2013-09-08 16:07:21
【问题描述】:

Java 应用程序将大部分时间用于对某些键进行排序和删除重复项。

因此,必须选择适应的排序算法。

键是整数(大约 256 位,但不一定),数组大小在 1000 到 100000 个键之间。

输入数组由连续的键组组成。这些组已经排序并且很小(大约 10 个键)。

一个数组示例(3 组,32 位键):

0x01000000
0x01010000
0x01010100
0x01010101

0x01000000
0x01010000
0x01010100
0x01010102

0x01000000
0x01020000
0x01020200
0x01020203

排序和删除重复后:

0x01000000
0x01010000
0x01010100
0x01010101
0x01010102
0x01020000
0x01020200
0x01020203

有什么难的吗?任何想法 ?有链接吗?

谢谢

PS : 在查看了包括归并排序、基数排序、qui...

PPS:最后我分叉了 Java 遗留的合并排序,添加了过滤和排序组的概念。它提供了极大的加速。

【问题讨论】:

  • 请分享您对此的一些想法。你有没有尝试过?
  • 我们不知道你不知道什么。这个问题对我来说似乎很直接。你觉得棘手的是什么?
  • 对 100,000 个整数进行排序应该很快。但什么是“256 位”整数?这些是大整数吗?
  • 我测试了快速排序、tim 排序(由 Java 库提供)和基数排序。 Tim sort 提供了一些有趣的结果,但我想知道我是否可以做得更好。
  • 我还测试了 Java 库提供的归并排序。这也比 tim sort 慢。

标签: java algorithm sorting


【解决方案1】:

合并排序 (http://en.wikipedia.org/wiki/Merge_sort)

由于您的输入数据已预先排序,因此您可以抢占先机。您可以将每个列表中的第一个值输入到 PriorityQueue 中,取出最少的值,然后将该列表中的下一个值添加到队列中。重复。进行一些检查以到达终点。 :-)

我确信有更完整的详细信息。

更多链接:

http://www.cs.washington.edu/education/courses/cse373/06sp/handouts/lecture08.pdf

Algorithm for N-way merge

还有,我自己用相当完整的 Java 代码回答:

Merging multiple sorted csv files with complex comparison

【讨论】:

  • 合并排序能否有效去除重复项?
  • 好点。通常它将包括重复项。但是,如果您添加了一些简单的逻辑来检查您要添加的值是否不是您刚刚添加的值,那么应该没问题。
  • 如果键是就地排序的,那么键会移动很多。不确定这是否有效。你有优化实现的链接吗?
  • 在我提供的第二个链接中查看@aioobe 答案和伪代码,或者在第三个链接中查看我的完整 Java 代码答案。您只是使用 PriorityQueue 或 TreeMap 从 N 个组(在您的示例中为 3)中“排序”前 N 个键。非常有效率。如果它等于你刚刚输出的最后一个,你只需要添加一个检查以不输出一个键。
【解决方案2】:

没有更多细节的最简单的解决方案是

您应该能够将所有行读入 TreeSet 并在最后打印出来。

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
TreeSet<String> sortedSet = new TreeSet<String>();
for(String line; (line = br.readLine()) != null;)
    sortedSet.add(line);
for (String s : sortedSet) 
    System.out.println(s);

【讨论】:

  • 由于输入数据已经基本排序,这将形成一个丑陋的树集。我怀疑一种更“正常”的算法将它们全部放入一个集合中,然后创建一个列表,然后 Collections.sort() 可能会更快。我不确定为什么 OP 的排序很慢,100,000 个整数并不多,所以我绝对喜欢你非常快速和简单的方法。
  • @user949300 TreeSet 应该是平衡树。合并排序可能更有效,但更复杂。我怀疑花在解析和比较键上的时间,而不是排序本身。
  • 你可能是正确的关于时间花在哪里。我在处理来自几十个文件的数千万个复杂字符串时进行了归并排序。
  • 我会检查 TreeSet 的性能。
  • No TreeSet 是经过测试的解决方案中最慢的。
【解决方案3】:

我建议您在此处使用 Collections.sort,因为这样可以处理重复项(如果您为数字创建 SET),并且排序时间复杂度为 O(nlogn),这与它得到的一样好.

如果你只有一组特定的数字,那么你可能想看看基数排序。

【讨论】:

  • Collections.sort() 不会删除重复项。
  • 很抱歉有歧义。当我提到集合时,我的意思是底层数据结构将是一个集合。
  • 你只能 Collections.sort() 一个列表。您可以使用 TreeSet,但不需要 sort()。
【解决方案4】:

如果您每次都对全新的数组进行排序,您可能会受益于Quick sortBucket sort

如果您的数组是更新Fibonacci heap(最有效,虽然复杂)、Binomial heap 或简单的Binary heap

【讨论】:

    【解决方案5】:

    由于您的排序键是有限范围内的整数,您可以使用radix sort。基数排序具有线性时间复杂度,而更通用的基于比较的排序算法对 n 个项目进行排序的运行时间最短为 O(n log n),这使得基数排序和类似的排序算法更适合大型数据集。

    【讨论】:

    • 我挑选了一些有代表性的数组并测试了基数排序。 Tim 排序比基数排序快。
    • 作为一种基于比较元素与 O(n log n) 时间复杂度的排序,对于大型数据集,Tim 排序保证比基数排序慢。对于小型数据集,执行时间由实现细节决定;例如,CPU 缓存在算法的特定实现中的使用效率。
    • 应用程序通常在相对较新的工作站上运行。你知道任何优化的基数排序实现吗?也许我的不好。
    【解决方案6】:

    您可以遍历所有元素并将它们全部放入Set。具体来说,将所有元素放在TreeSet 中,以便为您提供正确的排序。这也将自动删除重复项。您的代码实际上非常简单 -

    Set<int> sortedUniqueKeys = new TreeSet<int>(keys);
    

    其中 keys 是未排序的重复整数键数组。所有排序/重复删除都在构造函数中完成,并且(可能)很快。

    【讨论】:

    • 我会检查 TreeSet 的性能。
    • No TreeSet 是经过测试的解决方案中最慢的。
    • @sylvain 是的,我忘了提。这正是您所期望的折衷 - 超级简单的代码,但由于它如此简单,它失去了一堆优化。
    • 但是你说得有道理。我应该深入研究集合创建。
    • @sylvain 您应该考虑手动填充集合。这种粒度级别应该会给您带来很好的优化提升。还考虑使用hashset 而不是treeset 我相信它们的插入速度更快,但我忘记了。不过,它们确实有不同的顺序,所以在你决定坚持之前先试一试。
    猜你喜欢
    • 2021-02-06
    • 2012-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-11-01
    • 2019-04-24
    • 1970-01-01
    • 2017-04-14
    相关资源
    最近更新 更多