web-dev-qa-db-fra.com

Le moyen le plus simple de faire pivoter une liste en c #

Les listes indiquent que j'ai une liste List<int> {1,2,3,4,5}

Rotation signifie:

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

Peut-être que tourner n'est pas le meilleur mot pour cela, mais j'espère que vous comprenez ce que je veux dire

Ma question, quelle est la manière la plus simple (en short code, c # 4 Linq ready), et ne sera pas touchée par les performances (performances raisonnables)

Merci.

61
Eric Yin

Vous pouvez l'implémenter en tant que file d'attente. Retirer et mettre en file d'attente la même valeur.

** Je n'étais pas sûr des performances de conversion d'une liste en file d'attente, mais les gens ont voté pour mon commentaire, donc je poste ceci comme réponse.

46
cadrell0

List<T>

La manière la plus simple (pour une List<T>) consiste à utiliser:

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

Cependant, la performance est mauvaise - O (n).

Tableau

Ceci est fondamentalement équivalent au List<T> version, mais plus manuelle:

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

LinkedList<T>

Si vous pouviez utiliser un LinkedList<T> à la place, ce serait beaucoup plus simple:

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

C'est O(1) car chaque opération est à temps constant.

Queue<T>

la solution de cadrell0 d'utiliser une file d'attente est une seule instruction, car Dequeue supprime l'élément et le renvoie:

queue.Enqueue(queue.Dequeue());

Bien que je ne trouve aucune documentation sur les caractéristiques de performance de cela, je voudrais attendreQueue<T> à implémenter en utilisant un tableau et un index comme "point de départ virtuel" - auquel cas c'est une autre solution O(1)).

Notez que dans tous ces cas, vous voudrez d'abord vérifier que la liste est vide. (Vous pouvez considérer cela comme une erreur ou un non-fonctionnement.)

65
Jon Skeet

J'utilise celui-ci:

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

Il semble que certains répondeurs aient traité cela comme une chance d'explorer les structures de données. Bien que ces réponses soient informatives et utiles, elles ne sont pas très linq'ish.

L'approche Linq'ish est la suivante: vous obtenez une méthode d'extension qui retourne un IEnumerable paresseux qui sait comment construire ce que vous voulez. Cette méthode ne modifie pas la source et ne doit allouer une copie de la source que si nécessaire.

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

Utilisé comme:

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

Que dis-tu de ça:

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

rot est le nombre de points à faire pivoter - qui doit être inférieur au nombre d'éléments dans la liste input.

Comme la réponse @ cadrell0 montre si c'est tout ce que vous faites avec votre liste, vous devez utiliser une file d'attente au lieu d'une liste.

3
BrokenGlass

Ma solution est peut-être trop basique (je ne voudrais pas dire que c'est boiteux ...) et pas LINQ'ish.
Cependant, ses performances sont plutôt bonnes.

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

Vous pouvez jouer à Nice dans le cadre .net.

Je comprends que ce que vous voulez faire est plus un comportement d'itération qu'un nouveau type de collection; Je vous suggère donc d'essayer cette méthode d'extension basée sur IEnumerable, qui fonctionnera avec les collections, les listes, etc.

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;
    }
}
  • Performances raisonnables
  • Souple

Si vous voulez aller plus loin, faites un CircularList et maintenez le même énumérateur pour ignorer la Skip() lors de la rotation comme dans votre exemple.

1
Davi Fiamenghi

Essayer

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

Cependant, j'aime mieux la réponse de Jon Skeet.

1
Andy Evans

Vous pouvez utiliser le code ci-dessous pour la rotation à gauche.

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

Ma solution pour les tableaux:

    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

ci-dessous est mon approche. Je vous remercie

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

J'ai utilisé les extensions suivantes pour cela:

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

Ils sont certainement faciles (demande de titre OP) et leurs performances sont raisonnables (demande de rédaction OP). Voici une petite démo que j'ai exécutée dans LINQPad 5 sur un ordinateur portable de puissance supérieure à la moyenne:

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

À l'aide de 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();
    }
0