在 C# 中旋转列表的最简单方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9948202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 11:20:43  来源:igfitidea点击:

Easiest way to Rotate a List in c#

c#arrayslinqlist

提问by Eric Yin

Lists say I have a list List<int> {1,2,3,4,5}

列表说我有一个列表 List<int> {1,2,3,4,5}

Rotate means:

旋转的意思:

=> {2,3,4,5,1} => {3,4,5,1,2} => {4,5,1,2,3}

Maybe rotate is not the best word for this, but hope you understand what I means

也许旋转不是最好的词,但希望你明白我的意思

My question, whats the easiest way (in short code, c# 4 Linq ready), and will not be hit by performance (reasonable performance)

我的问题是,什么是最简单的方法(简而言之,c# 4 Linq ready),并且不会受到性能的影响(合理的性能)

Thanks.

谢谢。

采纳答案by cadrell0

You could implement it as a queue. Dequeue and Enqueue the same value.

您可以将其实现为队列。Dequeue 和 Enqueue 的值相同。

**I wasn't sure about performance in converting a List to a Queue, but people upvoted my comment, so I'm posting this as an answer.

**我不确定将列表转换为队列的性能,但人们赞成我的评论,所以我将其作为答案发布。

回答by Jon Skeet

List<T>

List<T>

The simplestway (for a List<T>) is to use:

最简单的方法(一List<T>)是使用:

int first = list[0];
list.RemoveAt(0);
list.Add(first);

Performance is nasty though - O(n).

虽然性能很差 - O(n)。

Array

大批

This is basically equivalent to the List<T>version, but more manual:

这基本上等同于List<T>版本,但更手动:

int first = array[0];
Array.Copy(array, 1, array, 0, array.Length - 1);
array[array.Length - 1] = first;

LinkedList<T>

LinkedList<T>

If you could use a LinkedList<T>instead, that would be much simpler:

如果您可以使用 aLinkedList<T>代替,那会简单得多:

int first = linkedList.First;
linkedList.RemoveFirst();
linkedList.AddLast(first);

This is O(1) as each operation is constant time.

这是 O(1) 因为每个操作都是常数时间。

Queue<T>

Queue<T>

cadrell0's solution of using a queue is a single statement, as Dequeueremoves the element andreturns it:

cadrell0 使用队列的解决方案是一个单一的语句,Dequeue删除元素返回它:

queue.Enqueue(queue.Dequeue());

While I can't find any documentation of the performance characteristic of this, I'd expectQueue<T>to be implemented using an array and an index as the "virtual starting point" - in which case this is another O(1) solution.

虽然我不能找到的这个性能特性的任何文档,我期望Queue<T>使用阵列和索引为“虚拟起点”来实现-在这种情况下,这是另一种O(1)的解决方案。

Note that in all of these cases you'd want to check for the list being empty first. (You could deem that to be an error, or a no-op.)

请注意,在所有这些情况下,您首先要检查列表是否为空。(你可以认为这是一个错误,或者一个空操作。)

回答by Andy Evans

Try

尝试

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]);

Although, I like Jon Skeet's answer better.

虽然,我更喜欢 Jon Skeet 的回答。

回答by BrokenGlass

How about this:

这个怎么样:

var output = input.Skip(rot)
                  .Take(input.Count - rot)
                  .Concat(input.Take(rot))
                  .ToList();

Where rotis the number of spots to rotate - which must be less than the number of elements in the inputlist.

哪里rot是要旋转的点数 - 它必须小于input列表中的元素数。

As @cadrell0 answer shows if this is all you do with your list, you should use a queue instead of a list.

正如@cadrell0 的回答所显示的,如果这就是你对列表所做的全部,你应该使用队列而不是列表。

回答by Amy B

It seems like some answerers have treated this as a chance to explore data structures. While those answers are informative and useful, they are not very Linq'ish.

似乎有些回答者将此视为探索数据结构的机会。虽然这些答案内容丰富且有用,但它们并不是非常 Linq'ish。

The Linq'ish approach is: You get an extension method which returns a lazy IEnumerable that knows how to build what you want. This method doesn't modify the source and should only allocate a copy of the source if necessary.

Linq'ish 方法是:你得到一个扩展方法,它返回一个惰性 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];
  }
}

Used as:

用作:

List<int> myList = new List<int>(){1, 2, 3, 4, 5};
foreach(IEnumerable<int> rotation in myList.Rotate())
{
  //do something with that rotation
}

回答by Davi Fiamenghi

You can play nice in .net framework.

你可以在 .net 框架中玩得很好。

I understand that what you want to do is more up to be an iteration behavior than a new collection type; so I would suggest you to try this extension method based on IEnumerable, which will work with Collections, Lists and so on...

我知道您想要做的更多是迭代行为而不是新的集合类型;所以我建议你试试这个基于 IEnumerable 的扩展方法,它适用于集合、列表等等......

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;
    }
}
  • Reasonable performance
  • Flexible
  • 合理的表现
  • 灵活的

If you want go further, make a CircularListand hold the same enumerator to skip the Skip()when rotating like in your sample.

如果你想更进一步,制作一个CircularList并保持相同的枚举器Skip()在旋转时跳过你的样本。

回答by mrzli

I use this one:

我用这个:

public static List<T> Rotate<T>(this List<T> list, int offset)
{
    return list.Skip(offset).Concat(list.Take(offset)).ToList();
}

回答by Pedro77

My solution for Arrays:

我的数组解决方案:

    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);
    }

回答by Dale Henning

I was asked to reverse a character array with minimal memory usage.

我被要求以最少的内存使用量反转字符数组。

char[] charArray = new char[]{'C','o','w','b','o','y'};

char[] charArray = new char[]{'C','o','w','b','o','y'};

Method:

方法:

static void Reverse(ref char[] s)
{
    for (int i=0; i < (s.Length-i); i++)
    {
        char leftMost = s[i];
        char rightMost = s[s.Length - i - 1];

        s[i] = rightMost;
        s[s.Length - i - 1] = leftMost;
    }
}

回答by William

I've used the following extensions for this:

我为此使用了以下扩展:

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();
}

They're certainly easy (OP title request), and they've got reasonable performance (OP write-up request). Here's a little demo I ran in LINQPad 5 on an above-average-powered laptop:

它们当然很容易(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
}