您的位置:首页 > 理论基础 > 数据结构算法

数据结构与算法分析(8)表、栈和队列(三)

2016-06-12 17:33 417 查看
[b] 介绍队列的相关知识:[/b]

[b] (3)队列ADT:[/b]

像栈一样,队列也是表。然而,使用队列时插入在一端进行而删除在另一端进行。

[b]3.1队列模型[/b]

队列的基本操作是Enqueue(入队),它是在表的末端插入一个元素;还有Dequeue(出队),它是删除(或同时)返回在表的开头的元素。





[b] 3.2队列的数组实现[/b]

如同栈的情形一样,对于队列而言任何表的实现都是合法的。队列的链表实现是直接的,现在我们只讨论队列的数组实现。

1)队列数组实现的ADT:

#define MinQueueSize (5)
typedef int ElementType;
struct QueueRecord{
int Capacity;//队列包含元素的最大值
int Front;
int Rear;
int Size;//现有队列的大小
ElementType *Array;
};
typedef struct QueueRecord Queue;
int IsEmpty(Queue Q);
int IsFull(Queue Q);
Queue CreateQueue(int MaxElements);
void DisposeQueue(Queue Q);
void MakeEmpty(Queue Q);
void EnQueue(ElementType X,Queue Q);
ElementType Front(Queue Q);
void Dequeue(Queue Q);
ElementType FrontAndDequeue(Queue Q);
int IsEmpty(Queue Q){
return Q->Size==0;
}
void MakeEmpty(){
Q->Size=0;
Q->Front=1;
Q->Rear=0;
}
//对下标进行处理,防止跃出数组边界
static int Succ(int Value,Queue Q){
if(++Value==Q->Capacity){
value=0;
}
return Value;
}
void Enqueue(ElementType X,Queue Q){
if(IsFull(Q)){
printf("队列已经满了!);
}else{
Q->Size++;
Q->Rear=Succ(Q->Rear,Q);
Q->Array[Q->Rear]==X;
}
}


2)队列的数组实现的实例:

将一个随机无序数组中的每个元素依次插入队列,再执行出队操作和入队操作。

//队列的应用实例
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MinQueueSize (5)

struct QueueRecord;
typedef int ElementType;
typedef struct QueueRecord *Queue;

int IsEmpty(Queue Q);
int IsFull(Queue Q);
Queue CreateQueue(int MaxElements);
void DisposeQueue(Queue Q);
void MakeEmpty(Queue Q);
void Enqueue(ElementType X,Queue Q);
ElementType Front(Queue Q);
void Dequeue(Queue Q);
ElementType FrontAndDequeue(Queue Q);
struct QueueRecord{
int Capacity;//队列包含元素的最大值
int Front;
int Rear;
int Size;//现有队列的大小
ElementType *Array;
};

