75 votes

Moyen le plus simple de faire pivoter une liste en c#

Les listes disent que j'ai une liste List {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 "rotation" n'est pas le meilleur mot pour cela, mais j'espère que vous comprenez ce que je veux dire

Ma question, quel est le moyen le plus facile (en peu de code, prêt pour Linq en c# 4), et ne sera pas affecté par les performances (performances raisonnables)

Merci.

16 votes

Vous pourriez le mettre en œuvre sous forme de file. Défilez et Enfilez la même valeur.

1 votes

Est-ce qu'une solution en tableau est acceptable ?

0 votes

Je veux une liste, plus souple, Array sur since ToList est très pratique

77voto

Jon Skeet Points 692016

List

La façon la plus simple (pour un List) est d'utiliser :

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

Les performances sont mauvaises cependant - O(n).

Array

C'est essentiellement équivalent à la version List, mais plus manuel :

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

LinkedList

Si vous pouviez utiliser une LinkedList à la place, ce serait beaucoup plus simple :

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

Ceci est O(1) car chaque opération est en temps constant.

Queue

La solution de cadrell0 utilisant une file d'attente est une seule déclaration, car Dequeue supprime l'élément et le retourne :

queue.Enqueue(queue.Dequeue());

Je ne trouve aucune documentation sur les caractéristiques de performances de ceci, je m'attends à ce que Queue soit implémenté en utilisant un tableau et un index comme "point de départ virtuel" - dans ce cas, c'est une autre solution O(1).

Remarquez que dans tous ces cas, vous voudriez vérifier d'abord si la liste est vide. (Vous pourriez considérer cela comme une erreur, ou une opération nulle.)

1 votes

Je choisirais plutôt une file d'attente qu'une liste chaînée. Un tableau circulaire est généralement plus performant dans le cas moyen, et dans les deux cas, tous les détails sont abstraits par le langage de toute façon.

1 votes

@Servy : Oui, c'est un commentaire juste. Une liste chaînée permet une rotation inverse plus facilement, car .NET n'a pas de file d'attente :( Vous pourriez bien sûr en construire une facilement cependant...

1 votes

Est-ce que 'RemoveFirst()` est void? Les docs montrent en fait un exemple exactement de ce que l'OP a demandé: msdn.microsoft.com/en-us/library/ms132181.aspx. Vous devez d'abord récupérer le premier noeud en utilisant linkedList.First?

49voto

cadrell0 Points 9152

Vous pourriez l'implémenter en tant que file d'attente. Défilez et mettez en file d'attente la même valeur.

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

8 votes

Les seuls problèmes de performances surviendraient si l'OP fait autre chose, car passer à une file d'attente signifie que l'accès à autre chose que les premiers/derniers éléments est inefficace.

1 votes

J'ai ajouté les appels réels à ma réponse, car vous ne les aviez pas inclus et la mienne consiste principalement à collecter des implémentations :) J'espère que cela ne vous dérange pas.

0 votes

@JonSkeet Pas du tout. La pure paresse est la raison pour laquelle je l'ai laissé de côté dans le mien.

28voto

mrzli Points 1986

J'utilise celui-ci :

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

0 votes

Astucieux, élégant et facile... la meilleure solution jusqu'à présent... gardez simplement à l'esprit que vous devez stocker la valeur de décalage pour transmettre la bonne valeur dans chaque appel.

0 votes

Vraiment une solution très agréable et élégante, merci beaucoup.

9voto

David B Points 53123

Il semble que certains répondants ont traité cela comme une opportunité 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> Rotate(this List source)
{
  for(int i = 0; i < source.Count; i++)
  {
    yield return source.TakeFrom(i).Concat(source.TakeUntil(i));
  }
}

  //similaire à list.Skip(i-1), mais en utilisant l'accès par indexeur de la liste pour réduire les itérations
public static IEnumerable TakeFrom(this List source, int index)
{
  for(int i = index; i < source.Count; i++)
  {
    yield return source[i];
  }
}

  //similaire à list.Take(i), mais en utilisant l'accès par indexeur de la liste pour réduire les itérations    
public static IEnumerable TakeUntil(this List source, int index)
{
  for(int i = 0; i < index; i++)
  {
    yield return source[i];
  }
}

Utilisé comme :

List myList = new List(){1, 2, 3, 4, 5};
foreach(IEnumerable rotation in myList.Rotate())
{
  //faire quelque chose avec cette rotation
}

3voto

BrokenGlass Points 91618

Que diriez-vous de ceci :

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

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

Comme le montre la réponse de @cadrell0, si c'est tout ce que vous faites avec votre liste, vous devriez utiliser une file d'attente à la place d'une liste.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X