【问题标题】:Can I Limit the depth of a Generic Stack?我可以限制通用堆栈的深度吗?
【发布时间】:2019-03-14 18:45:52
【问题描述】:

是否有限制 System.Collection.Generics.Stack 深度的内置方法?所以如果你处于最大容量,推送一个新元素会移除堆栈的底部?

我知道我可以通过转换为数组并重建堆栈来做到这一点,但我想可能已经有一个方法了。

编辑:我写了一个扩展方法:

    public static void Trim<T> (this Stack<T> stack, int trimCount) 
    {
        if (stack.Count <= trimCount)
            return;

       stack = new 
            Stack<T>
            (
                stack
                    .ToArray()
                    .Take(trimCount)
            );           
    }

因此,它在修剪时返回一个新堆栈,但功能方式不是不变性 =)

原因是我将应用程序的撤消步骤存储在堆栈中,而我只想存储有限数量的步骤。

【问题讨论】:

  • 如果你实现你的行为,这不是一个真正的堆栈。可以说更正确的行为是抛出异常或(在并发系统中)阻塞直到空间可用(假设当前线程不是唯一推入堆栈的线程)。
  • 我的第一个想法是,您这样做并没有帮助您的用户。
  • 杰伊,详细点?您将如何存储深度受限的撤消信息?

标签: c# generics stack


【解决方案1】:

您要查找的内容称为dropout stack。 AFAIK,BCL 不包含一个,尽管它们实现起来很简单。通常,撤消和重做功能依赖于此类数据结构。

它们基本上是一个数组,当你压入堆栈时,堆栈的“顶部”会在数组周围移动。最终,当堆栈已满时,顶部将返回到开头并替换堆栈的“底部”。

Google 没有提供太多相关信息。这是我能找到的最好的:

(警告 PDF) http://courses.cs.vt.edu/~cs2704/spring04/projects/DropOutStack.pdf

这里有一些样板代码,可以帮助您入门。我会让你填写其余的(完整性检查、计数、索引器等)

class DropOutStack<T>
{
    private T[] items;
    private int top = 0;
    public DropOutStack(int capacity)
    { 
        items = new T[capacity];
    }

    public void Push(T item)
    {
        items[top] = item;
        top = (top + 1) % items.Length;
    }
    public T Pop()
    {
        top = (items.Length + top - 1) % items.Length;
        return items[top];
    }
}

【讨论】:

    【解决方案2】:

    您实际上正在查看类似于循环列表实现的东西。 CodeProject 的 PIEBALDconsult 完成了 LimitedQueue 实现。这与您的要求相似。您只需要像作者所做的那样包装 Stack 而不是 Queue 。另外作者还实现了索引器,如果您需要访问除顶部堆栈以外的任何其他内容(可能显示撤消列表),这很方便。

    编辑:作者的实现还会在最后一个(首先,取决于它是队列还是堆栈)被删除时引发一个事件,以便您知道什么时候被丢弃。

    【讨论】:

      【解决方案3】:

      我看不到方法。您可以从 Stack&lt;T&gt; 继承,但似乎没有任何有用的东西可以覆盖。

      简单(如果有点乏味)的方法是将Stack&lt;T&gt; 包装在您自己的,例如LimitedStack&lt;T&gt; 中。然后实现您想要的方法并传递给内部Stack&lt;T&gt;,同时在Push 方法和您需要的任何其他地方包含您的限制逻辑。

      编写所有这些传递成员很痛苦,特别是如果您要实现与Stack&lt;T&gt; 相同的所有接口......但另一方面,您只需执行一次然后就完成了。

      【讨论】:

        【解决方案4】:

        我相信您正在寻找(可能已修改)dequeue - 允许从任一端访问的数据结构。

        【讨论】:

        • 不知道为什么这被否决了 - 对我来说似乎是一个完全合理的解决方案。
        【解决方案5】:

        Greg Dean =&gt; dropout stack 提供的解决方案很好,但我认为主要问题是关于在堆栈溢出时移除堆栈底部 但是提供的解决方案只是在堆栈被填满后替换堆栈的最后一项,因此您无法获得真实的历史记录,

        但是要获得真实的历史记录,一旦列表达到您的特定容量,您需要移动列表,但这是一个扩展操作,

        所以我认为最好的解决方案是链表

        这是我解决问题的方法

        
        public class HistoryStack<T>
        {
            private LinkedList<T> items = new LinkedList<T>();
            public List<T> Items => items.ToList();
            public int Capacity { get;}
            public HistoryStack(int capacity)
            {
                Capacity = capacity;
            }
        
            public void Push(T item)
            {
                // full
                if (items.Count == Capacity)
                {
                    // we should remove first, because some times, if we exceeded the size of the internal array
                    // the system will allocate new array.
                    items.RemoveFirst();
                    items.AddLast(item);
                }
                else
                {
                    items.AddLast(new LinkedListNode<T>(item));
                }
            }
        
            public T Pop()
            {
                if (items.Count == 0)
                {
                    return default;
                }
                var ls = items.Last;
                items.RemoveLast();
                return ls == null ? default : ls.Value;
            }
        }
        
        
        

        测试一下

        
        var hs = new HistoryStack<int>(5);
        hs.Push(1);
        hs.Push(2);
        hs.Push(3);
        hs.Push(4);
        hs.Push(5);
        hs.Push(6);
        hs.Push(7);
        hs.Push(8);
        
        var ls = hs.Items;
        Console.WriteLine(String.Join(",", ls));
        
        Console.WriteLine(hs.Pop());
        Console.WriteLine(hs.Pop());
        
        hs.Push(9);
        
        
        Console.WriteLine(hs.Pop());
        Console.WriteLine(hs.Pop());
        Console.WriteLine(hs.Pop());
        Console.WriteLine(hs.Pop());
        Console.WriteLine(hs.Pop()); // empty
        Console.WriteLine(hs.Pop()); // empty
        
        

        结果

        4,5,6,7,8
        8
        7
        9
        6
        5
        4
        0
        0
        
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-04-26
          • 1970-01-01
          • 2020-03-25
          相关资源
          最近更新 更多