队列实现比栈复杂一点,同样也是特殊的线性表,有顺序存储和链式存储。
参考大话数据结构
两种方式在时间上都是O(1),循环队列开辟了空间暂时不释放,链队列在申请和释放有一定的时间开销,入队出队频繁,会有细微差异。
空间上,循环队列可能会造成空间浪费,链队列虽然有指针节点的开销,可以接受,更灵活。
总结:在确定最大长度的情况下,建议循环队列(也要视情况定),不确定则采用链队列。
节点类:和栈的节点类一样
public class QueueNode<T>
{
private T data;
private QueueNode<T> next;
public T Data
{
get
{
return data;
}
set
{
data = value;
}
}
public QueueNode<T> Next
{
get
{
return next;
}
set
{
next = value;
}
}
public QueueNode()
{
data = default;
next = null;
}
public QueueNode(T data, QueueNode<T> next)
{
this.data = data;
this.next = next;
}
public QueueNode(T data)
{
this.data = data;
this.next = null;
}
public QueueNode(QueueNode<T> next)
{
this.next = next;
}
}
顺序循环队列:采用数组(List也可以),跟踪队头、队尾指针。
using System;
// 循环队列
public class CircularQueue<T>
{
private QueueNode<T>[] arr;
private int count;
public int Count
{
get { return count; }
set { count = value; }
}
//队头索引:从队头取数据
private int front;
//队尾索引:指向下一个可用的位置
private int rear;
public CircularQueue(int cap)
{
arr = new QueueNode<T>[cap];
front = 0;
rear = 0;
}
public bool IsEmpty()
{
return front == rear;
}
//留一个空间,避免判断是空还是满
public bool IsFull()
{
return (rear + 1) % arr.Length == front;
}
public void EnQueue(T data)
{
if (IsFull())
{
throw new System.Exception("满了!");
//return;
}
QueueNode<T> node = new(data);
arr[rear++] = node;
if (rear >= arr.Length)
rear %= arr.Length;
count++;
}
public T DeQueue()
{
if (IsEmpty())
{
throw new System.Exception("空了!");
//return default;
}
var node = arr[front];
arr[front++] = null;
if (front >= arr.Length)
front %= arr.Length;
count--;
return node.Data;
}
public void Clear()
{
Array.Clear(arr, 0, arr.Length);
front = 0;
rear = 0;
}
public T Peek()
{
return arr[front].Data;
}
}
链队列:保留了头结点方便操作(front节点未存数据,第一个节点是front.Next),其实也可以不要。
public class LinkedQueue<T>
{
private QueueNode<T> front;
private QueueNode<T> rear;
public LinkedQueue()
{
front = new QueueNode<T>();
rear = front;
}
private int count;
public int Count
{
get { return count; }
set { count = value; }
}
public bool IsEmpty()
{
return front == rear;
}
public void EnQueue(T data)
{
QueueNode<T> node = new(data);
rear.Next = node;
rear = node;
count++;
}
public T DeQueue()
{
if (IsEmpty())
{
//throw new System.Exception("空了");
return default;
}
QueueNode<T> node = front.Next;
front.Next = front.Next.Next;
count--;
return node.Data;
}
public void Clear()
{
front = rear;
front.Next = null;
}
public T Peek()
{
return front.Next.Data;
}
}