前面我们已经介绍了常用的数据结构,list集合的实现,栈的实现,今天我们来看一下与栈不同的数据结构—队列的实现,栈是先进后出,那么我们的队列则是先进先出。
-
队列的介绍
队列(Queue)是插入操作限定在表的尾部而其它操作限定在表的头部进行的线性表。把进行插入操作的表尾称为队尾(Rear),把进行其它操作的头部称为队头(Front)。当队列中没有数据元素时称为空队列(Empty Queue)。
队列通常记为: Q= (a1,a2,…,an),Q是英文单词queue的第 1 个字母。a1为队头元素,an为队尾元素。这n个元素是按照a1,a2,…,an的次序依次入队的,出对的次序与入队相同,a1第一个出队,an最后一个出队。所以,对列的操作是按照先进先出(First In First Out)或后进后出( Last In Last Out)的原则进行的,因此,队列又称为FIFO表或LILO表。队列Q的操作示意图如图所示。
在实际生活中有许多类似于队列的例子。比如,排队取钱,先来的先取,后来的排在队尾。
队列的操作是线性表操作的一个子集。队列的操作主要包括在队尾插入元素、在队头删除元素、取队头元素和判断队列是否为空等。与栈一样,队列的运算是定义在逻辑结构层次上的,而运算的具体实现是建立在物理存储结构层次上的。因此,把队列的操作作为逻辑结构的一部分,每个操作的具体实现只有在确定了队列的存储结构之后才能完成。队列的基本运算不是它的全部运算,而是一些常用的基本运算。 -
顺序队列(循环顺序队列)
用一片连续的存储空间来存储队列中的数据元素,这样的队列称为顺序队列(Sequence Queue)。类似于顺序栈,用一维数组来存放顺序队列中的数据元素。队头位置设在数组下标为 0 的端,用 front 表示;队尾位置设在数组的另一端,用 rear 表示。 front 和 rear 随着插入和删除而变化。当队列为空时, front=rear=-1。
如果再有一个数据元素入队就会出现溢出。但事实上队列中并未满,还有空闲空间,把这种现象称为“假溢出”。这是由于队列“队尾入队头出”的操作原则造成的。解决假溢出的方法是将顺序队列看成是首尾相接的循环结构,头尾指示器的关系不变,这种队列叫循环顺序队列(Circular sequence Queue)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Queue
{
public interface IQueue<T>
{
/// <summary>
/// 索引
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
T this[int index] { get; }
/// <summary>
/// 取得队列中数据的个数方法
/// </summary>
int Count { get; }
/// <summary>
/// 取得队列的长度
/// </summary>
/// <returns></returns>
int GetLength();
/// <summary>
/// 判断队列是否为空
/// </summary>
/// <returns></returns>
bool IsEmpty();
/// <summary>
/// 清空队列
/// </summary>
void Clear();
/// <summary>
/// 入队列
/// </summary>
void Enqueue(T item);
/// <summary>
/// 出队列
/// </summary>
/// <returns></returns>
T Dequeue();
/// <summary>
/// 取得队首的元素
/// </summary>
/// <returns></returns>
T Peek();
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Queue
{
/// <summary>
/// 顺序队列
/// </summary>
/// <typeparam name="T"></typeparam>
public class Sequeue<T> : IQueue<T>
{
private T[] data;
/// <summary>
/// 表示当前有多少个元素
/// </summary>
private int count;
/// <summary>
/// 队首
/// </summary>
private int front;//=队首元素索引减一
/// <summary>
/// 队尾
/// </summary>
private int rear;//=队尾元素索引
public Sequeue() : this(10)
{
}
public Sequeue(int size)
{
data = new T[size];
count = 0;
front = -1;
rear = -1;
}
public T this[int index]
{
get { return data[index]; }
}
public int Count
{
get { return count; }
}
public void Clear()
{
count = 0;
front = -1;
rear = -1;
}
public T Dequeue()
{
if (count > 0)
{
T temp = data[front + 1];
front++;
count--;
return temp;
}
else
{
//队列无数据
Console.WriteLine("队列目前还没有数据哦!无法取出数据,请先添加数据~");
return default(T);
}
}
public void Enqueue(T item)
{
if (count == data.Length)
{
Console.WriteLine("队列已满,无法添加新的数据~");
}
else
{
if (rear == data.Length - 1)//说明要添加到数组的0号索引位置了,以此来实现循环队列
{
data[0] = item;
rear = 0;
}
else
{
data[rear + 1] = item;
rear++;
}
count++;
}
}
public int GetLength()
{
return count;
}
public bool IsEmpty()
{
return count == 0;
}
public T Peek()
{
return data[front + 1];
}
}
}
- 链式队列
队列的另外一种存储方式是链式存储,这样的队列称为链队列(Linked Queue)。同链栈一样,链队列通常用单链表来表示,它的实现是单链表的简化。所以,链队列的结点的结构与单链表一样,如图所示。由于链队列的操作只是在一端进行,为了操作方便,把队头设在链表的头部,并且不需要头结点。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Queue
{
public class Node<T>
{
private T data;
private Node<T> next;
public Node(T data)
{
this.data = data;
}
public T Data
{
get { return data; }
set { data = value; }
}
public Node<T> Next
{
get { return next; }
set { next = value; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Queue
{
/// <summary>
/// 链式队列
/// </summary>
/// <typeparam name="T"></typeparam>
public class LinkQueue<T> : IQueue<T>
{
private Node<T> front; //头结点
private Node<T> rear;//尾结点
private int count;//表示元素的个数
public LinkQueue()
{
front = null;
rear = null;
count = 0;
}
public T this[int index]
{
get
{
Node<T> temp = front;
for (int i = 1; i < index; i++)
{
//让temp后移一个位置
temp = temp.Next;
}
return temp.Data;
}
}
public int Count
{
get { return count; }
}
public void Clear()
{
front = null;
rear = null;
count = 0;
}
public T Dequeue()
{
if (count == 0)
{
Console.WriteLine("队列为空哦~");
return default(T);
}
else if (count == 1)
{
T temp = front.Data;
front = rear = null;
count = 0;
return temp;
}
else
{
T temp = front.Data;
front = front.Next;//头结点的下个结点变成头结点了
count--;
return temp;
}
}
public void Enqueue(T item)
{
Node<T> newNode = new Node<T>(item);
if (count == 0)//新的结点就是我们的头结点
{
front = newNode;
rear = newNode;
count = 1;
}
else
{
rear.Next = newNode;//当前的尾结点的下一个结点就是我们的新结点
rear = newNode;//重新赋值我们的尾结点
count++;
}
}
public int GetLength()
{
return count;
}
public bool IsEmpty()
{
return count == 0;
}
public T Peek()
{
if (front != null)
{
return front.Data;
}
else
{
return default(T);
}
}
}
}
以上就是队列的两种实现方式了,里面的算法肯定不是最优的,高手请绕道~~~