【问题标题】:Most efficient sorting algorithm for sorted sub-sequences排序子序列的最有效排序算法
【发布时间】:2012-05-04 13:46:08
【问题描述】:

我有几个 long 类型(升序)数字的排序序列,并希望生成一个包含所有元素的主序列以相同的顺序。我寻找最有效的排序算法来解决这个问题。我以 C#、.Net 4.0 为目标,因此也欢迎针对并行性的想法。

这是一个例子:
s1 = 1,2,3,5,7,13
s2 = 2,3,6
s3 = 4,5,6,7,8
结果序列 = 1,2,2,3,3,4,5,5,6,6,7,7,8,13

编辑:当有两个(或更多)相同的值时,这两个(或更多)的顺序无关紧要。

【问题讨论】:

  • 节省时间?节省空间?其他一些效率参数?
  • 节省时间还是节省空间?
  • 您可以使用简单的 for 循环以 O(n) 复杂度合并每个两个子节点。
  • 大声笑,就像早期评论者暗示的那样,这似乎不是一个微不足道的问题,因为在我发布后 10 分钟内出现了 4 个完全相反的答案。只是想指出我最初的问题,询问针对我的特定问题的最有效算法的想法,这意味着我绝对不会寻找 C# 内置函数,因为我不知道优化子-序列已经排序。
  • @Freddy 只是觉得值得一提,有一些通用排序算法确实考虑到了这一点,例如 Java 和 Python 默认排序算法(但不是 .Nets)。如果您有兴趣,可以在 wikipedia 上查找 TimSort,但这可能对您来说太过分了,因为您已经确定了排序的子集。

标签: c# algorithm sorting quicksort mergesort


【解决方案1】:

只需合并序列。您不必再次对它们进行排序。

【讨论】:

  • 如果他按原样合并它们,他肯定会得到 1,2,3,5,7,13,2,3,6,4,5,6,7,8 吗?为什么不单独排序 3 个然后尝试合并它们,将所有 3 个以随意的顺序粘在一起,然后只对最后一个长的排序一次?
  • 这取决于合并算法。
  • @RhysW:n 路合并与追加不同。
  • @RhysW 您给出的数字是“连接”序列的结果,而不是合并它们。
  • @RhysW 你描述的叫appending。谷歌 Mergesort 了解 merging 的定义。
【解决方案2】:

据我所知,没有 .NET Framework 方法可以进行 K 路合并。通常,它是通过优先级队列(通常是堆)完成的。做起来不难,效率也很高。给定 K 个排序列表,共包含 N 个项目,复杂度为 O(N log K)。

我在我的文章A Generic Binary Heap Class 中展示了一个简单的二进制堆类。在Sorting a Large Text File 中,我介绍了如何创建多个排序的子文件并使用堆进行 K 路合并。给定一个小时(也许更少)的学习时间,您可能可以调整它以在您的程序中使用。

【讨论】:

  • 这听起来很有趣,一定会看看,感谢提示和链接。玩弄后会恢复。
  • Jim,感谢您的宝贵意见,我用优先级队列实现了 k-way 合并,它执行得非常好。我不得不将另一个提议的解决方案标记为答案,因为它提供了要附带的代码。但我继续阅读您的文章并从中获得很多价值,因为我不在这个领域工作,因此我为使用不正确的术语道歉。
【解决方案3】:

你只需要像合并排序一样合并你的序列。

这是可并行化的:

  1. 合并序列(1/2 中的 1 和 2),(3/4 中的 3 和 4),...
  2. 合并序列(1/2/3/4 中的 1/2 和 3/4),(5/6/7/8 中的 5/6 和 7/8),...
  3. …

这里是合并函数:

int j = 0;
int k = 0;
for(int i = 0; i < size_merged_seq; i++)
{
  if (j < size_seq1 && seq1[j] < seq2[k])
  {
    merged_seq[i] = seq1[j];
    j++;
  }
  else
  {
    merged_seq[i] = seq2[k];
    k++;
  }
}

【讨论】:

  • 您需要在任一输入序列结束时添加剩余部分。
  • @Thomash,感谢您的意见,我实现了一些非常相似的东西,尽管它更通用地处理 n 序列。
【解决方案4】:

简单的方法是将它们一一合并。但是,这将需要O(n*k^2) 时间,其中k 是序列数,n 是序列中的平均项目数。但是,使用分而治之的方法,您可以将此时间降低到 O(n*k*log k)。算法如下:

  1. 将 k 序列分成 k/2 组,每组 2 个元素(如果 k 为奇数,则分为 1 组,每组 1 个元素)。
  2. 合并每组中的序列。因此,您将获得 k/2 个新组。
  3. 重复直到获得单个序列。

【讨论】:

  • 而如果使用优先级队列和K路合并算法,则变成O(n log k)。
  • @JimMischel:是的!我刚刚注意到你的回答,为它投票!
【解决方案5】:

更新:

事实证明,使用所有算法...简单的方法仍然更快:

private static List<T> MergeSorted<T>(IEnumerable<IEnumerable<T>> sortedBunches)
{
    var list = sortedBunches.SelectMany(bunch => bunch).ToList();

    list.Sort();

    return list;
}

为了遗留目的......

这是按优先顺序排列的最终版本:

    private static IEnumerable<T> MergeSorted<T>(IEnumerable<IEnumerable<T>> sortedInts) where T : IComparable<T>
    {
        var enumerators = new List<IEnumerator<T>>(sortedInts.Select(ints => ints.GetEnumerator()).Where(e => e.MoveNext()));

        enumerators.Sort((e1, e2) => e1.Current.CompareTo(e2.Current));

        while (enumerators.Count > 1)
        {
            yield return enumerators[0].Current;

            if (enumerators[0].MoveNext())
            {
                if (enumerators[0].Current.CompareTo(enumerators[1].Current) == 1)
                {
                    var tmp = enumerators[0];
                    enumerators[0] = enumerators[1];
                    enumerators[1] = tmp;
                }
            }
            else
            {
                enumerators.RemoveAt(0);
            }
        }

        do
        {
            yield return enumerators[0].Current;
        } while (enumerators[0].MoveNext());
    }

【讨论】:

  • 感谢代码,我试过了,但它没有按预期工作。我用两个 long 类型的数组对它进行了测试,每个数组都包含数十万个值。它似乎被困在一个无限循环中,或者对于少量序列但每个序列有很多元素时效率极低......或者......可能的错误?
  • 能否将您使用过的数组粘贴到文件夹中?
  • 几乎是不可能的(具有几十万的元素),但在这里是一个小的子集:634398912001390000 634398912001540000 634398912002130000 634398912003100000 634398912003120000 634398912003120000 634398912003130000 跨度>
  • 您希望它们如何合并?这是一个简单的排序数组。你想合并数字吗?什么?
  • 我希望 long 类型的值按升序排序。每个序列都包含类似的 long 类型的值。如您所见,我粘贴的示例是 long 类型的值,严格按升序排列。不确定您要问什么,因为我认为这很清楚。你能解释一下不清楚的地方吗?
猜你喜欢
  • 2018-10-23
  • 2011-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 2010-11-27
  • 1970-01-01
相关资源
最近更新 更多