【问题标题】:In c#, how can i rotate a list a specific amount of places?在 c# 中,如何将列表旋转特定数量的位置?
【发布时间】:2016-07-19 23:52:12
【问题描述】:

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

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

        }
   } 

【问题讨论】:

  • 听起来像是我的家庭作业
  • @HairOfTheDog:只要 OP 表现出努力,就欢迎家庭作业(这里似乎就是这种情况)。
  • 为了获得额外的功劳,请在后备列表上实现可配置的视图,这样您就不必轮换任何东西...

标签: c# sorting for-loop


【解决方案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) 时间,与移位大小无关。

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        检查并确保旋转不是无意义的也很好,即通过删除和添加项目 5K 次将长度为 3 的列表旋转 5K 是没有意义的,您可以通过执行类似 places%=items.Count; 的操作来做到这一点在开始旋转之前。

        【讨论】:

          【解决方案5】:

          这是一个类似的问题: 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));
          }
          

          【讨论】:

            【解决方案6】:

            您可以编写List&lt;int&gt; 的用户定义扩展,使用List&lt;T&gt;.Reverse() 进行旋转。 我从 C++ 标准模板库中获取了基本思想,它基本上分三个步骤使用 Reverse: 反向(第一,中间) 反向(中间,最后) 反向(第一个,最后一个)

            据我所知,这是最有效和最快的方式。我测试了 10 亿个元素,旋转 Rotate(0, 50000, 800000) 需要 0.00097 秒。 (顺便说一句:向 List 添加 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();
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-11-21
              • 1970-01-01
              • 2011-11-02
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-01-29
              • 1970-01-01
              相关资源
              最近更新 更多