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

时间:2016-07-19 23:52:12

标签: c# sorting for-loop

我编写了一个代码来将列表旋转到特定数量的位置,我在下面的代码可以工作,但我想知道是否有更有效的方法来执行此操作。

{{1}}

6 个答案:

答案 0 :(得分:1)

这是一个典型的计算机科学问题。一种稍微快一点的技术是反转整个阵列,然后反转阵列的两个块:

// 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]
       ^^^^^^^^^^^^^^^^

或者,代码:

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

这是O(n)时间,无论班次大小如何。

答案 1 :(得分:1)

如果使用循环数组QUEUE(理论上它具有比列表更好的内存管理)来实现它会更快。这不需要物理旋转现有数据,因此它应该比原始代码更快。

顺便说一下,您可以阅读StackOverflow中的其他参考资料,以丰富您的知识,例如:

答案 2 :(得分:0)

您正在插入和删除列表元素。有一些与此相关的开销。可以通过索引访问列表。因此,您可以遍历列表,将元素移动到它们应该处于的位置。您将需要使用临时整数变量来避免覆盖任何列表数据。

答案 3 :(得分:0)

也很好检查并确保旋转不是荒谬的,即通过移除和添加5K次项目来旋转长度为3的列表5K没有意义,你可以通过做{{1在开始旋转之前。

答案 4 :(得分:0)

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

另外,试试这个:

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

答案 5 :(得分:0)

您可以编写用户定义的扩展名List<int>,该扩展名可以使用List<T>.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,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();
}