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

    介绍队列的相关知识:

    (3)队列ADT:

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

    3.1队列模型

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

    3.2队列的数组实现

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

      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)队列的数组实现的实例:

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

  1 //队列的应用实例 
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<time.h>
  5 #define MinQueueSize (5)
  6 
  7 struct QueueRecord;
  8 typedef int ElementType; 
  9 typedef struct QueueRecord *Queue;
 10 
 11 int IsEmpty(Queue Q);
 12 int IsFull(Queue Q);
 13 Queue CreateQueue(int MaxElements);
 14 void DisposeQueue(Queue Q);
 15 void MakeEmpty(Queue Q);
 16 void Enqueue(ElementType X,Queue Q);
 17 ElementType Front(Queue Q);
 18 void Dequeue(Queue Q);
 19 ElementType FrontAndDequeue(Queue Q);
 20 struct QueueRecord{
 21   int Capacity;//队列包含元素的最大值
 22   int Front;
 23   int Rear;
 24   int Size;//现有队列的大小
 25   ElementType *Array;    
 26 };  
 27 
 28 int *RandInt4(int total,int i,int j,int order){
 29     int *numberGroup=malloc(total*sizeof(int));//用于返回一个指定大小的数组 
 30     int tempory[j-i+1];//辅助产生随机数的数组。 
 31     int k;//用于循环遍历 
 32     int x;//接收产生的随机变量 
 33     srand(time(NULL));
 34     //初始化辅助数组 
 35     for(k=0;k<=j-i;k++){
 36         tempory[k]=0;
 37     }
 38     for(k=0;k<total;k++){
 39 L:        x=rand()%(j-i+1)+i;//产生从i到j,包括i和j的随机数
 40         if(tempory[x-i]==0){
 41           tempory[x-i]=1;
 42           //当需要产生的数组是无序的则执行: 
 43           if(order==0){
 44             numberGroup[k]=x;    
 45             }
 46         }else{
 47             goto L;
 48         } 
 49     }
 50     //当需要产生有序的随机数组时执行: 
 51     int w=0;
 52     if(order!=0){
 53       for(k=0;k<j-i+1;k++){
 54           if(tempory[k]==1){
 55               numberGroup[w++]=k+i;
 56               if(w>=total){
 57                   break;
 58               }
 59           }
 60       }    
 61     }
 62     
 63     return numberGroup; 
 64 }
 65 Queue CreateQueue(int MaxElements){
 66     if(MaxElements<MinQueueSize){
 67         printf("定义的队列的大小小于5");
 68         MaxElements=MinQueueSize; 
 69     }
 70     Queue Q=malloc(sizeof(struct QueueRecord));
 71     Q->Capacity=MaxElements;
 72     Q->Array=malloc(sizeof(int)*MaxElements);
 73     MakeEmpty(Q);
 74     return Q;
 75 }
 76 void MakeEmpty(Queue Q){
 77     Q->Front=0;//在数组的末尾,下标为MaxElements-1 
 78     Q->Rear=Q->Capacity-1;//在数组的开头,下标为0 
 79     Q->Size=0;//
 80 }
 81 //判断是否Front和Rear是否挨着,只有挨着队列才有可能为空或者满,当挨着时也有可能是两个元素。但此时size=2; 
 82 int IsFRClosed(Queue Q){
 83     int size=(Q->Rear-Q->Front+1)%Q->Capacity;
 84     if(size<0){
 85         size+=Q->Capacity;
 86     }
 87     if(size>Q->Capacity){
 88         size-=Q->Capacity;
 89     }
 90     //当size==0时两种情况,一种是队列为空,一种是队列满了。 
 91     return size==0;
 92 }
 93 int IsEmpty(Queue Q){
 94     if(IsFRClosed(Q))//这里这样做,只是更加不容易出错!
 95           return Q->Size==0;
 96     else
 97         return 0;
 98 }
 99 int IsFull(Queue Q){
100     if(IsFRClosed(Q)){  
101           return Q->Size==Q->Capacity;
102     }else
103         return 0;
104 }
105 void Enqueue(int element,Queue Q){
106     if(!IsFull(Q)){
107         Q->Size++;
108         Q->Rear+=1;
109         if(Q->Rear==Q->Capacity){
110             Q->Rear=0;
111         }
112         Q->Array[Q->Rear]=element; 
113     }else{
114         printf("  :无法将%2d插入队列,队列已满!
",element);
115     }
116 } 
117 void Dequeue(Queue Q){
118     if(!IsEmpty(Q)){
119         Q->Size--;
120         Q->Front++;
121         if(Q->Front==Q->Capacity    ){
122             Q->Front=0;
123         } 
124     }
125 } 
126 int FrontAndDequeue(Queue Q){
127     int tempFront=Q->Front;
128     //printf("Front=%4d
",Q->Front);
129     Dequeue(Q);
130     return Q->Array[tempFront]; 
131 }
132 int Front(Queue Q){
133     return Q->Array[Q->Front];
134 }
135 int main(){
136     int *tempGroup=RandInt4(40,0,100,0);
137     int *tempGroup2=RandInt4(30,0,100,0);
138     int i;
139     Queue Q=CreateQueue(50);
140     printf("1.入队40个元素!
");
141     for(i=0;i<40;i++){
142       printf("%4d",tempGroup[i]);
143       Enqueue(tempGroup[i],Q);
144       if((i+1)%20==0){
145           printf("
");
146         }    
147     }
148     printf("2.出队10个元素!
");
149     for(i=0;i<10;i++){
150         printf("%4d",Front(Q));
151         Dequeue(Q);
152         if((i+1)%10==0){
153             printf("
");
154         }
155     }
156     printf("3.再入队30个元素!
");
157     for(i=0;i<30;i++){
158       printf("%4d",tempGroup[i]);
159       Enqueue(tempGroup2[i],Q);
160       if((i+1)%20==0){
161           printf("
");
162         }    
163     }
164     printf("4.显示此队列中的所有出队结果!
"); 
165     int size=Q->Size; 
166     for(i=0;i<size;i++){
167           printf("%4d",FrontAndDequeue(Q));
168         if((i+1)%20==0){
169             printf("
");
170           }    
171     }
172     printf("
5.Front和Rear的位置是:
");
173     printf("Front=%d,Rear=%d",Q->Front,Q->Rear); 
174 }

    3.3队列的应用:

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

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

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

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

  

    3.4排队论

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

    3.5队列的应用实例:

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

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

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

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

  1 //迷宫问题的栈的解法 
  2 #include<stdio.h>
  3 #include<stdlib.h>
  4 #include<time.h>
  5 //在栈中存地图上区块的位置信息
  6 #define mapSize (10) 
  7 struct BlockNode{
  8     int row;
  9     int column;
 10     int direction;//用于指示下一个区块相对于当前区块的位置,依序右下左上,值为1234. 
 11     struct BlockNode *Next;
 12 };//用来构成栈的元素结点 
 13 struct PositionNode{
 14   int x;
 15   int y;    
 16 };
 17 struct MazNode{
 18     char mapStatus[mapSize][mapSize];//存迷宫指定位置的状态 
 19     struct PositionNode start;//起始位置 
 20     struct PositionNode end;//结束位置 
 21 };
 22 typedef struct MazNode *Map; 
 23 typedef struct BlockNode *BlockStack;
 24 typedef struct BlockNode *Block;
 25 typedef struct PositionNode *Position;
 26 void initMap(Map map){
 27   srand(time(NULL));
 28   int i,j;int x,y;
 29   for(i=0;i<10;i++){
 30     for(j=0;j<10;j++){
 31         map->mapStatus[i][j]='#';
 32     }
 33   }//建立围墙
 34   for(i=0;i<50;i++){
 35       x=rand()%8+1;
 36       y=rand()%8+1;
 37       map->mapStatus[x][y]='-';
 38   }
 39   map->start.x=1;map->start.y=1;
 40   map->end.x=8;map->end.y=8;
 41   map->mapStatus[1][1]='m';//mouse代表入口 
 42   map->mapStatus[8][8]='c';//cat代表出口 
 43 }
 44 void printMap(Map map){
 45   int i,j;
 46   for(i=0;i<10;i++){
 47       for(j=0;j<10;j++){
 48       printf("%c",map->mapStatus[i][j]);       
 49     }
 50     printf("
");
 51   }    
 52 } 
 53 BlockStack createStack(){
 54     BlockStack L=malloc(sizeof(struct BlockNode));
 55     L->Next=NULL;
 56     return L;
 57 }
 58 Block Top(BlockStack stack){
 59     return stack->Next;
 60 }
 61 void Pop(BlockStack    stack){
 62     stack->Next=stack->Next->Next;
 63 }
 64 void Push(Block    temp,BlockStack    stack){
 65     temp->Next=stack->Next;
 66     stack->Next=temp;
 67 }
 68 int IsEmpty(BlockStack stack){
 69   return stack->Next==NULL;    
 70 }
 71 Position FindNextPosition(Block tempBlock){
 72     Position p=malloc(sizeof(struct PositionNode));
 73     switch(tempBlock->direction)
 74     {
 75         case 1://
 76                p->x=tempBlock->row;
 77                p->y=tempBlock->column+1;
 78                break;
 79         case 2://
 80                 p->x=tempBlock->row+1;
 81                p->y=tempBlock->column;
 82                break;
 83         case 3://
 84                p->x=tempBlock->row;
 85                p->y=tempBlock->column-1;
 86                break;
 87         case 4://
 88                p->x=tempBlock->row-1;
 89                p->y=tempBlock->column;
 90                break; 
 91         default:printf("error!
");
 92     }
 93     return p;
 94 }
 95 int Pass(Position p,Map map){
 96     //printf("地图中的是%c
",map->mapStatus[p->x][p->y]);
 97     if(map->mapStatus[p->x][p->y]=='-'||map->mapStatus[p->x][p->y]=='c')
 98         return 1; 
 99     else
100         return 0;
101 } 
102 void makeFoot(Position p,Map map){
103     map->mapStatus[p->x][p->y]='*';
104 }
105 void printRoute(BlockStack stack){
106   Block temp;
107   while(stack->Next!=NULL){
108       temp=Top(stack);
109     printf("<%d,%d>",temp->row,temp->column);
110     Pop(stack);
111   }
112 }
113 void printStack(BlockStack    stack){
114     BlockStack ptr=stack->Next;
115     while(ptr!=NULL){
116           printf("<%d,%d>",ptr->row,ptr->column);
117           ptr=ptr->Next;    
118     }
119     printf("
");
120 }
121 BlockStack FindRoute(Map map){
122     BlockStack stack=createStack();
123     Block currentBlock;
124     Block temp=malloc(sizeof(struct BlockNode));
125     temp->row=map->start.x;
126     temp->column=map->start.y;
127     temp->direction=1;
128     Push(temp,stack);
129     //printStack(stack); 
130     //当栈不为空时,取栈顶元素为当前元素 
131     while(!IsEmpty(stack)){
132         currentBlock=Top(stack);
133         //当当前结点的四个方向的结点没有视察完时,逐个视察 
134         while(currentBlock->direction<=4){
135           Position nextAvaliable=FindNextPosition(currentBlock);
136           //printf("结点<%d,%d>的下%d个有效的位置是<%d,%d>
",currentBlock->row,currentBlock->column,currentBlock->direction,nextAvaliable->x,nextAvaliable->y);
137           //printf("%c",map->mapStatus[nextAvaliable->x][nextAvaliable->y]);
138           if(Pass(nextAvaliable,map)){
139               makeFoot(nextAvaliable,map);
140               //printMap(map);printf("
");
141             temp=malloc(sizeof(struct BlockNode));
142             temp->row=nextAvaliable->x;
143             temp->column=nextAvaliable->y;
144             temp->direction=1;
145             Push(temp,stack);
146             //printStack(stack);
147             currentBlock=temp;
148             //如果当前结点是末尾结点 
149             if(currentBlock->row==map->end.x&&currentBlock->column==map->end.y){
150                 return stack;
151             }            
152           }else{
153             currentBlock->direction++; 
154           }    
155         }
156         Block ord=Top(stack);
157         map->mapStatus[ord->row][ord->column]='@';//将已经访问过的点并且从栈中弹出的设为“@”。
158         Pop(stack);
159         //printStack(stack);
160     }
161     return NULL;
162 }
163 
164 int main(){
165     Map map=malloc(sizeof(struct MazNode));     
166     initMap(map);//初始化迷宫地图 
167     printMap(map);
168     printf("
");
169     BlockStack routeStack=FindRoute(map);//start为起始点,end为终点
170     if(routeStack==NULL){
171         printf("没有路径从起始点(%d,%d)通往(%d,%d)",map->start.x,map->start.y,map->end.x,map->end.y);
172     }else{
173         printf("从起始点(%d,%d)通往(%d,%d)的路径为:
",map->start.x,map->start.y,map->end.x,map->end.y);
174         printMap(map);
175         printRoute(routeStack);//倒序输出
176     } 
177 }//这里注释掉的语句都是调试用的。

 

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

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

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

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

  1 //用队列找到最短路径的问题
  2  
  3 #define MapSize (10+2)
  4 #include<stdio.h>
  5 #include<stdlib.h>
  6 #include<time.h> 
  7 struct PositionNode{
  8   int x;
  9   int y;    
 10 };
 11 struct MapNode{
 12     char mapStatus[MapSize][MapSize];//存迷宫指定位置的状态 
 13     struct PositionNode start;//起始位置 
 14     struct PositionNode end;//结束位置 
 15 };
 16 struct BoxNode{
 17   int row; 
 18   int column;
 19   int pre;//指向前一个元素的下标    
 20 };//队列中的元素类型 
 21 struct QueueNode{
 22   struct BoxNode *BoxBlock; 
 23   int Front;
 24   int Rear;    
 25 };
 26 typedef struct PositionNode Position;
 27 typedef struct MapNode *Map;
 28 typedef struct QueueNode *Queue;
 29 void initMap(Map map){
 30   //srand(time(NULL));
 31   int i,j;int x,y;
 32   for(i=0;i<12;i++){
 33     for(j=0;j<12;j++){
 34         map->mapStatus[i][j]='#';
 35     }
 36   }
 37   for(i=0;i<80;i++){
 38     x=rand()%10+1;
 39     y=rand()%10+1;    
 40       map->mapStatus[x][y]='-';
 41   }
 42   map->mapStatus[1][1]='M';
 43   map->mapStatus[10][10]='C';
 44   map->start.x=1;map->start.y=1;
 45   map->end.x=10;map->end.y=10;
 46 }
 47 void printMap(Map map){
 48     int i;int j;
 49     for(i=0;i<12;i++){
 50         for(j=0;j<12;j++){
 51             if(map->mapStatus[i][j]=='#'){
 52                 printf("%c%c",161,246);//白的当作墙                 
 53             }else if(map->mapStatus[i][j]=='-'){
 54                 printf("%c%c",161,245);//黑的当作有效的通道 
 55             }else if(map->mapStatus[i][j]=='*'){
 56                 printf("%c%c",161,242);//圆的代表路径     
 57             }else{
 58                 printf(" %c",map->mapStatus[i][j]);//其余的代表进出口 
 59             }
 60         }
 61         printf("
");
 62     }
 63 } 
 64 void initQueue(Queue queue){
 65   queue->Front=-1;
 66   queue->Rear=-1;
 67   queue->BoxBlock=malloc(sizeof(struct BoxNode)*100);
 68 }
 69 Queue FindRoute(Map map){
 70   int Find=0;//表示没有找到最短路径
 71   int row,column;//从队列的开头开始处理的结点的坐标
 72   int direction;//表示当前结点的方向(四个值) 
 73   Queue queue=malloc(sizeof(struct QueueNode));
 74   initQueue(queue);
 75   queue->Rear++;
 76   queue->BoxBlock[queue->Rear].row=map->start.x;
 77   queue->BoxBlock[queue->Rear].column=map->start.y;
 78   queue->BoxBlock[queue->Rear].pre=-1;
 79   while(queue->Front<=queue->Rear&&!Find){
 80     queue->Front++;
 81     row=queue->BoxBlock[queue->Front].row;
 82     column=queue->BoxBlock[queue->Front].column;
 83     //printf("当前处理的结点是:<%d,%d>
",row,column);
 84     if(row==map->end.x&&column==map->end.y){
 85         Find=1;//表示此结点是出口。路径已经找到
 86         return queue; 
 87     }
 88     for(direction=1;direction<5;direction++){
 89         switch(direction){
 90             case 1:
 91                    row=queue->BoxBlock[queue->Front].row;
 92                    column=queue->BoxBlock[queue->Front].column+1;
 93                    break;
 94             case 2:
 95                    row=queue->BoxBlock[queue->Front].row+1;
 96                    column=queue->BoxBlock[queue->Front].column;
 97                    break;
 98             case 3:
 99                    row=queue->BoxBlock[queue->Front].row;
100                    column=queue->BoxBlock[queue->Front].column-1;
101                    break;
102             case 4:
103                    row=queue->BoxBlock[queue->Front].row-1;
104                    column=queue->BoxBlock[queue->Front].column;
105                    break;
106                default:printf("error!
");
107         }
108         if(map->mapStatus[row][column]=='-'||map->mapStatus[row][column]=='C'){
109             queue->Rear++;
110             //printf("结点<%d,%d>加入队列!
",row,column);
111             //printMap(map);
112             queue->BoxBlock[queue->Rear].row=row;
113             queue->BoxBlock[queue->Rear].column=column;
114             queue->BoxBlock[queue->Rear].pre=queue->Front;
115                  if(map->mapStatus[row][column]!='C'){
116                 map->mapStatus[row][column]='@';//表明此结点已经访问过。
117                 }    
118         }
119     }    
120   }
121  return NULL;    
122 }
123 void printRoute(Queue queue,Map map){
124     int k=queue->Front;
125     int row,column;
126     k=queue->BoxBlock[k].pre;
127     while(k>0){
128       row=queue->BoxBlock[k].row;
129       column=queue->BoxBlock[k].column;
130       map->mapStatus[row][column]='*';//从Front开始,将结点的途径之地的map对应值设为'*'。 
131       k=queue->BoxBlock[k].pre;//指向Front位置元素的前一个
132     }
133 }
134 int main(){
135   Map map=malloc(sizeof(struct MapNode));
136   initMap(map);
137   printf("原始地图为:
"); 
138   printMap(map);
139   Queue queue=FindRoute(map);
140   if(queue!=NULL){
141         printRoute(queue,map);  
142         printf("最短路径为:
");
143       printMap(map);
144   }else{
145         printf("没有路径从起点<%d,%d>通向<%d,%d>,队列遍历的结点有:
",map->start.x,map->start.y,map->end.x,map->end.y);
146         printMap(map);
147   }    
148 }
//这里注释掉的语句都是调试用的。

      将第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(迷宫问题链表实现的详解)

原文地址:https://www.cnblogs.com/MenAngel/p/5566262.html