简体   繁体   English

在 c# 中,如何将列表旋转特定数量的位置?

[英]In c#, how can i rotate a list a specific amount of places?

I wrote a code to rotate a list a specific amount of places, the code I have below works but i would like to know if there is a more efficient way to do this.我编写了一个代码来将列表旋转特定数量的位置,下面的代码可以工作,但我想知道是否有更有效的方法来做到这一点。

  public void Test8(List<int> items, int places)
   {
      int nums;
        for (int i = 0; i < places; i++)
        {
            nums = items[items.Count() - 1];
            items.RemoveAt(items.Count - 1);
            items.Insert(0, nums);

        }
   } 

This is a classic computer science problem.这是一个经典的计算机科学问题。 One technique that's slightly faster is to reverse the entire array, then reverse the two chunks of the array:一种稍微快一点的技术是反转整个数组,然后反转数组的两个块:

// If we want to shift two places, start with an array
[1, 2, 3, 4, 5, 6, 7, 8]
// Then reverse the entire array
[8, 7, 6, 5, 4, 3, 2, 1]
// Then reverse the first n elements, two in our case
[7, 8, 6, 5, 4, 3, 2, 1]
 ^^^^
// Then reverse the remaining items
[7, 8, 1, 2, 3, 4, 5, 6]
       ^^^^^^^^^^^^^^^^

Or, as code:或者,作为代码:

static void Reverse(List<int> items, int posFrom, int posTo)
{
    // Helper to reverse a sub portion of an array in place
    while (posFrom < posTo)
    {
        // Swap the first and last items
        int temp = items[posFrom];
        items[posFrom] = items[posTo];
        items[posTo] = temp;
        // Shrink down to the next pair of items
        --posTo;
        ++posFrom;
    }
}

static void Test8(List<int> items, int places)
{
    // Sanity, if we try to rotate more than there are
    // items in the array, it just loops around
    places %= items.Count;
    // Reverse the entire array
    Reverse(items, 0, items.Count - 1);
    // Reverse the first group of items
    Reverse(items, 0, places - 1);
    // Reverse the second group of items
    Reverse(items, places, items.Count - 1);
}

This is O(n) time, irregardless of the shift size.这是 O(n) 时间,与移位大小无关。

It can be faster if you implement it using Circular Array QUEUE (which theoritically have better memory management than the list).如果您使用 Circular Array QUEUE(理论上它比列表具有更好的内存管理)来实现它,它可能会更快。 This does not need physically rotating the existing data, so it should be faster than your original code.这不需要物理旋转现有数据,因此它应该比您的原始代码更快。

BTW, you can read other references in StackOverflow to enrich your knowledge, for ex:顺便说一句,您可以阅读 StackOverflow 中的其他参考资料来丰富您的知识,例如:

You are inserting and removing list elements.您正在插入和删除列表元素。 There is some overhead associated with that.有一些与此相关的开销。 A list can be accessed by index.可以通过索引访问列表。 You can therefore loop through the list, moving elements to the position they should be in. You will need to use a temporary integer variable to avoid overwriting any list data.因此,您可以遍历列表,将元素移动到它们应该在的位置。您需要使用一个临时整数变量来避免覆盖任何列表数据。

Also good to check and make sure the rotation isn't nonsensical, ie rotating a list of length 3 right 5K by removing and adding items 5K times doesn't make sense, you can do that by doing something like places%=items.Count;检查并确保旋转不是无意义的也很好,即通过删除和添加项目 5K 次将长度为 3 的列表旋转 5K 是没有意义的,您可以通过执行类似places%=items.Count;的操作来做到这一点places%=items.Count; before you start rotating.在开始旋转之前。

Here is a similar question: C# Collection - Order by an element (Rotate)这是一个类似的问题: C# Collection - Order by an element (Rotate)

Also, try this:另外,试试这个:

