【问题标题】:Efficient way to get first missing element in ordered sequence?在有序序列中获取第一个缺失元素的有效方法?
【发布时间】:2010-11-09 01:47:53
【问题描述】:

我有一个像 {1, 3, 5, 6, 8, 9} 这样的有序序列,如果序列不包含任何缺失元素,我想获取第一个缺失元素(示例中为 2)或 max()。 现在我这样做了:

public static int GetRegisterNumber<T>(this IQueryable<T> enumerable, Func<T, bool> whereFunc, Func<T, int?> selectFunc)
{
    var regNums = enumerable.OrderBy(selectFunc).Where(whereFunc).ToArray();

    if (regNums.Count() == 0)
    {
        return 1;
    }

    for (int i = 0; i < regNums.Count(); i++)
    {
        if (i + 1 != regNums[i])
        {
            return regNums[i].Value + 1;
        }
    }

    return regNums.Last().Value + 1;
}

但我认为有更快的方法。有什么建议吗?

【问题讨论】:

  • Count() 非常非常糟糕...将发布...
  • 它只有整数吗?他们总是积极的吗?有重复吗?
  • 总是正整数,没有重复
  • 帖子的标题和第一句话说明你有一个有序的序列。但是,您的方法所做的第一件事是 OrderBy。如果您将其计算为算法运行时间的一部分,那么您应该编辑帖子以说明您有一个需要排序和过滤的序列,然后搜索第一个缺失的元素。

标签: c# linq linq-to-sql search


【解决方案1】:

建议:通过分析器运行您的代码。然后你就会知道它慢的地方。直观地说,OrderBy 是这个程序中最慢的东西。但是对于最慢的事情的直觉往往是非常非常错误的。使用分析器。

当然,您还应该消除此程序中的大量低效率问题。请记住,Count() 通过重新枚举来计算序列。 Count() 不知道自上次计数以来您没有更改序列!您可能希望存储计数而不是每次都重新计算,或者使用 Length,因为您有一个数组。

【讨论】:

  • .OrderBy 无论如何都在数据库中执行,所以我无法摆脱它
  • 您也可以使用 Any() 而不是将 Count() 与 0 进行比较
  • @xumix 在这种特定情况下您可能不需要重新排序。
  • 在大多数情况下(99.999%),序列将包含元素,因此 .Any 或 .Count() 将进行额外的数据库查询...
  • 为什么要查询数据库?您已将内容转储到一个数组中,现在正在查询该数组。
【解决方案2】:

我可能会看下面的内容; Where 可以在外面完成(老实说选择器也可以):

如果您希望从 1 开始:

public static int GetRegisterNumber<T>(this IEnumerable<T> enumerable,
    Func<T, int> selectFunc)
{
    int expected = 1;
    foreach (T item in enumerable) {
        if (selectFunc(item) != expected) return expected;
        expected++;
    }
    return expected;
}

从列表中的第一项开始:

public static int GetRegisterNumber<T>(this IEnumerable<T> enumerable,
    Func<T, int> selectFunc)
{
    bool first = true;
    int prev = -1;
    foreach (T item in enumerable)
    {
        int val = selectFunc(item);
        if(first) {
            prev = val;
            first = false;
        } else if (val != prev + 1) {
            return prev + 1;
        }
        prev = val;
    }
    return first ? 1 : prev + 1;
}

不清楚你想如何处理空值,所以我没有。请注意,这只迭代一次,并不会缓冲所有内容。

【讨论】:

    【解决方案3】:

    如前所述,首先使用分析器来查找速度较慢的位置。如果序列真的很大,排序很慢,可以使用radix sort,也就是O(kn),其中k是最大位数,n是序列中的元素个数。基于比较的排序算法通常是 O(n logn)。

    这样整个算法将是 O(kn),取决于 n,渐近速度更快,因此更具可扩展性。

    【讨论】:

      【解决方案4】:

      假设传入的值序列已经排序,如何:

      var upperBoundValue = values.Last() + 1;
      var firstMissingItem = Enumerable.Range(1, upperBoundValue).Except(values).First();
      

      如果您正在迭代地执行此操作,您可以通过将索引存储到您在序列中发现间隙的最后一个位置来优化该过程,然后从那里开始下一次迭代。

      【讨论】:

        【解决方案5】:

        为什么不做一些类似二分搜索的事情呢?

        假设您有一个包含 10 个元素的列表。阅读第一个元素。然后阅读第五个元素。如果第五个元素不是第一个元素+ 4,那么你知道有一个缺失的数字,否则你知道没有。然后像这样迭代直到找到第一个丢失的元素,或者到达列表的末尾。

        这当然假设您知道大小(问题中没有明确提到),但是您已经转换为数组,所以您应该知道。

        O(log N) 而不是 O(n)

        【讨论】:

        • 但是对于订购,您不会花费 O(logn)。按照他的说法,向量没有排序,所以二分查找无济于事(除非你先对其进行排序)
        • 是的,最后一步是O(logN)而不是O(n),但是排序大概还是O(n*logN)。
        • 帖子的标题和第一句都说序列是有序的。
        【解决方案6】:

        假设您的OrderByWhere 已经被应用:

        int firstMissing = collection.TakeWhile((x, i) => x == ++i).LastOrDefault() + 1;
        

        【讨论】:

        • 问题文本的一个很好的答案。请注意,有一个 LinqToSql 标记 - TakeWhile 不会翻译成 Sql。
        【解决方案7】:

        您在问题中放置了一个 LinqToSql 标记。我假设您正在寻找“第一个可用”的 ID,以便您可以使用此 ID 创建新记录。请考虑在数据库中打开 IDENTITY。

        【讨论】:

        • 通过IDENTITY,你的意思是OID吗?
        • @active92 没有。由于我们讨论的是LinqToSql,所以数据库是MSSql Server,而不是PostgreSQL。
        【解决方案8】:

        编辑:我刚刚注意到enumerableIQueryable&lt;T&gt;selectFuncwhereFuncFunc&lt;T, _&gt; 类型。这将导致调用OrderByWhereEnumerable 版本,而不是使用数据库调用。您可能希望将它们切换为 Expression&lt;Func&lt;T, _&gt;&gt;

        如果您不想先订购regNums,这里有一个 O(n) 高尔夫风格的解决方案:

        var max = regNums.Max(i => (int?)i) ?? 0;
        return Enumerable.Range(1, max + 1)
                         .Except(regNums)
                         .Min();
        

        按行:

        1. 通过转换为int?,如果regNums 为空,Max 将返回null,合并到0

        2. 构建所有可能寄存器的序列,包括我们的下一个值(如果已满)。

        3. 减去当前的寄存器组。

        4. 选择最低的。

        【讨论】:

        • 关于表达式stackoverflow.com/questions/1098341/…
        猜你喜欢
        • 1970-01-01
        • 2023-03-24
        • 2013-08-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-28
        • 1970-01-01
        相关资源
        最近更新 更多