web-dev-qa-db-fra.com

Comment supprimer un élément de pile qui n'est pas sur le dessus de la pile en C #

Malheureusement, un élément ne peut être retiré de la pile que par "pop". La pile n'a pas de méthode "remove" ou quelque chose de similaire, mais j'ai une pile (oui, j'ai besoin d'une pile!) À partir de laquelle j'ai besoin de supprimer certains éléments entre.

Y a-t-il un truc pour faire ça?

29
Enyra

Si vous devez supprimer des éléments qui ne sont pas au sommet, vous avez besoin d'autre chose que d'une pile.

Essayez de créer votre propre implémentation d'une pile à partir d'une liste. Ensuite, vous devez implémenter vos propres fonctions Push et Pop (ajouter et supprimer de la liste) et votre propre fonction spéciale PopFromTheMiddle.

Par exemple

public class ItsAlmostAStack<T>
{
    private List<T> items = new List<T>();

    public void Push(T item)
    {
        items.Add(item);
    }
    public T Pop()
    {
        if (items.Count > 0)
        {
            T temp = items[items.Count - 1];
            items.RemoveAt(items.Count - 1);
            return temp;
        }
        else
            return default(T);
    }
    public void Remove(int itemAtPosition)
    {
        items.RemoveAt(itemAtPosition);
    }
}
44
Binary Worrier

Pensez à utiliser un conteneur différent. Peut-être une LinkedList ..__ Ensuite, vous pouvez utiliser

AddFirst 
 AddLast 
 RemoveLast 
 RemoveFirst

comme pop/push de la pile et vous pouvez utiliser

Retirer

supprimer n'importe quel nœud du milieu de la liste

11
Michał Piaskowski

Vous pouvez utiliser un LinkedList

La suppression basée sur les listes sera probablement moins efficace . Dans la suppression par référence, les piles basées sur la liste auront la recherche O(N) et le redimensionnement O(N). La recherche LinkedList est O(N) et la suppression est O (1). Pour la suppression par index, LinkedList doit avoir O(N) traversal et O(1) suppression, tandis que List aura O(1) traversal (car il s'agit d'une indexation) et suppression de O(N) en raison du redimensionnement.

Outre l'efficacité, une implémentation de LinkedList vous maintiendra dans la bibliothèque standard, ouvrant votre code à plus de flexibilité et vous permettant d'écrire moins.

Cela devrait être capable de gérer Pop, Push et Remove

    public class FIFOStack<T> : LinkedList<T>
    {
        public T Pop()
        {
            T first = First();
            RemoveFirst();
            return first;
        }

        public void Push(T object)
        {
            AddFirst(object);
        }

        //Remove(T object) implemented in LinkedList
   }
6
Benjamin Brown

Peut-être une méthode d'extension fonctionnerait-elle, bien que je soupçonne qu'une structure de données totalement différente est vraiment nécessaire.

public static T Remove<T>( this Stack<T> stack, T element )
{
     T obj = stack.Pop();
     if (obj.Equals(element))
     {
         return obj;
     }
     else
     {
        T toReturn = stack.Remove( element );
        stack.Push(obj);
        return toReturn;
     }
}
5
tvanfosson

Dans une vraie pile, cela ne peut être fait que dans un sens -

Pop tous les éléments jusqu'à ce que vous supprimiez celui que vous voulez, puis repoussez-les sur la pile dans l'ordre approprié.

Ce n'est pas très efficace, cependant.

Si vous souhaitez vraiment supprimer de n’importe quel emplacement, je vous recommande de créer une pseudo-pile à partir d’une liste, d’une liste liée ou d’une autre collection. Cela vous donnerait le contrôle pour le faire facilement.

4
Reed Copsey
   Stack temp = new Stack();
   object x, y;
   While ((x = myStack.Pop()) != ObjectImSearchingFor)
       temp.Push(x);
   object found = x;
   While ((y = temp.Pop()) != null)
      myStack.Push(y);
3
Charles Bretana

Une astuce que j’utilise dans les situations délicates consiste à ajouter un indicateur «obsolète» aux éléments de la pile . object) . Ensuite, lorsque Pop () ing éléments, je vérifie simplement si l'indicateur est levé, et saute à nouveau dans une boucle jusqu'à ce qu'un élément non obsolète soit trouvé.

do 
{  
   obj = mQueue.Pop();  
} while (obj.deprecated);  

Vous pouvez gérer votre propre nombre d'éléments pour savoir combien d'éléments "réels" sont encore dans la file d'attente. De toute évidence, le verrouillage doit être utilisé si cela est nécessaire pour une solution multithread.

J'ai trouvé que, pour les files d'attente traversées par un flux constant (éléments poussés et sautés), il est bien plus efficace de le gérer de cette façon, c'est le plus rapide possible (paiement O(1) pour le retrait d'un élément du milieu. ) et au niveau mémoire, si l’objet retenu est petit, il n’est généralement pas pertinent de le faire si les objets suivent un rythme raisonnable.

3
Amit Bens

Alors ce n'est pas une pile non? La pile est LAST in FIRST out. Vous devrez écrire un personnalisé ou choisir quelque chose.

2
aJ.

Le constructeur d'une pile <> prend IEnumerable <> en tant que paramètre. Il serait donc possible d'effectuer les tâches suivantes:

myStack = new Stack<item>( myStack.Where(i => i != objectToRemove).Reverse() );

Ceci est non performant à plusieurs égards.

1
Phil Carson

J'ai utilisé une liste et ajouté des méthodes d'extension, par exemple,

public static IThing Pop(this List<IThing> list)
{
  if (list == null || list.Count == 0) return default(IThing);

  // get last item to return
  var thing = list[list.Count - 1];
  // remove last item
  list.RemoveAt(list.Count-1);

  return thing;
}

public static IThing Peek(this List<IThing> list)
{
  if (list == null || list.Count == 0) return default(IThing);

  // get last item to return
  return list[list.Count - 1];
}

public static void Remove(this List<IThing> list, IThing thing)
{
  if (list == null || list.Count == 0) return;
  if (!list.Contains(thing)) return;

  list.Remove(thing); // only removes the first it finds
}

public static void Insert(this List<IThing> list, int index, IThing thing)
{
  if (list == null || index > list.Count || index < 0) return;

  list.Insert(index, thing);
}
0
user2825546

hmmmm ...... Je suis d'accord avec les deux réponses précédentes, mais si vous cherchez à pirater votre chemin, sautez simplement et sauvegardez tous les éléments jusqu'à ce que vous obteniez celui que vous voulez, et ré-appuyez dessus

Oui, c'est un code moche, peu performant, probablement bizarre qui nécessitera un long commentaire expliquant pourquoi, mais vous pouvez le faire ....

0
webclimber

Je suis tombé sur cette question. Dans mon code, j'ai créé ma propre méthode d'extension:

public static class StackExtensions
{
    public static void Remove<T>(this Stack<T> myStack, ICollection<T> elementsToRemove)
    {
        var reversedStack = new Stack<T>();

        while(myStack.Count > 0)
        {
            var topItem = myStack.Pop();
            if (!elementsToRemove.Contains(topItem))
            {
                reversedStack.Push(topItem);
            }
        }

        while(reversedStack.Count > 0)
        {
            myStack.Push(reversedStack.Pop());
        }           
    }
}

Et j'appelle ma méthode comme ceci:

var removedReportNumbers = 
selectedReportNumbersInSession.Except(selectedReportNumbersList).ToList();

selectedReportNumbersInSession.Remove(removedReportNumbers);
0