您的问题是您的代码将数据结构及其方法与实际程序混淆了。你需要更多地分解你的程序。您首先需要创建一个实现 FIFO 队列的类。验证它的行为是否符合您的预期。然后,使用该类编写您需要的实际程序。
正如其他人所指出的,您真正想要的数据结构似乎是一个队列。
与某些人所说的相反(“不要使用数组”),使用数组作为后备存储来实现固定容量的队列是微不足道的。
您需要将数组用作循环缓冲区。除了数组本身,您还需要跟踪其他 3 条信息:
从理论上讲,您没有比这更多的信息。然而,有一个奇点,头部和尾部碰撞的状态要么是“队列空”状态,要么是“队列满状态”。因此,您需要跟踪队列的长度。
Dequeue()
操作很简单:
尽管有一种特殊情况,当队列为空时,该Enqueue()
操作并不复杂:
- 检查队列长度。
- 如果队列长度为 0,则将第一项添加到队列中:
- 将项目添加到偏移量 0 处的数组。
- 设置头部的偏移量和尾部的偏移量为0。
如果队列长度 > 0,
- 第一个空闲槽的偏移量是当前尾指针之后的 1,如上所述以数组长度为模。
- 如果计算出的偏移量与队列头部的偏移量冲突,则队列已满:抛出溢出异常。
- 否则,将新项目添加到该计算偏移量的数组中。
- 将偏移到尾部设置为计算偏移。
最后,增加队列长度。
您可能想要实现的其他操作:
Peek()
. 有时检查队列中的下一个项目而不将其出队很有用。
Contains()
. 形式上,队列是一种不透明的数据结构。您只能在尾部添加项目并从头部删除它们。然而,检查某些东西是否已经入队可能很有用,尽管我认为用类似集合的语义来装饰队列会是一个更好的设计。
将队列的当前长度作为属性公开也很有用。
你应该能够从那里弄清楚。如果您还没有弄清楚,我将在明天通过实施来修改此答案。
. . .
正如所承诺的,这是一个固定长度队列的实现:
class ArrayQueue<T>
{
private T[] backingStore ;
private int head ; // offset to head of the queue (least recently added item)
private int tail ; // offset to tail of the queue (most recently added item)
/// <summary>
/// current queue length
/// </summary>
public int Length { get ; private set ; }
/// <summary>
/// Maximum Queue Length
/// </summary>
public int Capacity { get { return this.backingStore.Length ; } }
/// <summary>
/// Add an item to the queue
/// </summary>
/// <param name="value"></param>
public void Enqueue( T value )
{
if ( Length == 0 )
{
this.backingStore[0] = value ;
this.head = 0 ;
this.tail = 0 ;
}
else
{
// A head/tail collision means the queue is full: throw an overflow exception
if ( this.tail == this.head ) { throw new OverflowException("Maximum capacity exceeded") ; }
this.backingStore[this.tail] = value ;
}
// increment the tail and the length, wrapping the tail point if necessary
this.tail = (this.tail+1) % this.backingStore.Length ;
++this.Length ;
return ;
}
/// <summary>
/// Remove the next (oldest) item from the queue
/// </summary>
/// <returns></returns>
public T Dequeue()
{
if ( this.Length < 1 ) { throw new InvalidOperationException("queue is empty") ; }
T value = this.backingStore[head] ;
this.backingStore[head] = default(T) ; // lose the reference so the newly dequeued item can be garbage-collected.
--this.Length;
this.head = (this.head+1) % this.backingStore.Length ;
return value ;
}
/// <summary>
/// Examine the head of the queue, without removing it
/// </summary>
/// <returns></returns>
public T Peek()
{
if ( this.Length < 1 ) { throw new InvalidOperationException("queue is empty") ; }
T value = this.backingStore[head] ;
return value ;
}
/// <summary>
/// Clear/Empty the queue
/// </summary>
public void Clear()
{
// clear any object references to the queue so they can be garbage-collected
Array.Clear(this.backingStore,0,this.backingStore.Length);
this.head = 0 ;
this.tail = 0 ;
this.Length = 0 ;
return ;
}
/// <summary>
/// indicates whether or not the specified item is present in the queue
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public bool Contains( T value )
{
bool found = false ;
for ( int i = 0 ; !found && i < this.Length ; ++i )
{
int p = (this.head+1) % this.Capacity ;
found = this.backingStore[p].Equals( value ) ;
}
return found ;
}
/// <summary>
/// Create an instance of an ArrayQueue<T> having the specified fixed capacity
/// </summary>
/// <param name="capacity"></param>
/// <returns></returns>
public static ArrayQueue<T> CreateInstance( int capacity )
{
if ( capacity < 0 ) throw new ArgumentOutOfRangeException("capacity","capacity must be non-negative");
ArrayQueue<T> instance = new ArrayQueue<T>(capacity) ;
return instance ;
}
/// <summary>
/// private (and only constructor)
/// </summary>
/// <param name="capacity"></param>
private ArrayQueue( int capacity )
{
this.backingStore = new T[capacity] ;
this.Clear() ;
return ;
}
}