【问题标题】:What is the fastest way to find Nth biggest number of an INT array?找到 INT 数组的第 N 个最大数的最快方法是什么?
【发布时间】:2015-12-21 12:26:51
【问题描述】:

我想要一个更快的函数来找到 C# 中 Int 数组的第 N 个最大数。此函数接受 N 和 Array 并返回该数字的 index

这是我已经拥有的。它只是对数组进行排序,然后返回该数字的索引。 效果很好,但我不确定这是否是最快的方法。没有完全排序的算法似乎是合乎逻辑的。

static int myFunction(int[] array, int N){
    int[] indexes = new int[array.Length];
    for (int i = 0; i < indexes.Length; i++)
        indexes[i] = i;

    for (int i = 0; i < array.Length; i++)
    {
        for (int j = i + 1; j < array.Length; j++)
        {
            if (array[i] < array[j])
            {
                int m = array[j];
                array[j] = array[i];
                array[i] = m;

                m = indexes[j];
                indexes[j] = indexes[i];
                indexes[i] = m;
            }
        }
    }
    return indexes[N];
}

一些结果:

myFunction(new int[] { 1, 3, 2, 0, 10 }, 0); //returns 4 (index of 10)
myFunction(new int[] { 1, 3, 2, 0, 10 }, 1); //returns 1 (index of 3)
myFunction(new int[] { 1, 3, 2, 0, 10 }, 2); //returns 2 (index of 2)

【问题讨论】:

  • 为什么要编写自己的排序算法?如果你想理解它没关系,但冒泡排序并不是最快的。如果您不想编写自己的快速排序实现的麻烦,还有Array.Sort
  • 你的排序是 n^2,所以显然不是,它不是最快的。
  • @FᴀʀʜᴀɴAɴᴀᴍ,对不起题外话,但是你是怎么用这种方式命名你的名字的?我怎样才能以同样的方式设计我的风格?
  • @TaReQMahMooD 老实说,我也不得不为那个东西搜索很多(相信我)。我发现它被称为小型股。 -> Tᴀʀᴇǫ Mᴀʜᴍᴏᴏᴅ

标签: c# algorithm


【解决方案1】:

随机快速选择算法在平均情况复杂度 O(n) 下工作。实际上,O(n^2) 是非常罕见的。它使用快速排序的分区功能

【讨论】:

【解决方案2】:

如果您的数组有无数个数字,并且您需要第五大数字,那么您正在对很多您不需要的数字进行排序。

保持一个长度为 n 的升序排序序列(链表?)会不会更快,并为每个元素检查它是否大于第一个元素(这是升序中最小的)

  • 如果更小:跳到大数组中的下一个元素
  • 如果较大:从已排序的数组中删除最小的元素,即第一个元素,并将较大的元素插入适当的位置,保持数组排序。

扫描完整个数组后,排序序列中的第一个元素就是您要查找的元素。

大多数比较只针对排序数组的第一个元素。您必须将数组更改 N 次,一次更改 N 个最大的数。数组的一个变化是去掉第一个元素(最小的),找到插入新元素的地方,保持数组有序

更正:我关于数组必须更改 N 次的说法是不正确的。这在提供按升序排序的数组时最容易看出:每个比较的数字都将大于 N 大小数组中的最小数字,从而导致替换

【讨论】:

  • 我会指出排序的链表是矫枉过正的。一个大小为 N 的最小堆(堆头小于所有其他元素)足以选择 N 个最大的元素,它的堆头是第 N 个最大的元素,正好回答了这个问题。即使您希望最后对 N 个最大的元素进行排序,最好在选择过程中继续使用最小堆,因为通常堆比排序列表需要更少的维护。
  • 这也是我想到的第一个算法,运行时间为 O(n * N) — 对于数组中的每个元素,您必须与 N 个元素进行比较以查看它是否应该打开你的候选名单。如果 N 较小(或 quickselect 运气不好),使用它可能会更好,但对于较大的 N,我想 quickselect 会更快。
  • @MatthieuM。使用最小堆的建议将其降低到 O(n log(N))。
  • Daan 说:“你必须与 N 个元素进行比较,看看它是否应该在你的候选名单上”。为什么?如果数组已排序,我只需要与第一个元素进行比较,不是吗?
