一、队列的概念:
队列(简称作队,Queue)也是一种特殊的线性表,队列的数据元素以及数据元素间的逻辑关系和线性表完全相同,其差别是线性表允许在任意位置插入和删除,而队列只允许在其一端进行插入操作在其另一端进行删除操作。
队列中允许进行插入操作的一端称为队尾,允许进行删除操作的一端称为队头。队列的插入操作通常称作入队列,队列的删除操作通常称作出队列。
下图是一个依次向队列中插入数据元素a0,a1,…,an-1后的示意图:
上图中,a0是当前 队头数据元素,an-1是当前 队尾数据元素。
为了避免当只有一个元素时,对头和队尾重合使得处理变得麻烦,所以引入两个指针:front指针指向队头元素,rear指针指向队尾元素的下一个位置,这样的话,当front指针等于rear时,此队列不是还剩一个元素,而是空队列。
二、队列的抽象数据类型:
数据集合:
队列的数据集合可以表示为a0,a1,…,an-1,每个数据元素的数据类型可以是任意的类型。
操作集合:
(1)入队列append(obj):把数据元素obj插入队尾。
(2)出队列delete():把队头数据元素删除并由函数返回。
(3)取队头数据元素getFront():取队头数据元素并由函数返回。
(4)非空否isEmpty():非空否。若队列非空,则函数返回false,否则函数返回true。
三、循环顺序队列:
线性表有顺序存储和链式存储,队列是一种特殊的线性表,同样也存在这两种存储方式。我们先来看一下队列的顺序存储。
1、顺序队列的“假溢出”:
上图中,front指针指向队头元素,rear指针指向队尾元素的下一个位置。图(d)中b、c、d出队后,front指针指向元素e,rear指针在数组外面。假设这个队列的总个数不超过5个,但目前如果接着入队的话,因数组末尾元素已经被占用,再向后加就会产生数组越界的错误,可实际上队列在下标为0、1、2、3、4的地方还是空闲的,我们把这种现象叫做“假溢出”。
2、循环顺序队列:
所以解决假溢出的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种逻辑上首尾相连的顺序存储结构称为循环队列。
如何判断循环队列究竟是空的还是满的:
现在问题又来了,我们之前说,空队列时,front指针等于rear指针,那么现在循环队列满的时候,也是front等于rear,那么如何判断循环队列究竟是空的还是满的?有如下办法:
办法1:设置一个标志位flag。初始时置flag=0;每当入队列操作成功就置flag=1;每当出队列操作成功就置flag=0。则队列空的判断条件为:rear == front && tag==0;队列满的判断条件为:rear = = front && tag= =1。
办法2:保留一个元素的存储空间。此时,队列满时的判断条件为 (rear + 1) % maxSize == front;队列空的判断条件还是front == rear。
办法3:设计一个计数器count,统计队列中的元素个数。此时,队列满的判断条件为:count > 0 && rear == front ;队列空的判断条件为count == 0。
下面是具体代码的实现
实现队列方法的接口:IQueue.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace 队列
{
interface IQueue<T>//泛型接口,因为不确定是什么类型的
{
int Count { get; }
int GetLength();
bool IsEmpty();
void Clear();
void Enqueue(T item);
T Dequeue();
T Peek();
}
}
实现顺序队列方法的类:SeqQueue.cs
using System;
using System.Collections.Generic;
using System.Text;
namespace 队列
{
class SeqQueue<T> : IQueue<T>
{
private T[] data;
private int count;//表示当前有多少个元素;
private int front;//队首指标=队首元素索引-1
private int rear;//队尾指标=队尾索引
//构造方法,初始化容量
public SeqQueue(int size)
{
data = new T [size];
count = 0;
front = -1;
rear = -1;
}
public SeqQueue() : this(10)
{
}
public int Count
{
get { return count;}
}
public void Clear()
{
count = 0;
front = 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)//当rear位于队列的最后一个索引的时候,此时rear又循环到了第一个索引
{
data[0] = item;
rear = 0;
count++;
}
else
{
data[rear + 1] = item;
rear++;
count++;
}
}
}
public int GetLength()
{
return count;
}
public bool IsEmpty()
{
return count == 0;
}
public T Peek()
{
//T temp = data[front + 1];
//return temp;
return data[front + 1];
}
}
}
测试:Program.cs
using System;
using System.Collections;
using System.Collections.Generic;
namespace 队列
{
class Program
{
static void Main(string[] args)
{
//1.使用BCL中的队列
//Queue<int> queue=new Queue<int>();
//12使用我们自己的队列方法
IQueue<int> queue=new SeqQueue<int>();
//入队(添加数据)
queue.Enqueue(23);//队首
queue.Enqueue(45);
queue.Enqueue(78);
queue.Enqueue(24);//队尾
Console.WriteLine("添加了23, 45 67 89之后队列的大小为:"+queue.Count);
//出队(取得队首的数据,并删除)
int i = queue.Dequeue();
Console.WriteLine("取得队首的数据为:"+i);
Console.WriteLine("出队之后队列的大小为:"+queue.Count);
int j = queue.Peek();
Console.WriteLine("Peek得到的结果是:"+j);
Console.WriteLine("Peek之后队列的大小为::"+queue.Count);
queue.Clear();
Console.WriteLine("CLear之后队列的大小:"+queue.Count);
Console.ReadKey();
}
}
}
运行结果: