相信绝大多数都学习过《数据结构》这门课程,而对这门课程里最熟悉的应该是堆栈和队列。本人前段时间买了一本有关算法(C语言实现)的书,发现其中对循环队列这一结构的算法存在漏洞,故写此文与大家交流探讨。
首先提一下循环队列的概念:队头指针head,队尾指针tail。
很显然,当队列满后,即便全部元素都出队,队列还是满的状态。这种情况就叫做“假溢出”,即数组中明明有可用空间,但却无法使用。
这是由定长数组的特性决定的。但我们可用改变一下思路,当队尾指针指向数组最后一个位置时,如果再有数据入队,并且队头指针没有指向数组的第一个元素,那么就让队为指针绕回到数组头部。这样就形成了一个逻辑上的环
即(循环队列)。
注:以上对循环队列的介绍系转载一高人博客。
书中所述对循环队列的代码如下:
#define MAXSIZE 10
typedef struct _QUEUE
{
int elems [ MAXSIZE ] ;
int front,rear;
}QUEUE;
int Initialize(QUEUE *queue)/*初始化队列*/
{
queue->front=queue->rear=0;
}
int InQueue(QUEUE *queue,int x)/*元素‘x’入队函数*/
{
if (((queue->rear+1) % MAXSIZE)==queue->front)
{
printf("Queue is overflow!\n");
return 0;
}
queue->rear=(queue->rear+1)% MAXSIZE;
queue->elems [ queue->rear ] =x;
}
int OutQueue(QUEUE *queue)/*出队函数*/
{
if (queue->rear==queue->front)
{
printf("Queue is Empty!\n");
return 0;
}
queue->front=(queue->front+1)% MAXSIZE;
}
int Show(QUEUE *queue)/*显示整个队列*/
{
int i=(queue->front+1)%MAXSIZE;
if (queue->front==queue->rear)
{
printf("Queue is Empty!\n");
return 0;
}
for(i=((queue->front+1)%MAXSIZE);i<=queue->rear;i=((i+1)%MAXSIZE))
{
printf("queue->elems [ %d ] =%d\n ",i,queue->elems [ i ] );
}
printf("\nQueue head is %d\n",queue->front);
printf("Queue rear is %d\n",queue->rear);
}
所有问题就出在 Show(QUEUE *queue) 这个函数上,我们做第一个测试代码:
main()
{
/*Initialize a queue and show it.*/
QUEUE queue;
Initialize(&queue);
Show(&queue);
/*Add elems int a queue and show it.*/
InQueue(&queue,1);
InQueue(&queue,2);
InQueue(&queue,3);
InQueue(&queue,4);
InQueue(&queue,5);
InQueue(&queue,6);
InQueue(&queue,7);
InQueue(&queue,8);
InQueue(&queue,9);
Show(&queue);
}
由于队列中始终要有一个空元素,根据此段代码定义 #define MAXSIZE 10
,所以实际队列最多存在9个元素。但我们运行程序以后会发现程序陷入死循环,即循环打印整个队列,截图如下:
经分析出现死循环的原因很简单,在 Show(QUEUE *queue) 里的循环条件:
for(i=((queue->front+1)%MAXSIZE);i<=queue->rear;i=((i+1)%MAXSIZE))
,在上述测试代码下,9个元素入队后,
queue->front=0,queue->rear=9,FOR语句在此条件下即为:for(i=(0+1)%10;i<=9;i=((i+1)%MAXSIZE))
,当i=9,
执行 printf("queue->elems [ %d ] =%d\n ",9,queue->elems [ 9 ] );
之后,i=(9+1)%10,即 i 又变为 0 ,又重新符合 i<=9 的循环条件,如此反复,陷入死循环。
该程序还有个漏洞。我们来做第二个测试代码:
main()
{
/*Initialize a queue and show it.*/
QUEUE queue;
Initialize(&queue);
/*Add elems int a queue and show it.*/
InQueue(&queue,1);
InQueue(&queue,2);
InQueue(&queue,3);
InQueue(&queue,4);
InQueue(&queue,5);
InQueue(&queue,6);
InQueue(&queue,7);
InQueue(&queue,8);
InQueue(&queue,9);
OutQueue(&queue);
OutQueue(&queue);
OutQueue(&queue);
OutQueue(&queue);
OutQueue(&queue);
InQueue(&queue,11);
InQueue(&queue,10);
Show(&queue);
}
这段代码运行后,发现它根本没有显示整个队列。运行结果截图如下:
分析原因:在上述测试代码下,先是9个元素入队,5个元素出队,又2个元素入队,即 queue->front=5 , queue->rear=1
。在此条件下,for(i=((queue->front+1)%MAXSIZE);i<=queue->rear;i=((i+1)%MAXSIZE))
就变成
for(i=(5+1)%10;i<=1;i=((i+1)%MAXSIZE)) ,从一开始就不符合 i<=1 的条件 ,故循环体没有执行。
故,Show(QUEUE *queue)
函数代码有严重漏洞。发现此问题后,本想偷懒,写封邮件给作者,希望他能修改代码,可能是作者又忙着出别的什么书了,没功夫陪我们“小孩”玩。“被逼无奈”之下,自己重写Show(QUEUE
*queue) 函数,只要能摆平以上两个Bug就成。
经分析发现,若我们将循环队列看成一个首尾相连的火车轨道,无论是入队还是出队, queue->front 和 queue->rear
都是顺时针方向运动的,故新的 Show 函数如下:
int Show(QUEUE *queue)
{
int i=(queue->front+1)%MAXSIZE;
if (queue->front==queue->rear)
{
printf("Queue is Empty!\n");
return 0;
}
while(i!=queue->rear)
{
printf("queue->elems [ %d ] =%d\n",i,queue->elems [ i ] );
i=(i+1)%MAXSIZE;
}
if(i==queue->rear)/*如果不加这句,此程序将有一个Bug,即队尾元素(最后一个元素)打印不出来*/
{
printf("queue->elems [ %d ] =%d\n",i,queue->elems [ i ] );
}
printf("\nQueue head is %d\n",queue->front);
printf("Queue rear is %d\n",queue->rear);
}
经初步测试,上文所述的两个BUG不会出现。但感觉代码写的有点窝囊,由于本人水平与学识的限制,不排除会被大本营的高人找出Bug,希望大家能写出更好的代码,供大家交流学习。
转自:http://student.csdn.net/space.php?uid=50992&do=blog&id=20368
分享到:
相关推荐
循环队列源代码 循环队列是一种数据结构,它通过一个数组或链表来存储元素,并提供了队列操作的接口。队列是一种先进先出的数据结构,元素的添加和删除都是从队头和队尾进行的。在本资源中,我们将探讨循环队列的...
循环队列是一种线性数据结构,它在计算机科学中被广泛应用于数据缓存、消息队列等场景。相比于传统的队列,循环队列利用数组的循环特性,避免了队列满或空时需要重新分配内存的问题,提高了空间利用率和操作效率。在...
循环队列是一种线性数据结构,它通过在队尾出队和队头入队时进行适当的操作,使得队列在物理存储空间上形成一个环形结构。这种设计使得队列在处理大量数据时能有效避免数组下标越界的问题,提高了空间利用率。在...
循环队列作为一种高效的数据结构,在串口通信的缓冲区管理中扮演着重要角色。这里我们将深入探讨串口缓冲区的概念、循环队列的工作原理以及如何在STM32F103ZET6微控制器上应用这些技术。 串口缓冲区是用于存储串口...
书中可能包含了各种操作循环队列的示例代码,通过分析和实践这些代码,可以加深对循环队列的理解,提升编程技能。 总之,循环队列是数据结构中不可或缺的一部分,它在解决实际问题时发挥着重要作用。严蔚敏教授的...
循环队列是一种线性数据结构,它在物理结构上实现了一个首尾相接的闭合序列,从而解决了普通队列在满和空时的操作限制。循环队列的主要优点是消除了队头和队尾的特殊状态,使得在处理数据时效率更高。下面将详细介绍...
循环队列是一种线性数据结构,它在物理结构上实现了一个闭合的循环,因此队首和队尾可以在数组的任意位置。这种设计使得在队列满或空时,仍能有效地进行入队和出队操作,避免了普通队列在达到边界条件时的特殊处理。...
循环队列是一种特殊的队列,它在队列的两端都可以进行插入和删除操作,通过对循环队列的基本操作和应用,我们可以更好地理解和掌握数据结构的概念。 循环队列的基本操作 循环队列的基本操作包括构造、清空、销毁、...
循环队列是一种常见的线性数据结构,它具有先进先出(FIFO)的特性,即最早进入队列的元素最先离开。这个特性使得循环队列在处理一系列有序任务或者缓存管理等方面有着广泛的应用。本文将详细探讨Android中如何实现...
本项目涉及的关键知识点是“循环队列”、“485收发”以及“协议制作”,这些对于理解如何高效地实现串口数据传输至关重要。 首先,我们来了解一下**循环队列**。循环队列是一种线性数据结构,它利用数组的循环特性...
### C语言实现仅有尾指针的循环队列 在数据结构的学习过程中,循环队列是一种重要的数据结构之一,尤其在解决计算机系统中的缓冲问题时应用广泛。本篇将基于题目提供的部分代码,深入探讨如何使用C语言实现一个仅有...
循环队列和约瑟夫环问题 循环队列是一种特殊的队列结构,它的特点是队列的末尾元素连接着队列的开头元素,形成一个环形结构。这种数据结构可以用来解决约瑟夫环问题。 约瑟夫环问题是一个经典的问题,它是由古罗马...
在这个场景中,我们关注的是使用SCL(Structured Control Language)编程语言实现的一种特定算法——循环队列FIFO(First In First Out)算法,并且这个算法被封装在了一个FB(Function Block)库文件中。...
循环队列是数据结构中的一种线性数据结构,它的特点是队尾指针可以在数组范围内的任何位置循环前进,从而避免了普通队列在队列满时需要重新申请空间的问题。循环队列的设计使得其在存储空间上的利用率较高,且在处理...
在计算机科学中,多线程和循环队列是两个重要的概念,它们在高效并发编程中发挥着关键作用。本文将详细探讨多线程环境下的循环队列应用。 首先,我们来理解多线程。多线程是一种编程模型,允许一个程序同时执行多个...
### 数据结构实验三:循环队列基本操作及约瑟夫环问题 #### 实验背景与目标 本实验旨在通过实际编程任务加深学生对于循环队列的理解及其在解决具体问题中的应用能力。实验主要包括两个部分: 1. **循环队列基本...
在Java中,队列的实现主要有三种:顺序队列、链式队列和循环队列。下面我们将详细探讨这三种队列的实现方式。 1. **顺序队列**: 顺序队列通常是基于数组实现的。在Java中,我们可以使用ArrayList或LinkedList来...
循环队列是串口通信中数据缓冲区管理的一种高效实现,它在处理实时性要求较高的数据流时尤为有用。下面我们将详细探讨“array_串口队列_循环队列_”这一主题。 首先,我们来看一下“串口队列”。串口队列是串行通信...