int *RandInt4(int total,int i,int j,int order){
int *numberGroup=malloc(total*sizeof(int));//用于返回一个指定大小的数组
int tempory[j-i+1];//辅助产生随机数的数组。
int k;//用于循环遍历
int x;//接收产生的随机变量
srand(time(NULL));
//初始化辅助数组
for(k=0;k<=j-i;k++){
tempory[k]=0;
}
for(k=0;k<total;k++){
L:        x=rand()%(j-i+1)+i;//产生从i到j,包括i和j的随机数
if(tempory[x-i]==0){
tempory[x-i]=1;
//当需要产生的数组是无序的则执行:
if(order==0){
numberGroup[k]=x;
}
}else{
goto L;
}
}
//当需要产生有序的随机数组时执行:
int w=0;
if(order!=0){
for(k=0;k<j-i+1;k++){
if(tempory[k]==1){
numberGroup[w++]=k+i;
if(w>=total){
break;
}
}
}
}

return numberGroup;
}
Queue CreateQueue(int MaxElements){
if(MaxElements<MinQueueSize){
printf("定义的队列的大小小于5");
MaxElements=MinQueueSize;
}
Queue Q=malloc(sizeof(struct QueueRecord));
Q->Capacity=MaxElements;
Q->Array=malloc(sizeof(int)*MaxElements);
MakeEmpty(Q);
return Q;
}
void MakeEmpty(Queue Q){
Q->Front=0;//在数组的末尾,下标为MaxElements-1
Q->Rear=Q->Capacity-1;//在数组的开头,下标为0
Q->Size=0;//
}
//判断是否Front和Rear是否挨着,只有挨着队列才有可能为空或者满,当挨着时也有可能是两个元素。但此时size=2;
int IsFRClosed(Queue Q){
int size=(Q->Rear-Q->Front+1)%Q->Capacity;
if(size<0){
size+=Q->Capacity;
}
if(size>Q->Capacity){
size-=Q->Capacity;
}
//当size==0时两种情况,一种是队列为空,一种是队列满了。
return size==0;
}
int IsEmpty(Queue Q){
if(IsFRClosed(Q))//这里这样做,只是更加不容易出错!
return Q->Size==0;
else
return 0;
}
int IsFull(Queue Q){
if(IsFRClosed(Q)){
return Q->Size==Q->Capacity;
}else
return 0;
}
void Enqueue(int element,Queue Q){
if(!IsFull(Q)){
Q->Size++;
Q->Rear+=1;
if(Q->Rear==Q->Capacity){
Q->Rear=0;
}
Q->Array[Q->Rear]=element;
}else{
printf("  :无法将%2d插入队列,队列已满!\n",element);
}
}
void Dequeue(Queue Q){
if(!IsEmpty(Q)){
Q->Size--;
Q->Front++;
if(Q->Front==Q->Capacity    ){
Q->Front=0;
}
}
}
int FrontAndDequeue(Queue Q){
int tempFront=Q->Front;
//printf("Front=%4d\n",Q->Front);
Dequeue(Q);
return Q->Array[tempFront];
}
int Front(Queue Q){
return Q->Array[Q->Front];
}
int main(){
int *tempGroup=RandInt4(40,0,100,0);
int *tempGroup2=RandInt4(30,0,100,0);
int i;
Queue Q=CreateQueue(50);
printf("1.入队40个元素!\n");
for(i=0;i<40;i++){
printf("%4d",tempGroup[i]);
Enqueue(tempGroup[i],Q);
if((i+1)%20==0){
printf("\n");
}
}
printf("2.出队10个元素!\n");
for(i=0;i<10;i++){
printf("%4d",Front(Q));
Dequeue(Q);
if((i+1)%10==0){
printf("\n");
}
}
printf("3.再入队30个元素!\n");
for(i=0;i<30;i++){
printf("%4d",tempGroup[i]);
Enqueue(tempGroup2[i],Q);
if((i+1)%20==0){
printf("\n");
}
}
printf("4.显示此队列中的所有出队结果!\n");
int size=Q->Size;
for(i=0;i<size;i++){
printf("%4d",FrontAndDequeue(Q));
if((i+1)%20==0){
printf("\n");
}
}
printf("\n5.Front和Rear的位置是:\n");
printf("Front=%d,Rear=%d",Q->Front,Q->Rear);
}




[b] 3.3队列的应用:[/b]

有几种使用队列给出提高运行效率的算法,我们将在第九章的图论算法中讨论他们。现在先给出某些应用队列的例子:

1)当作业送交给一台打印机,他们就按照到达的顺序被排列起来。因此被送往行式打印机的作业基本是被放到一个队列中。

2)实际生活中的每次排队都应该是一个队列。

3)在计算机网络中,有许多种PC机的网络设置,其中磁盘是放在一台叫做文件服务器上的。使用其他计算机的用户是按照先到先使用的原则来访问文件的。因此其数据结构是一个队列。

[b]3.4排队论[/b]

处理用概率的方法计算用户排队等待时间、等待服务器的队列能够排多长,以及其他诸如此类的问题将用到被称为排队论(queueing theory)的整个数学分支。问题的答案依赖于用户加入队列的频率以及一旦用户得到服务处理时处理服务所花费的时间。这两个参数作为概率分布函数给出。

[b] 3.5队列的应用实例:[/b]

迷宫问题:迷宫实验是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置一块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口到出口,而不走错一步。老鼠经多次试验终于得到它学习走迷宫的路线。

设计一个计算机程序对任意设定的迷宫,求出一条从入口到出口的通路,或得出没有通路的结论。

