如何在C#中删除不在堆栈顶部的堆栈项

时间:2009-04-14 16:32:06

标签: c# stack

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

这样做有诀窍吗?

13 个答案:

答案 0 :(得分:46)

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

尝试从List中创建自己的堆栈实现。然后你可以实现自己的推送和弹出功能(在列表中添加和删除),以及你自己特殊的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);
    }
}

答案 1 :(得分:11)

考虑使用不同的容器。也许是LinkedList。 然后你可以使用

AddFirst
AddLast
RemoveLast
RemoveFirst

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

Remove

从列表中间删除任何节点

答案 2 :(得分:6)

您可以使用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
   }

答案 3 :(得分:5)

也许扩展方法可行,但我怀疑完全需要完全不同的数据结构。

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

答案 4 :(得分:4)

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

弹出所有项目,直到删除所需的项目,然后按适当顺序将它们推回到堆栈中。

但这并不是很有效率。

如果你真的想从任何位置删除,我建议从List,LinkedList或其他一些集合构建一个伪栈。这样可以让您轻松完成控制。

答案 5 :(得分:3)

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

答案 6 :(得分:3)

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

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

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

我发现对于持续流过它们的队列 - 推送和弹出的项目 - 以这种方式处理它的效率要高得多,它可以获得最快的速度(支付O(1)从中间移除项目)并且如果保留的对象很小,那么如果物品以合理的速度流动,那么它几乎无关紧要。

答案 7 :(得分:2)

然后它不是堆栈对吗?堆栈为LAST in FIRST out。 您必须编写自定义或选择其他内容。

答案 8 :(得分:1)

Stack&lt;&gt;的构造函数采用IEnumerable&lt;&gt;作为参数。因此可以执行以下操作:

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

这在许多方面都不具备效果。

答案 9 :(得分:0)

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

是的,丑陋,表现不佳,可能是奇怪的代码需要长时间的评论来解释原因,但你可以做到....

答案 10 :(得分:0)

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

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

答案 11 :(得分:0)

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

import bisect
import math
import pathlib


primes = []
last_prime = None


def _get_primes():
    """
    Load all the primes in global primes. Set global last_prime to last prime
    read.
    """

    global primes
    global last_prime
    path_to_primes = pathlib.Path(__file__).parent \
            .joinpath('../resources/primes.txt')
    with path_to_primes.open() as file:
        for line in file:
            for n in line.split():
                n = n.strip()
                if n:
                    n = int(n)
                    primes.append(n)
    last_prime = primes[-1]


def gen_primes_before(n):
    """
    Generates all the primes before n in reverse order.
    """

    assert n <= last_prime, "Maximum value for n is {}".format(last_prime)
    pos = bisect.bisect_left(primes, n)
    if pos:
        yield from primes[:pos]


def gen_factors(n):
    """
    Generates all the factors of a number. May return some values multiple
    times. Values returned are not ordered.
    """
    type_n = type(n)
    assert type_n is int or (type_n is float and n.is_integer()), "Wrong type"
    n = int(n)
    r = int(math.sqrt(n)) + 1
    assert r <= last_prime, "n is over limit"
    yield 1
    yield n
    for prime in gen_primes_before(r):
        partner = n/prime
        if partner.is_integer():
            yield from gen_factors(prime)
            yield from gen_factors(partner)


def get_factors(n):
    """
    Get all the factors of n as a sorted list.
    """
    return sorted(set(gen_factors(n)))


_get_primes()
if __name__ == '__main__':
    l = (1e9,)
    for n in l:
        print("The factors of {} are {}".format(n, get_factors(n)))

答案 12 :(得分:0)

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

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