【问题标题】:How to remove a stack item which is not on the top of the stack in C#如何在C#中删除不在堆栈顶部的堆栈项
【发布时间】:2010-10-19 09:39:42
【问题描述】:

不幸的是,一个项目只能通过“pop”从堆栈中移除。堆栈没有“删除”方法或类似的东西,但我有一个堆栈(是的,我需要一个堆栈!),我需要从中删除一些元素。

有什么诀窍吗?

【问题讨论】:

  • 这是作业吗?如果是这样,请忽略我的回答,这可能不是您所需要的,请改用 Reed Copsey 的回答:stackoverflow.com/questions/748387/…
  • 不,这不是功课,这只是一个小型私人项目,情况非常特殊^^
  • 弹出堆栈并丢弃该值有什么问题?
  • 其实没什么问题,但是弹出一个栈然后再推它比从一个简单的集合中移除一个项目要慢。

标签: c# stack


【解决方案1】:

如果您需要删除不在顶部的项目,那么您需要的不是堆栈。

尝试从列表中实现您自己的堆栈。然后您可以实现自己的推送和弹出功能(在列表中添加和删除),以及您自己的特殊 PopFromTheMiddle 功能。

例如

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

【讨论】:

  • 围绕现有容器进行自定义实现可以让您做所有您需要做的事情。如果您像我一样需要双端堆栈(destack),它还可以让您编写最棒的函数名称:PushBottom()、PopBottom() 和 PeekBottom() :)。
  • 无缘无故投反对票?这怎么可能?伙计们,如果您不喜欢我的回答,或者有什么问题,您可以告诉我以便我解决吗?我们都在这里学习 :) 谢谢。
  • 我猜反对票与你有关,这与 OP 的断言相矛盾,即他确实需要一个堆栈。为了记录,我和你在一起。你可能需要 push() 和 pop(),但是如果你需要其他的插入/移除方法,那么调用它的堆栈就不再那么有意义了;它支持堆栈语义
  • 我知道这是旧的,但我想建议修改。我会更改 items.Remove(temp);到 items.RemoveAt(items.Count - 1);。目前,如果此“堆栈”中有两个相同的项目,则会出现意外行为。
  • @Justin:它已经过时并不会减少错误,我会尽快更新。很好发现,谢谢伙计。
【解决方案2】:

考虑使用不同的容器。也许是一个链表。 然后就可以使用了

添加优先
最后添加
删除最后一个
删除第一

就像从堆栈中弹出/推送一样,您可以使用

删除

从列表中间删除任何节点

【讨论】:

    【解决方案3】:

    您可以使用LinkedList

    基于列表的删除可能效率较低。 在通过引用删除基于列表的堆栈将有 O(N) 搜索和 O(N) 调整大小。 LinkedList 搜索是 O(N),删除是 O(1)。 对于按索引删除,LinkedList 应该有 O(N) 的遍历和 O(1) 的删除,而 List 将有 O(1) 的遍历(因为它是索引)和由于调整大小而导致的 O(N) 的删除。

    除了效率之外,LinkedList 实现将使您保持在标准库中,从而使您的代码具有更大的灵活性并减少您编写的代码。

    这应该可以处理 Pop、Push 和 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
       }
    

    【讨论】:

      【解决方案4】:

      也许扩展方法会起作用,但我怀疑确实需要完全不同的数据结构。

      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】:

        在真正的堆栈中,这只能以一种方式完成 -

        弹出所有项目,直到删除您想要的项目,然后以适当的顺序将它们推回堆栈。

        不过,这不是很有效。

        如果您真的想从任何位置移除,我建议您从 List、LinkedList 或其他一些集合构建一个伪堆栈。这将使您能够轻松地执行此操作。

        【讨论】:

        • 我有两个特殊情况,其中一个堆栈有最大容量,但如果它已满,第一个将被删除
        • 我会从 LinkedList 中创建一个自定义“堆栈”。这将提供从任一端轻松移除。或者,寻找一个 Deque 集合实现。这允许堆栈和队列之类的语义,从而可以轻松实现您的标准。
        • 这里有一个 Deque 类,您可以轻松修改:codeproject.com/KB/recipes/deque.aspx
        【解决方案6】:

        那它不是栈对吧?堆栈是LAST in FIRST out。 您将不得不编写一个自定义的或选择其他的。

        【讨论】:

        • 还有哪些不同类型的堆栈?
        • 他说的是队列,栈总是后进先出
        【解决方案7】:
           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);
        

        【讨论】:

        • 我想避免这样的事情,也许LinQ有办法,但似乎我必须这样做。
        【解决方案8】:

        我在有毛病的情况下使用的一个技巧是在堆栈中的项目中添加一个“已弃用”标志。 当我想“删除”一个项目时,我只需升起该标志(并清理该对象占用的任何资源)。 然后当 Pop()ing 项目时,我只需检查标志是否被提升,然后在循环中再次弹出,直到找到未弃用的项目。

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

        您可以管理自己的项目计数,以了解有多少“真实”项目仍在队列中,如果多线程解决方案需要,显然应该使用锁定。

        我发现对于不断流经它们的队列 - 推送和弹出的项目 - 以这种方式处理它的效率要高得多,这是您可以获得的最快速度(支付 O(1) 从中间删除一个项目)如果保留的对象很小,并且在内存方面很明智,那么项目是否以合理的速度流动几乎是无关紧要的。

        【讨论】:

        • 不赞成或反对您的建议,但您可能希望将“已弃用”字段名称更改为其他名称。这并不意味着我认为你认为它意味着什么。
        • 此解决方案在许多情况下都可能有用且实用。非常容易实现。
        【解决方案9】:

        Stack 的构造函数将 IEnumerable 作为参数。因此可以执行以下操作:

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

        这在很多方面都表现不佳。

        【讨论】:

          【解决方案10】:

          hmmmm......我同意前两个答案,但如果你想破解你的方式,只需弹出并保存所有元素,直到你找到你想要的元素,然后重新推送它们

          是的,这是丑陋、性能不佳、可能很奇怪的代码,需要很长的注释来解释原因,但你可以这样做....

          【讨论】:

            【解决方案11】:

            我遇到了这个问题。在我的代码中,我创建了自己的扩展方法:

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

            我这样称呼我的方法:

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

            【讨论】:

              【解决方案12】:

              我使用了一个列表并添加了一些扩展方法,例如,

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

              【讨论】:

                【解决方案13】:

                这仍然处理整个堆栈,但它是删除条目的替代方法。不过这种写法,如果同一个条目在堆栈中多次出现,它将全部删除。

                using System.Collections.Generic;
                
                namespace StackTest
                {
                    class Program
                    {
                        static void Main(string[] args)
                        {
                            Stack<string> stackOne = new Stack<string>();
                
                            stackOne.Push("Five");
                            stackOne.Push("Four");
                            stackOne.Push("Three");
                            stackOne.Push("Two");
                            stackOne.Push("One");
                        
                            deleteStackEntry(stackOne, @"Three");
                            deleteStackEntry(stackOne, @"Five");
                        }
                        static void deleteStackEntry(Stack<string> st, string EntryToDelete)
                        {
                            // If stack is empty
                            if (st.Count == 0) return;
                
                            // Remove current item 
                            string currEntry = st.Pop();
                
                            // Remove other items with recursive call
                            deleteStackEntry(st, EntryToDelete);
                
                            // Put all items back except the one we want to delete 
                            if (currEntry != EntryToDelete)
                                st.Push(currEntry);
                        }
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2020-12-19
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2022-11-02
                  • 1970-01-01
                  • 2020-02-18
                  相关资源
                  最近更新 更多