1)第一种方法:用栈实现:

解题思路:从起始结点开始,寻找当前结点的下一个有效的结点(找下一个结点的顺序依照“右下左上”),如果下一个结点是可通过的'-',则压入栈中,并标志其为*,代表已经走过,将此结点作为当前结点再次重复操作。如果下一个结点不能通过,则按照找结点的顺序(右下左上)找下一个。如果四个方向的都不行。则将当前结点从栈中弹出。执行上一次压入栈中的那个结点的找结点过程。直到遍历所有能到达的结点或者找到出口为止。

//迷宫问题的栈的解法
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//在栈中存地图上区块的位置信息
#define mapSize (10)
struct BlockNode{
int row;
int column;
int direction;//用于指示下一个区块相对于当前区块的位置,依序右下左上,值为1234.
struct BlockNode *Next;
};//用来构成栈的元素结点
struct PositionNode{
int x;
int y;
};
struct MazNode{
char mapStatus[mapSize][mapSize];//存迷宫指定位置的状态
struct PositionNode start;//起始位置
struct PositionNode end;//结束位置
};
typedef struct MazNode *Map;
typedef struct BlockNode *BlockStack;
typedef struct BlockNode *Block;
typedef struct PositionNode *Position;
void initMap(Map map){
srand(time(NULL));
int i,j;int x,y;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
map->mapStatus[i][j]='#';
}
}//建立围墙
for(i=0;i<50;i++){
x=rand()%8+1;
y=rand()%8+1;
map->mapStatus[x][y]='-';
}
map->start.x=1;map->start.y=1;
map->end.x=8;map->end.y=8;
map->mapStatus[1][1]='m';//mouse代表入口
map->mapStatus[8][8]='c';//cat代表出口
}
void printMap(Map map){
int i,j;
for(i=0;i<10;i++){
for(j=0;j<10;j++){
printf("%c",map->mapStatus[i][j]);
}
printf("\n");
}
}
BlockStack createStack(){
BlockStack L=malloc(sizeof(struct BlockNode));
L->Next=NULL;
return L;
}
Block Top(BlockStack stack){
return stack->Next;
}
void Pop(BlockStack    stack){
stack->Next=stack->Next->Next;
}
void Push(Block    temp,BlockStack    stack){
temp->Next=stack->Next;
stack->Next=temp;
}
int IsEmpty(BlockStack stack){
return stack->Next==NULL;
}
Position FindNextPosition(Block tempBlock){
Position p=malloc(sizeof(struct PositionNode));
switch(tempBlock->direction)
{
case 1://右
p->x=tempBlock->row;
p->y=tempBlock->column+1;
break;
case 2://下
p->x=tempBlock->row+1;
p->y=tempBlock->column;
break;
case 3://左
p->x=tempBlock->row;
p->y=tempBlock->column-1;
break;
case 4://上
p->x=tempBlock->row-1;
p->y=tempBlock->column;
break;
default:printf("error!\n");
}
return p;
}
int Pass(Position p,Map map){
//printf("地图中的是%c\n",map->mapStatus[p->x][p->y]);
if(map->mapStatus[p->x][p->y]=='-'||map->mapStatus[p->x][p->y]=='c')
return 1;
else
return 0;
}
void makeFoot(Position p,Map map){
map->mapStatus[p->x][p->y]='*';
}
void printRoute(BlockStack stack){
Block temp;
while(stack->Next!=NULL){
temp=Top(stack);
printf("<%d,%d>",temp->row,temp->column);
Pop(stack);
}
}
void printStack(BlockStack    stack){
BlockStack ptr=stack->Next;
while(ptr!=NULL){
printf("<%d,%d>",ptr->row,ptr->column);
ptr=ptr->Next;
}
printf("\n");
}
BlockStack FindRoute(Map map){
BlockStack stack=createStack();
Block currentBlock;
Block temp=malloc(sizeof(struct BlockNode));
temp->row=map->start.x;
temp->column=map->start.y;
temp->direction=1;
Push(temp,stack);
//printStack(stack);
//当栈不为空时,取栈顶元素为当前元素
while(!IsEmpty(stack)){
currentBlock=Top(stack);
//当当前结点的四个方向的结点没有视察完时,逐个视察
while(currentBlock->direction<=4){
Position nextAvaliable=FindNextPosition(currentBlock);
//printf("结点<%d,%d>的下%d个有效的位置是<%d,%d>\n",currentBlock->row,currentBlock->column,currentBlock->direction,nextAvaliable->x,nextAvaliable->y);
//printf("%c",map->mapStatus[nextAvaliable->x][nextAvaliable->y]);
if(Pass(nextAvaliable,map)){
makeFoot(nextAvaliable,map);
//printMap(map);printf("\n");
temp=malloc(sizeof(struct BlockNode));
temp->row=nextAvaliable->x;
temp->column=nextAvaliable->y;
temp->direction=1;
Push(temp,stack);
//printStack(stack);
currentBlock=temp;
//如果当前结点是末尾结点
if(currentBlock->row==map->end.x&¤tBlock->column==map->end.y){
return stack;
}
}else{
currentBlock->direction++;
}
}
Block ord=Top(stack);
map->mapStatus[ord->row][ord->column]='@';//将已经访问过的点并且从栈中弹出的设为“@”。
Pop(stack);
//printStack(stack);
}
return NULL;
}

