什么是最微优和优雅的&#34;按顺序插入&#34; IEnumerable <t>的算法?

时间:2018-01-13 19:52:00

标签: c# .net algorithm time-complexity

我正在尝试按顺序创建一个通用&#34;插入&#34; C#中的算法,可以实现以下

/// <summary>
/// Inserts a new element in its proper order into an ordered enumeration
/// </summary>
/// <typeparam name="T">The comparable type</typeparam>
/// <param name="source">The sorted enumeration</param>
/// <param name="insertion">The element to insert</param>
/// <returns>A new enumeration with <paramref name="insertion"/> in its proper order</returns>
public static IEnumerable<T> InsertInOrder<T>(this IEnumerable<T> source, T insertion) where T : IComparable<T>
{
      throw new NotImplementedException();
}

并在操作次数和内存方面尽可能优化。我遇到的主要问题是,在使用MoveNext()时,在我的算法中始终存在这些情况,其中我偶然发现了#34;可能已经移动太远,并希望恢复前一个元素。这让我很难写出最优雅的东西。

为了在O(n)时间内看起来优雅,而不是直接使用enumereator我想出了

IEnumerable<T> smallers = source.TakeWhile(x => x.CompareTo(insertion) < 0);
IEnumerable<T> greaters = source.SkipWhile(x => x.CompareTo(insertion) >= 0);

IEnumerable<T> reordered = smallers.Concat(new[] { insertion }).Concat(greaters);

foreach (T element in reordered)
{
    yield return element;
}

但由于source上有多次枚举,因此效率不高。

所以我尝试转换为单枚举,最终(虽然它让我感到尴尬多久)想出了

bool inserted = false;
IEnumerator<T> mover = source.GetEnumerator();
while (mover.MoveNext())
{
    if (mover.Current.CompareTo(insertion) < 0)
    {
        yield return mover.Current;
    }
    else
    {
        if (!inserted)
        {
            yield return insertion;
            inserted = true;
        }
        yield return mover.Current;
    }
}

但问题是,一旦我们到达列表中insertion小于或等于mover.Current的点,if (mover.Current.CompareTo(insertion) < 0)就变成无用的检查。

所以,最终我想出了

bool movedNext;
IEnumerator<T> mover = source.GetEnumerator();
while (movedNext = mover.MoveNext() && mover.Current.CompareTo(insertion) < 0)
{
    yield return mover.Current;
}
yield return insertion;
if (movedNext)
{
    do
    {
        yield return mover.Current;
    } while (mover.MoveNext());
}

但是仍然存在额外变量moveNext并且不得不一遍又一遍地重新分配它movedNext = mover.MoveNext()的效率低下。

是否有可能在没有额外变量的情况下编写此插入算法来处理&#34;可能的额外移动&#34;? :)

1 个答案:

答案 0 :(得分:1)

最后一点代码就像它能得到的一样好。不要再担心额外的#include <memory> struct Obj { }; void f(const Obj &o) { std::shared_ptr<const Obj> optr(&o, [](const Obj*){}); } 变量和movedNext循环内的赋值等无效率低下的问题。

现在,如果你的问题是简单的专业好奇,是否可以避免?然后答案是:是的,当然!但是(总有一个但是)付出代价: goto 进入,可读性退出:

while
我会付出千倍的低效率......