【问题标题】:Get Array Max and Min between Start and End Indexes获取开始和结束索引之间的数组最大值和最小值
【发布时间】:2014-07-16 15:48:52
【问题描述】:

我需要一种简单、轻量级的方法来获取双精度数组的 ma​​xmin 值。诀窍是我只需要数组中两个索引之间的最大值和最小值。

内置的 arrayOfDoubles.Max()arrayOfDoubles.Min()工作,因为它们会检查整个数组。

此代码将不断运行,并且需要有一定的效率。也就是说,简单性和可读性比速度本身更重要。

这是获取两个索引之间的最大值和最小值的一种方法:

double[] array = new double[8] { 3, 1, 15, 5, 11, 9, 13, 7 };

int startIndex = 3;
int endIndex = 6;

// Get max and min between these two indexes in the array
double max = GetMax(array, startIndex, endIndex);
double min = GetMin(array, startIndex, endIndex);

Console.WriteLine("Max = " + max + ", Min = " + min);

这是 GetMax 的代码,GetMin 非常相似:

private static double GetMax(double[] array, int startIndex, int endIndex)
{
    double max = double.MinValue;

    for (int i = startIndex; i <= endIndex; i++)
    {
        // Increase max with each bigger value
        max = Math.Max(max, array[i]);
    }

    return max;
}

输出:Max = 13, Min = 5

问题:我可以通过哪些其他方式获得相同的结果,并且可能更简单且不需要太多开销?

【问题讨论】:

  • 真的没有比这更简单的了。
  • 唯一的可能是在同一个循环中同时做最小值和最大值。您可以手动内联 Math.Min 和 Math.Max 调用,但无论如何这些调用可能会自动内联。
  • 数组内容多久改变一次?每次迭代之间是否所有元素都发生了变化,还是只有一些元素会发生变化?我的意思是,可能有更好的结构来存储您的数据..
  • 顺便问一下,我们在这里讨论的数组有多大?

标签: c# arrays max min


【解决方案1】:
var list = array.Skip(startIndex).Take(endIndex - startIndex + 1).ToList();
var min = list.Min();
var max = list.Max();

【讨论】:

  • ToList() 是不必要的。
  • @slippyr4 需要对数组进行一次迭代(对子列表进行两次迭代)。
  • 不,不是。第二次迭代导致重新查询。但是对于一个 8 元素数组,除了实例化一个 List 对象之外,requery 不会是一个世界。所以,再一次, ToList() 是没有必要的。
  • @slippyr4 The second iteration causes a requery,仅在子列表上(使用 ToList() 时)。所以我不同意,但无论如何,至少,它并不比不使用它更糟糕。
  • 鉴于不使用 ToList() 会产生相同的结果,我坚持认为它是不必要的。
【解决方案2】:

你也可以这样做:

var segment = new ArraySegment<double>(array, startIndex, endIndex - startIndex + 1);
var min = segment.Min();
var max = segment.Max();

【讨论】:

  • +1 for ArraySegment:这个struct有效地封装了所有子数组边界检查,同​​时还实现了IList&lt;T&gt;接口!
【解决方案3】:

很明显,在 unsorted 数组中查找最小和最大元素需要 O(n) 算法,因为无论你如何做,你都必须检查每个元素,至少一次.因此,您唯一希望得到的优化是特定于实现的,并且只能在常量方面为您带来收益。

但是,您可以使用一个聪明的技巧来仅使用 3*[n/2] 比较来查找数组中的最小值和最大值。虽然这不是渐近改进,但与直接算法中所需的典型 2*n 比较相比,它是 n/2 的改进。因此,如果您希望执行大量最小/最大计算,那么可能值得考虑将其作为一个选项。

你的做法是这样的:

  • 到目前为止,在两个变量中保持最小和最大元素
  • 在每次迭代中:
    • 比较接下来的两个元素以确定哪个更大
    • 将较大的元素与最大值进行比较(必要时交换)
    • 将较小的元素与最小值进行比较(必要时交换)

您将执行该循环的 n/2 次迭代,每次迭代进行 3 次比较,总共 3*[n/2] 次操作。

这是一个实现:

void GetMinMax(double[] array, int start, int end, out double min, out double max)
{
    min = array[start];
    max = array[start];

    if((end - start) % 2 == 0)               // if there's an odd number of elements
       start = start + 1;                    //   skip the first one

    for (int i = start + 1; i <= end; i += 2)
    {
        if(array[i] > array[i-1])
        {
            if(array[i] > max) max = array[i];
            if(array[i - 1] < min) min = array[i - 1];
        } else {
            if(array[i - 1] > max) max = array[i - 1];
            if(array[i] < min) min = array[i];
        }
    }
}

【讨论】:

  • 我喜欢这是多么彻底和高效!然而,我的简单性和可读性标准让我得到了另一个答案。
  • @andrewralon - 很高兴您对答案表示赞赏。我绝对不建议例行执行此操作,因为节省的成本通常不能证明更复杂的代码是合理的。 LINQ 解决方案在简单性/表现力方面绝对胜出,但没有提高效率。
【解决方案4】:

您可以通过扫描阵列一次来做到这一点。这是在无数面试问题中提出的标准最小/最大跟踪算法,因此可读性应该不是问题。

void GetMinMax(double[] array, int start, int end, out double min, out double max)
{
    min = Double.MaxValue;
    max = Double.MinValue;

    // TODO: error checks for out-of-bounds, null, etc...
    for (int i = start; i <= end; ++i)
    {
        if (array[i] > max) max = array[i];
        if (array[i] < min) min = array[i];
    }
}

编辑:

使用 Usman Zafar 的 answer 来简化并消除对多个错误检查的需要:

void GetMinMax(IList<double> list, out double min, out double max)
{
    // TODO: null/empty check for list.

    min = Double.MaxValue;
    max = Double.MinValue;

    for (int i = 0; i < list.Count; ++i)
    {
        if (list[i] > max) max = list[i];
        if (list[i] < min) min = list[i];
    }
}

现在拨打任何IList&lt;double&gt;,或者在您的情况下:new ArraySegment&lt;double&gt;(array, 3, 4)

【讨论】:

    【解决方案5】:

    使用 LINQ 的甜味剂:

            var res = array.Skip(startIndex).Take(1 + endIndex - startIndex );
            var min = res.Min();
            var max = res.Max();
    

    【讨论】:

    • 你忘了取最后一个元素。将您的代码与有问题的预期值进行比较。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-06
    • 2013-01-11
    • 2021-12-20
    • 2021-08-11
    • 2021-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多