面试中给我的最有趣的编码挑战之一是创建一个功能队列。要求是,每次入队调用都将创建一个新队列,该队列包含旧队列和尾部的新项目。出队也将返回新队列,并将出队的项目作为出参数。
从此实现中创建IEnumerator将是非破坏性的。而且,让我告诉您,要实现性能良好的功能性队列要比实现性能良好的功能性栈困难得多(堆栈Push / Pop都在尾部工作,因为队列入队在尾部工作,出队在头上工作)。
我的意思是……通过实现自己的Pointer机制(StackNode <T>)并在枚举器中使用函数语义来创建无损堆栈枚举器是微不足道的。
public class Stack<T> implements IEnumerator<T>
{
private class StackNode<T>
{
private readonly T _data;
private readonly StackNode<T> _next;
public StackNode(T data, StackNode<T> next)
{
_data=data;
_next=next;
}
public <T> Data{get {return _data;}}
public StackNode<T> Next{get {return _Next;}}
}
private StackNode<T> _head;
public void Push(T item)
{
_head =new StackNode<T>(item,_head);
}
public T Pop()
{
//Add in handling for a null head (i.e. fresh stack)
var temp=_head.Data;
_head=_head.Next;
return temp;
}
///Here's the fun part
public IEnumerator<T> GetEnumerator()
{
//make a copy.
var current=_head;
while(current!=null)
{
yield return current.Data;
current=_head.Next;
}
}
}
一些注意事项。在语句current = _head之前调用push或pop的调用;如果没有多线程,则完成将为您提供不同的枚举堆栈(您可能需要使用ReaderWriterLock来防止这种情况)。我将StackNode中的字段设置为只读,但如果T是可变对象,则可以更改其值。如果要创建一个将StackNode作为参数的Stack构造函数(并将其头设置为传入的节点)。以这种方式构造的两个堆栈不会相互影响(我提到的可变T除外)。您可以将所需的所有内容推入并弹出,而其他则不会改变。
我的朋友就是您如何对Stack进行无损枚举。