static void Main(string[] args)
{
    var items = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    var rotatedItems = Rotate(items, 4);
    // rotated is now {5, 6, 7, 8, 9, 1, 2, 3, 4}            
    Console.WriteLine(string.Join(", ", rotatedItems));
    Console.Read();
}

public static IEnumerable<int> Rotate(IEnumerable<int> items, int places)
{
    return items.Skip(places).Concat(items.Take(places));
}

You can write a user defined extension of List<int> that does the rotation by using List<T>.Reverse() .您可以编写用户定义的List<int>扩展,使用List<T>.Reverse()进行旋转。 I took the basic idea from the C++ Standard Template Library which basically uses Reverse in three steps: Reverse(first, mid) Reverse(mid, last) Reverse(first, last)我从 C++ 标准模板库中获取了基本思想,它基本上分三个步骤使用 Reverse: Reverse(first, mid) Reverse(mid, last) Reverse(first, last)

As far as I know, this is the most efficient and fastest way.据我所知,这是最有效和最快的方式。 I tested with 1 billion elements and the rotation Rotate(0, 50000, 800000) takes 0.00097 seconds.我测试了 10 亿个元素,旋转Rotate(0, 50000, 800000)需要 0.00097 秒。 (By the way: adding 1 billion ints to the List already takes 7.3 seconds) (顺便说一句:向 List 添加 10 亿个整数已经需要 7.3 秒)

Here's the extension you can use:这是您可以使用的扩展程序:

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

The usage is like:用法是这样的:

static void Main(string[] args)
{
    List<int> iList = new List<int>{0,1,2,3,4,5,6,7,8,9};    
    Console.WriteLine("Before rotate:");
    foreach (var item in iList)
    {
      Console.Write(item + " ");
    }
    Console.WriteLine();
    
    int firstIndex = 0, midIndex = 3, lastIndex = 5;
    iList.Rotate(firstIndex, midIndex, lastIndex);
    
    Console.WriteLine($"After rotate {firstIndex}, {midIndex}, {lastIndex}:");
    foreach (var item in iList)
    {
      Console.Write(item + " ");
    }
    Console.ReadKey();
}

Circular Right-Shift Array with specified number of times in ArrayList. ArrayList 中指定次数的循环右移数组。 Specified Range of array elements and circular shift.数组元素的指定范围和循环移位。 become faster code as compare with array implementation与数组实现相比,代码变得更快

Code代码

using System;
using System.Collections;
public class Program
{
    // Circular Array repeting logic in ArrayList

    public static ArrayList CircularshiftArry(ArrayList a, int circularrep)
    {
        int I = 1;

        while (I <= circularrep)
        {
            int n = a.Count;
            a.Insert(0, a[n - I]);
            I++;
        }
        return a;
    }

    public static void Main()
    {
        
        Console.WriteLine("ENTER HOW MANY CIRCULAR REPETATION YOU WANT");
        int circularrep = int.Parse(Console.ReadLine()); 
        ArrayList a = new ArrayList();
        Console.WriteLine("HOW MANY ARRAY ELEMENTS YOU WANT TO ENTER");
        int num = int.Parse(Console.ReadLine());
        for (int i = 0; i < num; i++)
        {
            Console.WriteLine("ENTER ARRAY ELEMENTS:{0}", i);
            int p = int.Parse(Console.ReadLine());
            a.Add(p);
        }

        Console.WriteLine("\n");

        Console.WriteLine("The enterd array is :");
        for (int i = 0; i < num; i++)
        {
            Console.Write("{0}\t", a[i]);

        }

        ArrayList b = CircularshiftArry(a, circularrep);

        Console.WriteLine("\n");

        int N = b.Count;
        Console.WriteLine("The {0}times circular shifted  array is :", circularrep);
        for (int i = 0; i < N - circularrep; i++)
        {
            Console.Write("{0}\t", b[i]);

        }

        Console.ReadLine();

    }

}

This is the output in console window这是控制台窗口中的输出

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM