web-dev-qa-db-ja.com

C#でリストを回転させる最も簡単な方法

リストにリストがあると言うList<int> {1,2,3,4,5}

回転とは:

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

回転はこれに最適なWordではないかもしれませんが、私の意味を理解してください

私の質問、最も簡単な方法は何ですか(短いコードでは、c#4 Linq対応)、パフォーマンスには影響されません(合理的なパフォーマンス)

ありがとう。

61
Eric Yin

キューとして実装できます。同じ値をデキューおよびエンキューします。

**リストをキューに変換する際のパフォーマンスについては確信がありませんでしたが、人々は私のコメントを支持していたので、これを回答として投稿しています。

46
cadrell0

List<T>

simplest方法(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;

LinkedList<T>

代わりにLinkedList<T>を使用できる場合、それははるかに簡単です。

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

これはO(1)です。各操作は一定時間です。

Queue<T>

Dequeueが要素を削除し、が返すので、cadrell0のキュー使用のソリューションは単一のステートメントです。

queue.Enqueue(queue.Dequeue());

このパフォーマンス特性のドキュメントは見つかりませんが、expectQueue<T>を「仮想開始として配列とインデックスを使用して実装しますポイント」-この場合、これは別のO(1)ソリューションです。

これらのすべてのケースで、最初にリストが空であることを確認することに注意してください。 (それはエラー、またはノーオペレーションとみなすことができます。)

65
Jon Skeet

私はこれを使用します:

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

一部の回答者は、これをデータ構造を探索する機会として扱っているようです。これらの回答は有益で有用ですが、あまりLinq'ishではありません。

Linq'ishのアプローチは次のとおりです。必要なものを構築する方法を知っている遅延IEnumerableを返す拡張メソッドを取得します。このメソッドはソースを変更せず、必要な場合にのみソースのコピーを割り当てる必要があります。

public static IEnumerable<IEnumerable<T>> Rotate<T>(this List<T> source)
{
  for(int i = 0; i < source.Length; 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.Length; 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
}
8
Amy B

これはどう:

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

ここで、rotは回転するスポットの数です。これは、inputリスト内の要素の数よりも少なくなければなりません。

@ cadrell0の答えはリストでこれがすべてかどうかを示しているので、リストの代わりにキューを使用する必要があります。

3
BrokenGlass

私の解決策は基本的すぎるかもしれません(私はそれが下手だと言いたくありません...)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;
}
2
ThomAce

.netフレームワークでNiceをプレイできます。

あなたがしたいことは、新しいコレクション型よりも反復動作に近いことを理解しています。 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;
    }
}
_
  • リーズナブルなパフォーマンス
  • フレキシブル

さらに進む場合は、CircularListを作成し、同じ列挙子を保持して、サンプルのように回転するときにSkip()をスキップします。

1
Davi Fiamenghi

試して

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

とはいえ、私はJon Skeetの回答の方が好きです。

1
Andy Evans

以下のコードを左回転に使用できます。

List<int> backUpArray = array.ToList();

for (int i = 0; i < array.Length; i++)
{
    int newLocation = (i + (array.Length - rotationNumber)) % n;
    array[newLocation] = backUpArray[i];
}
1
user3838082

配列の私のソリューション:

    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);
    }
1
Pedro77
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;
}
0
Arun

以下は私のアプローチです。ありがとうございました

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

これには次の拡張機能を使用しました。

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
}
0
William

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