从数组中找到相等或最接近的较小值

时间:2012-11-04 00:57:12

标签: c# performance

假设我有这个数组(它实际上是255长,值最多为int.MaxValue)

int[] lows = {0,9,0,0,5,0,0,8,4,1,3,0,0,0,0};

从这个数组我想得到一个值等于较小的索引到我的数字。

number = 7 -> index = 4
number = 2 -> index = 9
number = 8 -> index = 7
number = 9 -> index = 1

找到它的最快方法是什么?

到目前为止,我已经使用了线性搜索,但事实证明这对我的需求来说效率太低,因为即使这个数组只有255个长度,也会搜索数百万次的值。

我需要与java中使用的 TreeSet.floor(E)相同的东西。我想使用 Dictionary ,但我不知道它是否可以找到我想要的第一个更小或相等的值。

4 个答案:

答案 0 :(得分:3)

对数组进行排序,然后进行二进制搜索以查找值。

请参阅:

https://en.wikipedia.org/wiki/Binary_search

Array.BinarySearch Method

答案 1 :(得分:1)

如果它没有排序(或以其他方式保存在可以帮助搜索的成员之间存在关系的数据结构中),那么必须检查每个成员以找到正确的

最简单的解决方案可能是对它进行排序,然后进行二进制切割/搜索以找到符合条件的元素。


如果你希望效率能够仍然使用未排序的数组,请在数组的某处保持sorted标志(即,将整个事物转换为包含指示符和数组的类),以指示列表排序。

然后,只要数组发生更改,就将此标志设置为false

在您要进行搜索的位置,首先检查sorted标志,如果数组设置为false,则对数组进行排序(将其设置为true作为其中一部分处理)。如果标志为true,则绕过排序。

这样,您只需在需要时进行排序。如果数组自上次排序以来没有改变,那么重新排序是没有意义的。

如果用户需要,您还可以维护原始的未排序列表,将排序后的列表保持为带有该类的附加数组(类别的另一个优点 - 如果您的数组有用)。那样,你什么都不会失去。您拥有可供用户使用的原始未触动数据,以及快速有效查找所需元素的方法。

您的对象(排序后)将包含:

int[] lows       = {0,9,0,0,5,0,0,8,4,1,3,0,0,0,0};
int[] sortedlows = {0,0,0,0,0,0,0,0,0,1,3,4,5,8,9};
boolean isSorted = true;

如果您随后将that_object[0]更改为3,则最终会:

int[] lows       = {3,9,0,0,5,0,0,8,4,1,3,0,0,0,0};
int[] sortedlows = {0,0,0,0,0,0,0,0,0,1,3,4,5,8,9};
boolean isSorted = false;

表示在搜索sortedLows之前需要进行排序。


请记住,将其转换为课程不是要求。如果您担心它的性能(特别是通过getter方法访问数组元素),您可以维护数组并标记自己,同时仍允许直接访问未排序的数组。您只需确保代码中更改数组的每个位置也正确设置标记。

但是你应该在走这条路之前测量性能。基于类的方式“更安全”,因为对象本身控制着整个事物。

答案 2 :(得分:1)

首先,规范化数据:

public static Dictionary<int, int> GetNormalised(int[] data)
{
    var normalised = data.Select((value, index) => new { value, index })
        .GroupBy(p => p.value, p => p.index)
        .Where(p => p.Key != 0)
        .OrderBy(p => p.Key)
        .ToDictionary(p => p.Key, p => p.Min());
    return normalised;
}

搜索方法:

public static int GetNearest(Dictionary<int, int> normalised, int value)
{
    var res = normalised.Where(p => p.Key <= value)
        .OrderBy(p => value - p.Key)
        .Select(p => (int?)p.Value)
        .FirstOrDefault();

    if (res == null)
    {
        throw new ArgumentOutOfRangeException("value", "Not found");
    }

    return res.Value;
}

单元测试:

[TestMethod]
public void GetNearestTest()
{
    var data = new[] { 0, 9, 0, 0, 5, 0, 0, 8, 4, 1, 3, 0, 0, 0, 0 };
    var normalised = Program.GetNormalised(data);

    var value = 7;
    var expected = 4;
    var actual = Program_Accessor.GetNearest(normalised, value);
    Assert.AreEqual(expected, actual);

    value = 2;
    expected = 9;
    actual = Program_Accessor.GetNearest(normalised, value);
    Assert.AreEqual(expected, actual);

    value = 8;
    expected = 7;
    actual = Program_Accessor.GetNearest(normalised, value);
    Assert.AreEqual(expected, actual);

    value = 9;
    expected = 1;
    actual = Program_Accessor.GetNearest(normalised, value);
    Assert.AreEqual(expected, actual);        
}

优化性能缓存所有使用的结果。

答案 3 :(得分:1)

要在C#中模拟Java TreeSet,请使用C#class:SortedDictionary或SortedSet; Java TreeSet中的mock floor方法,使用LINQ方法,以获得最小值。 SortedSet data = new SortedSet(); data.Where(p =&gt; p&lt; prices [i])。OrderByDescending(p =&gt; p).Take(1)

基于HackerRank最低成本算法,Java TreeSet实现解决方案工作正常,但C#SortedDictionary,SortedSet超时。
详情,请参阅我的编码博客:http://juliachencoding.blogspot.ca/2016/11/c-sorteddictionary-minimum-cost.html

所以,C#SortedSet类GetViewBetween可以做同样的事情。见GetViewBetween API

有帖子:SortedSet / SortedList with better LINQ performance?