C#数组特定范围内的最小值

4

大家好! 在C#中,如何获取int数组特定范围内的最小值? 例如: int[] array= new int{1,2,3,4,5,6,7,8,76,45}; 我想要获取第3个到第8个元素之间的最小值。 也许可以通过LINQ查询来实现?


它是否始终至少有8个元素? - ChaosPandion
7个回答

13
array.Skip(2).Take(5).Min();

3
不,这不是因为它的作用没有立即清晰可见。它很聪明,看起来也很聪明,但不是最易读的解决方案,因此应该被拒绝。抱歉。 - jason
6
@Jason:跳过2个项目,取5个项目,然后得到最小值。有什么不清楚的吗? - poindexter12
3
@Jon Skeet:我感觉重点被忽略了,特别是如果你不同意的话。是的,可以理解它跳过了两个数字,取了五个数字并找到了最小值。但不清楚的是它是否解决了手头的问题。这需要稍微思考一下(虽然很少),而我之前提到的解决方案则不需要。我感觉这个方案之所以得到投票支持是因为它聪明巧妙(但聪明的代码并不好!)。 - jason
1
@Jason:我认为这比Thomas的方法更容易理解。使用“Skip”和“Take”立即表明元素的与过滤无关......而更一般的“Where”子句则迫使读者仔细考虑到底发生了什么。请注意,对于整数数组,很容易意外地颠倒valueindex参数。如果您想使它更与“开始”和“结束”相关,则Eric的方法是一个不错的选择。 - Jon Skeet
1
@Jason:不,我们并不是在寻找具有某种属性的的最小值。这些值是整数-它们没有索引。序列元素有索引,但是在Thomas的答案中由value变量引用的值只是一个整数。如果你要过载“值”这个词来表示“序列中的条目”和“序列中条目的值”,那么在我看来,你需要更大的努力来说服我们你在争论简单性 :) (顺便评论一下我的答案吗?) - Jon Skeet
显示剩余14条评论

6

我想我可以为此添加我的意见。由于Jason反对我们说跳过多少而不是结束索引,我们可以添加一个简单的扩展方法:

public static IEnumerable<T> WithIndexBetween<T>(this IEnumerable<T> source,
    int startInclusive, int endExclusive)
{
    // The two values can be the same, yielding no results... but they must
    // indicate a reasonable range
    if (endExclusive < startInclusive)
    {
        throw new ArgumentOutOfRangeException("endExclusive");
    }
    return source.Skip(startInclusive).Take(endExclusive - startInclusive);
}

然后:

int min = array.WithIndexBetween(2, 7).Min();

根据个人喜好调整扩展方法名称。(命名很难,我不会花费太长时间在这里想出一个好的名称 :)


是的,很好,因为现在它读起来就像问题陈述一样。请注意,它的读法是“取那些索引介于两个和七个之间的序列值,并取最小值”,这就是我对Thomas'阅读方式的理解,这也是我一开始更喜欢它的原因。所以,我喜欢这个翻译。 - jason
我正准备更新我的答案,内容与你的答案类似,然后我看到了你的答案... - Thomas Levesque
Jon,这太棒了!我喜欢扩展方法。而且Jason,我喜欢你的坚持,尤其是对抗Jon Skeet! :) - decyclone

3
int[] arr = {0,1,2,3,4,5,6,7,8};
int start = 3;
int end = 8;
int min = arr.Skip(start - 1).Take(end - start).Min();

3
int min = array.Where((value, index) => index >= 2 && index <= 7).Min(); 

编辑

实际上,以上方法相当低效,因为它枚举了整个序列,即使我们对索引大于7的项目不感兴趣。更好的解决方案是使用TakeWhile

int min = array.TakeWhile((value, index) => index <= 7).Skip(2).Min();

很遗憾,这段文字不太易读... 使其更好的选项可能是创建一个自定义扩展方法,就像Jon的答案中所示。


2

还有一个选项:

int start = 3;
int end = 8;
var min = Enumerable.Range(start - 1,end - start).Select(idx => array[idx]).Min();

据我所知,如果你需要取范围接近结尾并且数组非常长的情况下,这种方法"理论上"更快。
这是因为(我再说一遍,据我所知)Skip()没有考虑到它是一个数组(即可以在O(1)的时间内随机访问),仍然枚举它。

1
是的,在使用数组(或其他IList<T>)的特定情况下,它更快 - 但当然在一般序列情况下会崩溃。当然,“Skip”在未来的版本中可能也会被修复 :) - Jon Skeet
当然,我真的希望它能够修复。与此同时,我的只是一种警告 :) - digEmAll
尽管这不是我通常会选择的解决方案,但+1 为这个警告。 - Jon Skeet

0

就我个人而言,我更喜欢这样:

public static class ArrayExtensions {
    public static bool ArrayAndIndexesAreValid(
        T[] array,
        int startInclusive,
        int endExclusive
    ) {
    return array != null &&
           array.Length > 0 &&
           startInclusive >= 0 && startInclusive < array.Length &&
           endExclusive >= 1 && endExclusive <= array.Length &&
           startInclusive < endExclusive;
    }
    public static IEnumerable<T> Slice<T>(
        this T[] array,
        int startInclusive,
        int endExclusive
    ) {
        Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
            array,
            startInclusive,
            endExclusive)
        );
        for (int index = startInclusive; index < endExclusive; index++) {
            yield return array[index];
        }
    }
    public static T MinimumInIndexRange<T>(
        this T[] array,
        int startInclusive,
        int endExclusive
    ) where T : IComparable {
        Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
            array,
            startInclusive,
            endExclusive)
        );
        return array.Slice(startInclusive, endExclusive).Min();
    }

    public static T MaximumInIndexRange<T>(
        this T[] array,
        int startInclusive,
        int endExclusive
    ) where T : IComparable {
        Contract.Requires<ArgumentException>(ArrayAndIndexesAreValid(
            array,
            startInclusive,
            endExclusive)
        );
        return array.Slice(startInclusive, endExclusive).Max();
    }
}

如果切片不包含任何元素,它将返回int.MaxValue,这可能不是一个好主意。它还混合了“查找最小值”和“考虑数组切片”的逻辑,而基于LINQ的解决方案则没有这样做。如果您需要找到数组切片的最大值,则需要重复所有切片逻辑。 - Jon Skeet
通过不将其泛化,您还迫使自己在需要从long[]等中获取最小值时重新实现它。在我看来,将此操作泛化是易如反掌的。当然,在返回空数组切片的值时这样做很困难... - Jon Skeet
@Jon Skeet: 它在空时抛出异常(这可能不是首选项;请参见第二个“Contract.Requires”)。我一会儿会回复您的第二条评论。 - jason
@Jon Skeet:是的,很公正。我已经编辑了以解决您的顾虑。不需要重新实现long[]中的最小值,也不需要重新实现计算最大值的分割等操作。由于空序列没有最小值(序列中最小值的定义是值v 在序列中 ,使得对于序列中所有其他值w,都有v<=w;这里,“<=”是一个通用的全序关系),我们应该在这种情况下引发异常。如果愿意的话,可以包含带有默认值的重载,以在空数组的情况下用作最小/最大值。 - jason
@Jon Skeet:你抓住我了;在我进一步思考但在看到你的评论之前,我删除了关于YAGNI的评论。我完全同意你的观点,将其通用化甚至远远不足以算作过度工程化。 - jason
显示剩余4条评论

0
array.Skip(3).Take(4).Min();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接