int main(){
Map map=malloc(sizeof(struct MazNode));
initMap(map);//初始化迷宫地图
printMap(map);
printf("\n");
BlockStack routeStack=FindRoute(map);//start为起始点,end为终点
if(routeStack==NULL){
printf("没有路径从起始点(%d,%d)通往(%d,%d)",map->start.x,map->start.y,map->end.x,map->end.y);
}else{
printf("从起始点(%d,%d)通往(%d,%d)的路径为:\n",map->start.x,map->start.y,map->end.x,map->end.y);
printMap(map);
printRoute(routeStack);//倒序输出
}
}//这里注释掉的语句都是调试用的。




本题的地图是随机生成的,你可以在initMap里随意更改地图的大小,但相应的printMap函数也要求更改。

如果把注释掉的第140行代码取消掉,你能清晰的看到结点遍历的过程。

2)第二种方法:用队列实现:

解题思路:队列的解决方案与栈的解决方案异曲同工。从入口开始,依序(右下左上)四个方向遍历,如果下个结点是可通过的,则加入队列。当前结点的四个方向的结点在同一层。因此,当从队列的开头顺序查看队列的结点时,只要是到达了出口,此路径肯定是最短的。因为后面的即使还有到达出口的路径但层级数肯定大于第一个出现的路径。因此这种方法求出的是最小短路径。

//用队列找到最短路径的问题