【解决方案3】:

这将是@HaraldDutch 答案的实现。

int get(int[] array, int n)
{
    var comparer = Comparer<int>.Create((x, y) => array[x].CompareTo(array[y]));    //compare the array entries, not the indices
    var highestIndices = new SortedSet<int>(comparer);
    for (var i = 0; i < array.Length; i++)
    {
        var entry = array[i];
        if (highestIndices.Count < n) highestIndices.Add(i);
        else if (array[highestIndices.Min] < entry)
        {
            highestIndices.Remove(highestIndices.Min);
            highestIndices.Add(i);
        }
    }

    return highestIndices.Min;
}

你必须传入 1 而不是 0。

【讨论】:

  • 您可以通过跟踪索引一次性完成此操作。
  • 在非唯一条目进入候选名单的情况下,后处理方法也存在问题。保持索引的开销最小。
  • 这在我们的数组包含重复值的情况下不起作用。
  • @user2697817 感谢您的提示。抱歉,我花了这么长时间才更新答案。
  • @MateenUlhaq 是的,我选择忽略重复值是可能的这一事实,因为我不知道它应该如何表现。它应该将重复值视为一个,还是仍将它们视为多个?
【解决方案4】:

您需要使用选择算法 https://en.wikipedia.org/wiki/Selection_algorithm

这里有漂亮的幻灯片:https://c3p0demo.googlecode.com/svn/trunk/scalaDemo/script/Order_statistics.ppt 一般算法:

Select(A,n,i):
    Divide input into ⌈n/5⌉ groups of size 5.

    /* Partition on median-of-medians */
    medians = array of each group’s median.
    pivot = Select(medians, ⌈n/5⌉, ⌈n/10⌉)
    Left Array L and Right Array G = partition(A, pivot)

    /* Find ith element in L, pivot, or G */
    k = |L| + 1
    If i = k, return pivot
    If i < k, return Select(L, k-1, i)
    If i > k, return Select(G, n-k, i-k)

【讨论】:

    【解决方案5】:

    您可以创建一个大小为 N 的堆,其中最大的数字作为其第一个元素(与通常给出的最小元素相反)。然后遍历整数数组,只要有一个小于堆中最大成员的元素,就将其插入堆中。如果这使得堆的大小超过 N,则删除其中最大的成员。

    这应该是最便宜的方法之一。特定的“m 中第 n 大”算法可能会击败它,但可能不是渐近的。

    【讨论】:

      【解决方案6】:

      您的排序算法目前还不是最快的。您应该在谷歌上搜索“Quicksort”以获得更快的算法。

      在您实现了快速排序之后,您会考虑是否真的需要对整个数组进行排序。假设您想从 10,000 个数字中找出最大的 20 个,为什么要对剩余的 9,980 个数字进行排序?您可以轻松地修改快速排序,以便它会找到 N 个最大的数字,但大部分会忽略其余的数字。

      【讨论】:

        【解决方案7】:

        也许这可以帮助某人。在 int 数组中查找第 n 个最大的数。

                    int[] arr = new int[] { 3, 2, 1, 5 };
                    Array.Sort(arr);
                    int elemCount = 0;
                    int? thirdLargestNumber = null;
                    foreach (var elem in arr)
                    {
                        var temp = arr.Skip(elemCount).ToArray();
                        if (temp.Length == 3) //replace `3` with variable.
                        { 
                            thirdLargestNumber = temp[0];
                            break;
                        }
                        elemCount++;
                    }
                    Console.WriteLine($"Third largest number = {thirdLargestNumber}");
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多