数据结构--队列
队列ADT
队列像栈一样,也是一个表。使用队列时,从一端进行插入,从另一端进行删除。队列的基本操作是入队(Enquence),它是在表的末端即队尾(rear)插入一个元素;还有出队(Dequeue),它是删除(或返回)在表的开头即队头(front)的元素。队列同样也有两种存储方法,分别是顺序循环存储结构和链式存储结构。
队列的数组实现
对于每一个队列的数据结构,我们保留一个数组Queue[ ],以及位置Front 和 Rear ,他们代表队列的两端。我们还要记录实际存在于队列的元素的个数Size。为使一个元素X入队,我们让Size 和 Rear 增加1,然后置Queue[Rear] = X。为使一个元素出队,我们置返回值为Queue[Front],Size减1,然后使Front增1。但是这种实现存在一个问题,经过多次入队后,再下一次入队就会是一个不存在的位置,然而,队列中也许只存在几个元素,因为若干元素可能已经出队了。简单的解决方法是:只要Front 和 Rear 到达数组的尾端,它就绕回开头,这种叫做循环数组实现。
1.定义顺序循环队列结构体
typedef struct
{
ElementType Queue[MaxQueueSize];
int Rear;
int Front;
int Size;
}SequenceQueue;
typedef struct
{
ElementType Queue[MaxQueueSize];
int Rear;
int Front;
int Size;
}SequenceQueue;
2.初始化顺序循环队列
void QueueInitiate(SequenceQueue *Q)
{
Q->Rear = 0;
Q->Front = 0;
Q->Size = 0;
}
3.判断队列是否为空
int QueueNotEmpty(SequenceQueue Q)
{
if(Q.Size == 0)
return 0; /*队列为空时返回0,否则返回1 */
else
return 1;
}
4.入队列
int EnQueue(SequenceQueue *Q, ElementType X)
{
if(Q->Size > 0 && Q->Rear == Q->Front)
{
printf("Out of space");
return 0;
}
else
{
Q->Queue[Q->Rear] = X;
Q->Rear = (Q->Rear + 1) % MaxQueueSize;
Q->Size ++;
return1;
}
}
5.出队列
int DeQueue(SequenceQueue *Q, ElementType *X)
{
if(Q->Size == 0)
{
printf("Out of space");
return 0;
}
else
{
*X = Q->Queue[Q->Front];
Q->Front = (Q->Front + 1) % MaxQueueSize;
Q->Size--;
return 1;
}
}
6.获取队头数据元素
int QueueGet(SequenceQueue *Q, ElementType *X)
{
if(Q->Size == 0)
{
printf("Out of space");
return 0;
}
else
{
*X = Q->Queue[Q->Front];
return 1;
}
}
队列的链表实现
1. 链式队列的结构体
typedef struct QueueNode
{
ElementType data;
struct QueueNode *next;
}LinkQueueNode;
typedef struct
{
LinkQueueNode * Front;
LinkQueueNode * Rear;
}LinkQueue;
2.队列初始化
void QueueInitiate(LinkQueue *Q)
{
Q->Rear = NULL;
Q->Front = NULL;
}
3.判断队列是否为空
int QueueNotEmpty(LinkQueue Q)
{
if(Q->Front == NULL)
return 0;
else
return 1;
}
4.入队列
int EnQueue(LinkQueue *Q, ElementType X)
{
LinkQueueNode *p;
if((p=(LinkQueueNode *) malloc(sizeof(LinkQueueNode))) == NULL)
{
printf("Out of space");
return 0;
}
p->data = x;
p->next = NULL;
if(Q->Rear != NULL)
Q->Rear->next = p;
Q->Rear = p;
if(Q->Front == NULL)
Q->Front = p;
return 1;
}
5.出队列
int DeQueue(LinkQueue *Q, ElementType *X)
{
LinkQueueNode *p;
if(Q->Front == NULL)
{
printf("Out of space");
return 0;
}
else
{
*X = Q->Front->data;
p = Q->Front;
Q->Front = Q->Front->next;
if(Q->Front == NULL)
Q->Rear = NULL;
free(p);
return 1;
}
}
6.返回队头数据元素
int QueueGet(LinkQueue Q, ElementType *X)
{
if(Q.Front == NULL)
{
printf("Out of space");
return 0;
}
else
{
*X = Q.Front->data;
return 1;
}
}
7.取队头数据元素
void Destory(LinkQueue Q)
{
LinkQueueNode *p, *p1;
p = Q->Front;
while(p != NULL)
{
p1 = p;
p = p->next;
free(p1);
}
}
队列的应用
1.解决具有先进先出排队特点的问题
2.解决农夫过河问题
一个农夫带着一只狼,一只羊和一棵白菜,欲从河的北岸坐船到南岸去。小船只能容纳农夫和一件物品,只有农夫撑船。另外,狼会吃羊,羊会吃白菜,所以农夫不能单独地留下狼和羊自己离开,或者留下羊和白菜自己离开。
解决:
1.将农夫、狼、羊和白菜在河的北岸和南岸的情况,抽象为N种不同状态组合,而这些状态之间可以相互转变,只要找到一组运算可以将初始状态转为目标状态,那么问题就解决了。
2.使用广度优先搜索获得所有的状态,显然可以用队列的方式实现。
3.使用一个4位二进制数从左到右表示农夫、狼、白菜和羊的位置,0表示该角色在南岸,1则表示该角色在北岸。那么问题转为从初始状态0000(全部在南岸)开始,寻找一种安全状态序列1111(全部在北岸)的过程。
4.用函数location表示这个4位的二进制,用函数safe判断安全状态。
5.利用以整数队列来实现广度优先搜索,记录已被访问的各个状态以及相应的路径,构造一个整数顺序表route记录所有的状态,共16种(0000~1111)。在顺序表中每个元素初始值为-1。
3.使用队列给出提高运行效率的算法