#define MapSize (10+2)
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
struct PositionNode{
int x;
int y;
};
struct MapNode{
char mapStatus[MapSize][MapSize];//存迷宫指定位置的状态
struct PositionNode start;//起始位置
struct PositionNode end;//结束位置
};
struct BoxNode{
int row;
int column;
int pre;//指向前一个元素的下标
};//队列中的元素类型
struct QueueNode{
struct BoxNode *BoxBlock;
int Front;
int Rear;
};
typedef struct PositionNode Position;
typedef struct MapNode *Map;
typedef struct QueueNode *Queue;
void initMap(Map map){
//srand(time(NULL));
int i,j;int x,y;
for(i=0;i<12;i++){
for(j=0;j<12;j++){
map->mapStatus[i][j]='#';
}
}
for(i=0;i<80;i++){
x=rand()%10+1;
y=rand()%10+1;
map->mapStatus[x][y]='-';
}
map->mapStatus[1][1]='M';
map->mapStatus[10][10]='C';
map->start.x=1;map->start.y=1;
map->end.x=10;map->end.y=10;
}
void printMap(Map map){
int i;int j;
for(i=0;i<12;i++){
for(j=0;j<12;j++){
if(map->mapStatus[i][j]=='#'){
printf("%c%c",161,246);//白的当作墙
}else if(map->mapStatus[i][j]=='-'){
printf("%c%c",161,245);//黑的当作有效的通道
}else if(map->mapStatus[i][j]=='*'){
printf("%c%c",161,242);//圆的代表路径
}else{
printf(" %c",map->mapStatus[i][j]);//其余的代表进出口
}
}
printf("\n");
}
}
void initQueue(Queue queue){
queue->Front=-1;
queue->Rear=-1;
queue->BoxBlock=malloc(sizeof(struct BoxNode)*100);
}
Queue FindRoute(Map map){
int Find=0;//表示没有找到最短路径
int row,column;//从队列的开头开始处理的结点的坐标
int direction;//表示当前结点的方向(四个值)
Queue queue=malloc(sizeof(struct QueueNode));
initQueue(queue);
queue->Rear++;
queue->BoxBlock[queue->Rear].row=map->start.x;
queue->BoxBlock[queue->Rear].column=map->start.y;
queue->BoxBlock[queue->Rear].pre=-1;
while(queue->Front<=queue->Rear&&!Find){
queue->Front++;
row=queue->BoxBlock[queue->Front].row;
column=queue->BoxBlock[queue->Front].column;
//printf("当前处理的结点是:<%d,%d>\n",row,column);
if(row==map->end.x&&column==map->end.y){
Find=1;//表示此结点是出口。路径已经找到
return queue;
}
for(direction=1;direction<5;direction++){
switch(direction){
case 1:
row=queue->BoxBlock[queue->Front].row;
column=queue->BoxBlock[queue->Front].column+1;
break;
case 2:
row=queue->BoxBlock[queue->Front].row+1;
column=queue->BoxBlock[queue->Front].column;
break;
case 3:
row=queue->BoxBlock[queue->Front].row;
column=queue->BoxBlock[queue->Front].column-1;
break;
case 4:
row=queue->BoxBlock[queue->Front].row-1;
column=queue->BoxBlock[queue->Front].column;
break;
default:printf("error!\n");
}
if(map->mapStatus[row][column]=='-'||map->mapStatus[row][column]=='C'){
queue->Rear++;
//printf("结点<%d,%d>加入队列!\n",row,column);
//printMap(map);
queue->BoxBlock[queue->Rear].row=row;
queue->BoxBlock[queue->Rear].column=column;
queue->BoxBlock[queue->Rear].pre=queue->Front;
if(map->mapStatus[row][column]!='C'){
map->mapStatus[row][column]='@';//表明此结点已经访问过。
}
}
}
}
return NULL;
}
void printRoute(Queue queue,Map map){
int k=queue->Front;
int row,column;
k=queue->BoxBlock[k].pre;
while(k>0){
row=queue->BoxBlock[k].row;
column=queue->BoxBlock[k].column;
map->mapStatus[row][column]='*';//从Front开始,将结点的途径之地的map对应值设为'*'。
k=queue->BoxBlock[k].pre;//指向Front位置元素的前一个
}
}
int main(){
Map map=malloc(sizeof(struct MapNode));
initMap(map);
printf("原始地图为:\n");
printMap(map);
Queue queue=FindRoute(map);
if(queue!=NULL){
printRoute(queue,map);
printf("最短路径为:\n");
printMap(map);
}else{
printf("没有路径从起点<%d,%d>通向<%d,%d>,队列遍历的结点有:\n",map->start.x,map->start.y,map->end.x,map->end.y);
printMap(map);
}
}

//这里注释掉的语句都是调试用的。




将第30行代码取消注释,将随机产生地图。将110行和111行取消注释,你将看到详细的结点遍历过程。

其实,队列也可以求解所有路径,还可以用四叉树求解所有路径,在此先不做探讨。

后续会专门写一篇解决迷宫问题的文档,包括迷宫生成算法,和找出迷宫的所有路径和最短路径问题,结合openGL动态绘制结点的遍历过程。类似下面的链接那种:

http://www.2cto.com/kf/201411/349889.html(结点的动态遍历)

http://bbs.9ria.com/thread-156150-1-1.html(三种迷宫生成算法)

http://wenku.baidu.com/link?url=AgRCE5ddoGdA-rW1nyyYD2Qhny5xPEjAlH_DkFgIB2wBFvyu0HijO7774gp-eP_bRa2OlrENBlZbE7zaFCjS_Ami2L7JGF8BUE5jXjr0pu3(迷宫问题链表实现的详解)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: