C#Collection – 按元素排序(旋转)

我有一个IEnumerable集合。 可以说它包含5个点(实际上它更像2000)

我想订购这个集合,以便集合中的特定点成为第一个元素,因此它基本上是在特定点切割集合并将它们重新连接在一起。

所以我的5点清单:

{0,0}, {10,0}, {10,10}, {5,5}, {0,10}

关于索引3处的元素重新排序将变为:

{5,5}, {0,10}, {0,0}, {10,0}, {10,10}

解决这个问题的计算效率最高的方法是什么,或者是否存在已经存在的内置方法……如果是这样,我似乎找不到一个!

 var list = new[] { 1, 2, 3, 4, 5 }; var rotated = list.Skip(3).Concat(list.Take(3)); // rotated is now {4, 5, 1, 2, 3} 

在这种情况下,一个简单的数组副本就是O(n),它应该足以满足几乎所有的实际用途。 但是,我会授予您在某些情况下 – 如果这是多级算法的深层内容 – 这可能是相关的。 此外,您是否只需要以有序的方式遍历此集合或创建副本?

链接列表很容易像这样重新组织,尽管访问随机元素会更加昂贵。 总体而言,计算效率还取决于您访问此项集合的准确程度(以及它们是什么类型的项目 – 值类型或引用类型?)。

标准的.NET链表似乎不支持这样的手动操作,但一般来说,如果你有一个链表,你可以按照你描述的方式轻松移动列表的各个部分,只需指定新的“next”和“previous” “指向端点的指针。

此处提供的集合库支持此function: http : //www.itu.dk/research/c5/ 。 具体来说,您正在寻找LinkedList.Slide()方法,您可以对LinkedList.View()返回的对象使用该方法。

版本没有枚举list两次,但由于T[]而导致内存消耗较高:

 public static IEnumerable Rotate(IEnumerable source, int count) { int i = 0; T[] temp = new T[count]; foreach (var item in source) { if (i < count) { temp[i] = item; } else { yield return item; } i++; } foreach (var item in temp) { yield return item; } } [Test] public void TestRotate() { var list = new[] { 1, 2, 3, 4, 5 }; var rotated = Rotate(list, 3); Assert.That(rotated, Is.EqualTo(new[] { 4, 5, 1, 2, 3 })); } 

注意:添加参数检查。

ulrichb显示的Linq方法的另一种替代方法是使用Queue Class(一个fifo集合)出列到你的索引,并将你已经取出的那些排队。

使用linq的天真实现将是:

 IEnumerable x = new[] { 1, 2, 3, 4 }; var tail = x.TakeWhile(i => i != 3); var head = x.SkipWhile(i => i != 3); var combined = head.Concat(tail); // is now 3, 4, 1, 2 

这里发生的是你执行两次比较所需的比较,以达到组合序列中的第一个元素。 该解决方案可读且紧凑,但效率不高。 其他贡献者描述的解决方案可能更有效,因为它们使用特殊数据结构作为数组或列表。