列表说我有一个列表 List<int> {1,2,3,4,5}
旋转意味着:
=> {2,3,4,5,1} => {3,4,5,1,2} => {4,5,1,2,3}
也许轮换不是最好的选择,但希望您理解我的意思
我的问题是,最简单的方法是什么(简而言之,C#4 Linq已准备就绪),并且不会受到性能(合理的性能)的影响
谢谢。
列表说我有一个列表 List<int> {1,2,3,4,5}
旋转意味着:
=> {2,3,4,5,1} => {3,4,5,1,2} => {4,5,1,2,3}
也许轮换不是最好的选择,但希望您理解我的意思
我的问题是,最简单的方法是什么(简而言之,C#4 Linq已准备就绪),并且不会受到性能(合理的性能)的影响
谢谢。
Answers:
您可以将其实现为队列。出队和入队相同的值。
**我不确定将列表转换为队列的性能,但是人们反对我的评论,因此我将其发布为答案。
在最简单的方法(一List<T>
)是使用:
int first = list[0];
list.RemoveAt(0);
list.Add(first);
但是,性能令人讨厌-O(n)。
数组
这基本上等同于List<T>
版本,但是更多的是手动的:
int first = array[0];
Array.Copy(array, 1, array, 0, array.Length - 1);
array[array.Length - 1] = first;
如果可以使用aLinkedList<T>
代替,那会简单得多:
int first = linkedList.First;
linkedList.RemoveFirst();
linkedList.AddLast(first);
这是O(1),因为每个操作都是恒定时间。
cadrell0使用队列的解决方案是一条语句,因为它Dequeue
删除了元素并返回了它:
queue.Enqueue(queue.Dequeue());
虽然我不能找到的这个性能特性的任何文档,我期望 Queue<T>
使用阵列和索引为“虚拟起点”来实现-在这种情况下,这是另一种O(1)的解决方案。
请注意,在所有这些情况下,您都需要先检查列表是否为空。(您可以认为这是一个错误或无操作。)
我用这个:
public static List<T> Rotate<T>(this List<T> list, int offset)
{
return list.Skip(offset).Concat(list.Take(offset)).ToList();
}
似乎有些回答者将此视为探索数据结构的机会。尽管这些答案是有益的和有用的,但它们并不是很明显。
Linq'ish的方法是:您获得一个扩展方法,该方法返回一个懒惰的IEnumerable,该IEnumerable知道如何构建所需的内容。此方法不会修改源,应该仅在必要时分配源的副本。
public static IEnumerable<IEnumerable<T>> Rotate<T>(this List<T> source)
{
for(int i = 0; i < source.Count; i++)
{
yield return source.TakeFrom(i).Concat(source.TakeUntil(i));
}
}
//similar to list.Skip(i-1), but using list's indexer access to reduce iterations
public static IEnumerable<T> TakeFrom<T>(this List<T> source, int index)
{
for(int i = index; i < source.Count; i++)
{
yield return source[i];
}
}
//similar to list.Take(i), but using list's indexer access to reduce iterations
public static IEnumerable<T> TakeUntil<T>(this List<T> source, int index)
{
for(int i = 0; i < index; i++)
{
yield return source[i];
}
}
用作:
List<int> myList = new List<int>(){1, 2, 3, 4, 5};
foreach(IEnumerable<int> rotation in myList.Rotate())
{
//do something with that rotation
}
我的解决方案可能太基本了(我不想说这很la脚...),而不是LINQ'ish。
但是,它具有相当好的性能。
int max = 5; //the fixed size of your array.
int[] inArray = new int[5] {0,0,0,0,0}; //initial values only.
void putValueToArray(int thisData)
{
//let's do the magic here...
Array.Copy(inArray, 1, inArray, 0, max-1);
inArray[max-1] = thisData;
}
尝试
List<int> nums = new List<int> {1,2,3,4,5};
var newNums = nums.Skip(1).Take(nums.Count() - 1).ToList();
newNums.Add(nums[0]);
虽然,我更喜欢乔恩·斯基特的回答。
list.Add(list.RemoveAt(0));
我对阵列的解决方案:
public static void ArrayRotate(Array data, int index)
{
if (index > data.Length)
throw new ArgumentException("Invalid index");
else if (index == data.Length || index == 0)
return;
var copy = (Array)data.Clone();
int part1Length = data.Length - index;
//Part1
Array.Copy(copy, 0, data, index, part1Length);
//Part2
Array.Copy(copy, part1Length, data, 0, index);
}
我为此使用了以下扩展名:
static class Extensions
{
public static IEnumerable<T> RotateLeft<T>(this IEnumerable<T> e, int n) =>
n >= 0 ? e.Skip(n).Concat(e.Take(n)) : e.RotateRight(-n);
public static IEnumerable<T> RotateRight<T>(this IEnumerable<T> e, int n) =>
e.Reverse().RotateLeft(n).Reverse();
}
它们当然很容易(OP标题请求),并且它们具有合理的性能(OP书面请求)。这是我在LINQPad 5中使用高于平均水平的笔记本电脑运行的一个小演示:
void Main()
{
const int n = 1000000;
const int r = n / 10;
var a = Enumerable.Range(0, n);
var t = Stopwatch.StartNew();
Console.WriteLine(a.RotateLeft(r).ToArray().First());
Console.WriteLine(a.RotateLeft(-r).ToArray().First());
Console.WriteLine(a.RotateRight(r).ToArray().First());
Console.WriteLine(a.RotateRight(-r).ToArray().First());
Console.WriteLine(t.ElapsedMilliseconds); // e.g. 236
}
您可以在.net框架中发挥出色。
我了解到,您要做的不只是一种迭代行为,还包括一种新的集合类型。因此,我建议您尝试基于IEnumerable的扩展方法,该方法可与Collections,Lists等一起使用...
class Program
{
static void Main(string[] args)
{
int[] numbers = { 1, 2, 3, 4, 5, 6, 7 };
IEnumerable<int> circularNumbers = numbers.AsCircular();
IEnumerable<int> firstFourNumbers = circularNumbers
.Take(4); // 1 2 3 4
IEnumerable<int> nextSevenNumbersfromfourth = circularNumbers
.Skip(4).Take(7); // 4 5 6 7 1 2 3
}
}
public static class CircularEnumerable
{
public static IEnumerable<T> AsCircular<T>(this IEnumerable<T> source)
{
if (source == null)
yield break; // be a gentleman
IEnumerator<T> enumerator = source.GetEnumerator();
iterateAllAndBackToStart:
while (enumerator.MoveNext())
yield return enumerator.Current;
enumerator.Reset();
if(!enumerator.MoveNext())
yield break;
else
yield return enumerator.Current;
goto iterateAllAndBackToStart;
}
}
如果您想走得更远,请像旋转样品一样旋转CircularList
并按住相同的枚举器以跳过Skip()
。
下面是我的方法。谢谢
public static int[] RotationOfArray(int[] A, int k)
{
if (A == null || A.Length==0)
return null;
int[] result =new int[A.Length];
int arrayLength=A.Length;
int moveBy = k % arrayLength;
for (int i = 0; i < arrayLength; i++)
{
int tmp = i + moveBy;
if (tmp > arrayLength-1)
{
tmp = + (tmp - arrayLength);
}
result[tmp] = A[i];
}
return result;
}
public static int[] RightShiftRotation(int[] a, int times) {
int[] demo = new int[a.Length];
int d = times,i=0;
while(d>0) {
demo[d-1] = a[a.Length - 1 - i]; d = d - 1; i = i + 1;
}
for(int j=a.Length-1-times;j>=0;j--) { demo[j + times] = a[j]; }
return demo;
}
使用Linq,
List<int> temp = new List<int>();
public int[] solution(int[] array, int range)
{
int tempLength = array.Length - range;
temp = array.Skip(tempLength).ToList();
temp.AddRange(array.Take(array.Length - range).ToList());
return temp.ToArray();
}
如何使用模块化算法:
public void UsingModularArithmetic()
{
string[] tokens_n = Console.ReadLine().Split(' ');
int n = Convert.ToInt32(tokens_n[0]);
int k = Convert.ToInt32(tokens_n[1]);
int[] a = new int[n];
for(int i = 0; i < n; i++)
{
int newLocation = (i + (n - k)) % n;
a[newLocation] = Convert.ToInt32(Console.ReadLine());
}
foreach (int i in a)
Console.Write("{0} ", i);
}
因此,当我从控制台读取内容时,基本上将值添加到数组中。
List<int> {1,2,3,4,5}
并说明代码如何进行轮换。