How to remove a stack item which is not on the top

2019-06-14 20:47发布

Unfortunately an item can only be removed from the stack by "pop". The stack has no "remove" method or something similar, but I have a stack (yes I need a stack!) from which I need to remove some elements between.

Is there a trick to do this?

标签: c# stack
12条回答
【Aperson】
2楼-- · 2019-06-14 21:27

I came across this question. In my code I created my own extension method:

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

And I call my method like this:

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

selectedReportNumbersInSession.Remove(removedReportNumbers);
查看更多
在下西门庆
3楼-- · 2019-06-14 21:28

In a true stack, this can only be done one way -

Pop all of the items until you remove the one you want, then push them back onto the stack in the appropriate order.

This is not very efficient, though.

If you truly want to remove from any location, I'd recommend building a pseudo-stack from a List, LinkedList or some other collection. This would give you the control to do this easily.

查看更多
smile是对你的礼貌
4楼-- · 2019-06-14 21:32

Then it is not a stack right? Stack is LAST in FIRST out. You will have to write a custom one or choose something else.

查看更多
爷、活的狠高调
5楼-- · 2019-06-14 21:32

The constructor of a Stack<> takes IEnumerable<> as a parameter. So it would be possible to perform the following:

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

This is non performant in a number of ways.

查看更多
可以哭但决不认输i
6楼-- · 2019-06-14 21:35

A trick I use in hairy situations is adding a 'deprecated' flag to the items in the stack. When I want to 'remove' an item, I simply raise that flag (and clean up any resources that are taken by the object). Then when Pop()ing items I simply check if the flag is raised, and pop again in a loop until a non-deprecated item is found.

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

You can manage your own item-count to know how many 'real' items are still in the queue, and obviously locking should be employed if this is required for multi-threaded solution.

I found that for queues that have constant flow through them - items pushed and popped - it's much more efficient to hanle it this way, its the fastest you can get (paying O(1) for removing an item from the middle) and memory wise if the object that is retained is small, it's mostly irrelevant if the items flow in a reasonable pace.

查看更多
虎瘦雄心在
7楼-- · 2019-06-14 21:37

You could use a LinkedList

List based removal will likely be less efficient. In removal by reference List based stacks will have O(N) search and O(N) resizing. LinkedList search is O(N) and removal is O(1). For removal by index, LinkedList should have O(N) traversal and O(1) removal, while List will have O(1) traversal (because it is indexing) and O(N) removal due to resizing.

Besides efficiency, a LinkedList implementation will keep you in the standard library, opening your code to more flexibility and have you writing less.

This should be able to handle Pop, Push, and 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
   }
查看更多
登录 后发表回答