【问题标题】:How do I insert an int into a sorted array quickly?如何快速将 int 插入排序数组?
【发布时间】:2011-05-12 00:15:47
【问题描述】:

我想将一个 int 插入一个已排序的数组中。此操作将非常频繁地执行,因此需要尽可能快。

  • 使用 List 或任何其他类代替数组是可能的,甚至是首选的
  • 所有值都在 1 到 34 范围内
  • 该数组通常正好包含 14 个值

我想了很多不同的方法,包括二分查找和简单的复制插入,但发现很难决定。另外,我觉得我错过了一个想法。你有这方面的经验或有什么新想法可以考虑吗?

【问题讨论】:

  • 将对数组执行什么样的查询?
  • @Ani 主要是枚举和一些索引查找
  • 编写几个方法并比较它们的执行时间看起来并不难。您已经得到了一些很好的答案,建议使用多种方法,现在您必须做出选择。怎么办?
  • 你的申请是什么?有什么用?可能正在以其他方式思考,例如集群更好地改进一下

标签: c# .net arrays performance copy


【解决方案1】:

我将使用一个长度为 35(因为你说的范围是 1-34)的 int 数组来记录数字的状态。

int[] status = Enumerable.Repeat(0, 35).ToArray(); 
//an array contains 35 zeros
//which means currently there is no elements in the array
status[10] = 1;  // now the array have only one number: 10
status[11] ++;   // a new number 11 is added to the list

所以如果你想在列表中添加一个数字 i:

status[i]++;  // O(1) to add a number

要从列表中删除 i:

status[i]--;   // O(1) to remove a number

想知道列表中的所有数字吗?

    for (int i = 0; i < status.Length; i++)
    {
        if (status[i] > 0)
        {
            for (int j = 0; j < status[i]; j++)
                Console.WriteLine(i);
        }
    }
    //or more easier using LINQ
    var result = status.SelectMany((i, index) => Enumerable.Repeat(index, i));

下面的例子可以帮助你更好地理解我的代码:

the real number array: 1 12 12 15 9 34 // i don't care if it's sorted
the status array: status[1]=1,status[12]=2,status[15]=1,status[9]=1,status[34]=1
                  all others are 0

【讨论】:

  • 虽然不能直接回答我的问题,但我认为这是一个非常好的主意,因为无论如何我都需要直方图。
【解决方案2】:

这是一个非常小的数组,只有 14 个值,我认为切换到更智能的数据结构(例如列表)不会让您受益匪浅,尤其是在您快速良好的随机访问时。在这种规模下,甚至二进制搜索实际上也可能比线性搜索慢。你确定,比如说,insert-on-copy 不能满足你的性能要求吗?

【讨论】:

  • 我不确定 :) 在这里保存的每几个刻度在我的情况下直接转换为分钟,所以我想知道是否有更好的东西。
  • 好吧,如果每个滴答声都值几分钟,那意味着整个工作需要数小时或数天。相比之下,小幅收益看起来就没有那么吸引人了。
【解决方案3】:

此操作将经常执行,因此需要尽可能快。

您注意到“经常”发生的事情往往不是程序中的瓶颈 - 实际的瓶颈是什么通常令人惊讶。在执行任何优化之前,您应该编写一些简单的代码并测量程序的实际性能。

我想了很多不同的方法,包括二分查找和简单的复制插入,但发现很难决定。

假设这是瓶颈,不同方法的 big-O 性能在此无关紧要,因为您的数组很小。尝试几种不同的方法、测量结果、查看哪种方法效果最好并选择该方法会更容易。如果您已按照第一段中的建议进行操作,则您已经拥有一个可用于此步骤的分析器设置。

【讨论】:

  • (是的,我做到了,根据分析器,这部分程序绘制了 35% 的时间。)
  • 您是否尝试过在非托管代码中实现此功能?
  • @mafutct:35% 并不是一个巨大的数字。即使您将程序这部分的时间减少到零(一个不切实际的理想),您的程序总体上仍然会很慢 - 甚至不会快两倍。也许您需要找到一种全新的方法,而不是对程序的这一部分进行微优化?您需要按排序顺序存储整个列表还是优先队列适合您?
  • @Gin:不,如果可能的话,我宁愿避免这种情况,尽管我也感觉到从长远来看可能需要它。
  • @Mark:实际上,我认为 35% 是一个巨大的数字——例如,将其减少到 10% 会显着提高性能。当然,我也在研究另外 65%。
