我有一个IEnumerable<Point>
集合。可以说它包含5个点(实际上它更像2000)
我想订购这个集合,以便集合中的特定点成为第一个元素,所以它基本上是在一个特定的点上切割一个集合并将它们重新连接在一起。
所以我的5点清单:
{0,0}, {10,0}, {10,10}, {5,5}, {0,10}
对于索引3处的元素重新排序将变为:
{5,5}, {0,10}, {0,0}, {10,0}, {10,10}
解决此问题的计算效率最高的方法是什么,或者是否存在已经存在的内置方法...如果是这样我似乎无法找到它!
答案 0 :(得分:13)
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}
答案 1 :(得分:2)
在这种情况下,一个简单的数组副本就是O(n),它应该足以满足几乎所有的实际目的。但是,我会授予您在某些情况下 - 如果这是多级算法的深层内容 - 这可能是相关的。此外,您是否只需要以有序的方式遍历此集合或创建副本?
链接列表很容易像这样重新组织,尽管访问随机元素会更加昂贵。总的来说,计算效率还将取决于如何准确地访问这些项目集合(以及它们是什么类型的项目 - 值类型或引用类型?)。
标准的.NET链接列表似乎不支持这样的手动操作,但一般来说,如果你有一个链表,你可以按照你描述的方式轻松移动列表的各个部分,只需指定新的“下一个”和“以前”指向端点的指针。
此处提供的集合库支持此功能:http://www.itu.dk/research/c5/。
具体来说,您正在寻找LinkedList<T>.Slide()
可以在LinkedList<T>.View()
返回的对象上使用的方法。
答案 2 :(得分:1)
版本没有枚举list
两次,但因T[]
而导致内存消耗增加:
public static IEnumerable<T> Rotate<T>(IEnumerable<T> 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 }));
}
注意:添加参数检查。
答案 3 :(得分:0)
ulrichb显示的Linq方法的另一种替代方法是使用队列类(一个fifo集合)出列到你的索引,然后将你已经取出的那些排队。
答案 4 :(得分:0)
使用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
这里发生的是您执行两次比较所需的比较以获得组合序列中的第一个元素。 该解决方案可读且紧凑,但效率不高。 其他贡献者描述的解决方案可能更有效,因为它们使用特殊的数据结构作为数组或列表。
答案 5 :(得分:0)
您可以编写一个用户定义的List扩展名,以使用List.Reverse()进行旋转。我从C ++标准模板库中获取了基本思想,该库主要通过三个步骤使用反向:反向(第一,中间)反向(中间,最后)反向(第一,最后)
据我所知,这是最有效,最快的方法。我测试了10亿个元素,旋转Rotate(0,50000,800000)花费了0.00097秒。 (顺便说一句:向列表中添加10亿个整数已经需要7.3秒)
这是您可以使用的扩展名:
public static class Extensions
{
public static void Rotate(this List<int> me, int first, int mid, int last)
{
//indexes are zero based!
if (first >= mid || mid >= lastIndex)
return;
me.Reverse(first, mid - first + 1);
me.Reverse(mid + 1, last - mid);
me.Reverse(first, last - first + 1);
}
}
用法类似于:
static void Main(string[] args)
{
List<int> iList = new List<int>{0,1,2,3,4,5};
Console.WriteLine("Before rotate:");
foreach (var item in iList)
{
Console.Write(item + " ");
}
Console.WriteLine();
int firstIndex = 0, midIndex = 2, lastIndex = 4;
iList.Rotate(firstIndex, midIndex, lastIndex);
Console.WriteLine($"After rotate {firstIndex}, {midIndex}, {lastIndex}:");
foreach (var item in iList)
{
Console.Write(item + " ");
}
Console.ReadKey();
}