使用LINQ在有序列表中查找“空间”的最有效方法

时间:2016-11-04 01:41:12

标签: c# algorithm linq time-complexity

我正在尝试做的事情就像迭代

这样的东西
IEnumerable<int> ids = items.Select(item => item.Id); 

0 1 5 7

然后我想选择2,因为这是i范围内的最小数量[0, 7+1]i-1位于列表中,i isn'吨。如果列表中没有0,则会选择0

什么是最紧凑,高效,可读,最花哨,最LINQiest的方法?

如果我需要提供更全面的解释,请告诉我。

4 个答案:

答案 0 :(得分:7)

对于非空集合,这可能是最短的实现,但它是O(N)(它还假设您拥有的Id字段是数字,但不会改变任何内容):

var missingIndex = list.TakeWhile((v, i) => v == i).Last() + 1;

工作原理:它遍历列表,直到值与其索引匹配。然后在某些值与其索引不匹配之后 - 是一个差距。

<强> UPD

@Rob的帮助下修复了处理以非零开头的列表:

var missingIndex = list.TakeWhile((v, i) => v == i).Select(i => i + 1).LastOrDefault();

对于高效的解决方案,您需要使用二进制搜索,这种搜索在命令式样式中看起来会更好(请参阅非linq&#39; y)。

答案 1 :(得分:2)

编写扩展方法来实现这一点并不困难。

    //examples
    class Program
    {
        static void Main(string[] args)
        {
            //Your original example
            var items = new List<int> { 0, 1, 5, 7 };
            var gap = items.FindFirstGap();
            Console.WriteLine(gap); //shows 2

            //No gaps
            items = new List<int> { 0, 1, 2, 3 };
            gap = items.FindFirstGap();
            Console.WriteLine(gap); //shows 4

            //no 0
            items = new List<int> { 1, 2, 3, 4 };
            gap = items.FindFirstGap();
            Console.WriteLine(gap); //shows 0

            Console.ReadLine();
        }
    }

以下解决方案是O(N)并且支持任何struct数据类型,您可以提供函数来计算&#34; + 1&#34;和#34;等于&#34;对于。我做了一个重载,为你做了所有基本整数类型逻辑,所以你不需要在调用者处指定它。

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    public static class ExtensionMethods
    {
        public static T FindFirstGap<T>(this IEnumerable<T> @this, Func<T, T> getNext, IEqualityComparer<T> comparer) where T : struct
        {
            using (var enumerator = @this.GetEnumerator())
            {
                T nextItem = default(T);
                while (enumerator.MoveNext())
                {
                    var currentItem = enumerator.Current;
                    if (!comparer.Equals(currentItem, nextItem))
                        return nextItem;
                    nextItem = getNext(currentItem);
                }
                return nextItem;
            }
        }

        public static T FindFirstGap<T>(this IEnumerable<T> @this, Func<T, T> getNext) where T : struct
        {
            return FindFirstGap(@this, getNext, EqualityComparer<T>.Default);
        }

        public static int FindFirstGap(this IEnumerable<int> @this)
        {
            return FindFirstGap(@this, i => i + 1, EqualityComparer<int>.Default);
        }

        public static long FindFirstGap(this IEnumerable<long> @this)
        {
            return FindFirstGap(@this, i => i + 1, EqualityComparer<long>.Default);
        }

        public static short FindFirstGap(this IEnumerable<short> @this)
        {
            return FindFirstGap(@this, i => (short)(i + 1), EqualityComparer<short>.Default);
        }

        public static byte FindFirstGap(this IEnumerable<byte> @this)
        {
            return FindFirstGap(@this, i => (byte)(i + 1), EqualityComparer<byte>.Default);
        }
    }
}

答案 2 :(得分:1)

这是我的看法:

    private static int GetGap(int[] items)
    {
        // return items.TakeWhile((v, i) => v == i).Select(i => i + 1).LastOrDefault();
        if (!items.Any()) return 0;
        if (items.First() != 0) return 0;
        int counter = 0;
        return items.ToList().LastOrDefault(x => x == counter++) + 1;
    }

试验:

   public static IEnumerable TestCases
    {
        get
        {
            yield return new TestCaseData(new int[] { 0, 1, 5, 7 }, 2).SetName("1");
            yield return new TestCaseData(new int[] { 0, 1, 2, 3, 4 },5).SetName("2");
            yield return new TestCaseData(new int[] { 1, 2, 3, 4 },0).SetName("3");
            yield return new TestCaseData(new int[] { -2, -1, 1, 2, 3, 4 }, 0).SetName("4");
            yield return new TestCaseData(new int[] { -2, -1, 0, 1, 2, 3, 4 },0).SetName("5");
            yield return new TestCaseData(new int[] { 0 },1).SetName("6");
            yield return new TestCaseData(new int[] { },0).SetName("7");

        }
    }

    [TestCaseSource("TestCases")]
    public void Test(int[] items, int expected ) {
        Assert.AreEqual(expected, GetGap(items));
    }

答案 3 :(得分:0)

寻找第一个差距的指标, 如果它符合您的要求,请试试这个。

 int[] items = new int[] { 0,1, 3, 4, 6 }; 
 var firstGap = items.Select((index, value) => new { value, index })
                     .FirstOrDefault(c => c.value == 0 ? items[c.value] != 0 : items[c.value] != items[c.value - 1] + 1).value;