【解决方案4】:

对于插入到中间,LinkedList&lt;int&gt; 将是最快的选择 - 其他任何事情都涉及复制数据。在 14 个元素中,不要强调二分搜索等 - 只需前进到您想要的项目:

using System;
using System.Collections.Generic;
static class Program
{
    static void Main()
    {

        LinkedList<int> data = new LinkedList<int>();
        Random rand = new Random(12345);
        for (int i = 0; i < 20; i++)
        {
            data.InsertSortedValue(rand.Next(300));
        }
        foreach (int i in data) Console.WriteLine(i);
    }
}
static class LinkedListExtensions {
    public static void InsertSortedValue(this LinkedList<int> list, int value)
    {
        LinkedListNode<int> node = list.First, next;
        if (node == null || node.Value > value)
        {
            list.AddFirst(value);
        }
        else
        { 
            while ((next = node.Next) != null && next.Value < value)
                node = next;
            list.AddAfter(node, value);
        }
    }
}

【讨论】:

  • +1 for O(1) 当你想要插入项目时,如果你写一个简单的二分搜索,它会得到更好的答案。
  • 我认为跟随指针(包括未缓存的查找和页面错误)与数组复制相比会产生更差的性能?你有其他经验吗?
  • @mafutrct - 数组复制不是免费的。要确定您必须针对您的特定场景进行概要分析,我不会期望在这里走一些参考文献会成为杀手。如果你想要 absolute 性能,硬编码到 LinkedList&lt;int&gt; 并使用 &lt; / &gt; 运算符而不是 Comparer&lt;T&gt;.Default
  • @Saeed - 由于您没有基于索引器的访问权限,因此很难对链表进行二进制搜索。
【解决方案5】:

在这里使用蛮力方法是最好的决定,因为 14 不是数字 :)。然而,这不是一个可扩展的决定,因为如果有一天 14 变成 14000 会导致问题

【讨论】:

  • (在我的情况下,14 是一个真实世界的常数,因此可以假设它始终保持在低 2 位范围内。)
  • @mafutrct:那么蛮力是最好的方法
【解决方案6】:

您的阵列最常见的操作是什么? 插入?阅读?

  • 堆数据结构将为您提供两者的 O(log(14))。 SortedDictionary 可能会影响您的表现。
  • 使用简单数组将为您提供 O(1) 的读取时间和 O(14) 的插入时间。

顺便说一句,您尝试过 System.Collections.Generic.SortedDictionary 和 System.Collections.Generic.SortedList 吗?

【讨论】:

    【解决方案7】:

    如果您使用的是 .Net 4,您应该查看SortedSet&lt;T&gt;。否则,请查看 SortedDictionary&lt;TKey, TValue&gt;,将 TValue 设置为 object,然后将 null 放入其中,因为您只是对键感兴趣。

    【讨论】:

    • 我认为 SortedSet 不允许欺骗。
    • 是的,如果你第二次添加一个值,关于一个元素多次存在的信息就会丢失。但是在您的要求中,您没有谈论重复。
    【解决方案8】:

    如果数组上没有重复值并且可能的值不会改变,那么值等于索引的固定大小的数组可能是一个不错的选择

    插入和读取都是 O(1)

    【讨论】:

    • 正确。这就是我想在回答中说的。
    【解决方案9】:

    您有一个从 1 到 34 的可能值范围,这个范围相当窄。因此,最快的方法可能是使用具有 34 个插槽的阵列。要插入一个数字 n 只需执行 array[n-1]++ 并删除它执行 array[n.1]-- (如果 n>0)。

    要检查集合中是否存在值,请执行 array[n-1]>0.

    edit:该死的……丹尼更快了。 :)

    【讨论】:

      【解决方案10】:

      写一个方法接受一个整数数组并使用冒泡排序对它们进行排序。该方法不允许创建任何额外的数组。冒泡排序是一种简单的排序算法,它通过循环遍历要排序的数组,比较每对相邻元素,如果它们的顺序错误则交换它们。

      【讨论】:

        猜你喜欢
        • 2023-04-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-11
        • 1970-01-01
        • 2019-06-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多