【问题标题】:How to get the Point with minimal X from an array of Points without using OrderBy?如何在不使用 OrderBy 的情况下从点数组中获取具有最小 X 的点?
【发布时间】:2009-10-15 18:56:53
【问题描述】:

想象一下我有

 var points = new Point[]
 {
     new Point(1, 2),
     new Point(2, 3)
 };

为了得到最小 X 的点,我可以:

 var result = points.OrderBy(point => point.X).First();

但对于大型数组,我认为这不是更快的选择。有更快的替代方案吗?

【问题讨论】:

    标签: c# .net linq linq-to-objects


    【解决方案1】:

    最好用

    int x = points.Min(p => p.X);
    var result = points.First(p => p.X == x);
    

    因为这消除了对该列表进行排序的必要性(即,它是 O(n) 而不是 O(n log n))。此外,它比使用OrderByFirst 更清晰。

    你甚至可以写一个扩展方法如下:

    static class IEnumerableExtensions {
        public static T SelectMin<T>(this IEnumerable<T> source, Func<T, int> selector) {
            if (source == null) {
                throw new ArgumentNullException("source");
            }
    
            int min = 0;
            T returnValue = default(T);
            bool flag = false;
    
            foreach (T t in source) {
                int value = selector(t);
                if (flag) {
                    if (value < min) {
                        returnValue = t;
                        min = value;
                    }
                }
                else {
                    min = value;
                    returnValue = t;
                    flag = true;
                }
            }
    
            if (!flag) {
                throw new InvalidOperationException("source is empty");
            }
    
            return returnValue;
        }
    

    用法:

    IEnumerable<Point> points;
    Point minPoint = points.SelectMin(p => p.X);
    

    您可以根据自己的需要进行概括。这样做的好处是它避免了两次遍历列表的可能性。

    【讨论】:

    • 除非我的逻辑失败了。Min() 必须检查每个元素?然后它返回该值。首先,然后再次开始遍历同一个列表,直到找到具有该 X 值的第一个点。最坏的情况是 2*n?
    • 我添加了一个扩展方法来解决这个问题。其次,2 * nO(n)
    【解决方案2】:

    以下应该是最快的,但不是最漂亮的方法:

    public static T MinValue<T>(this IEnumerable<T> e, Func<T, int> f)
    {
        if (e == null) throw new ArgumentException();
        var en = e.GetEnumerator();
        if (!en.MoveNext()) throw new ArgumentException();
        int min = f(en.Current);
        T minValue = en.Current;
        int possible = int.MinValue;
        while (en.MoveNext())
        {
            possible = f(en.Current);
            if (min > possible)
            {
                min = possible;
                minValue = en.Current;
            }
        }
        return minValue;
    }
    

    我只包含了int扩展,但是做其他的就很简单了。

    编辑:根据 Jason 修改。

    【讨论】:

    • 评论:Count 是不好调用的,除非在IEnumerable 上绝对必要,因为它会导致列表被遍历(在一般情况下)。如果穿过IEnumerable 很贵,那就太糟糕了。
    • 另一条评论:请注意,在while 循环中,您对当前元素计算f(en.Current) 两次。如果f 很昂贵,那么这不是最优的。
    【解决方案3】:

    对于今天想要执行此操作的任何人,MoreLinq 是 NuGet 提供的一个库,其中包括其他答案提供的运算符以及框架中不存在的其他几个有用的操作。

    【讨论】:

      猜你喜欢
      • 2011-02-05
      • 1970-01-01
      • 2020-06-14
      • 1970-01-01
      • 2021-11-29
      • 2013-11-25
      • 2020-03-25
      • 1970-01-01
      • 2020-08-12
      相关资源
      最近更新 更多