队列简介
队列是只允许在一端进行插入操作,而在另一端进行删除操作的线性表
允许进行存入操作的一端称为队尾
允许进行删除操作的一端称为队头
当线性表中没有元素时,就称为空队
队列的特点是先进先出,FIFO
队列示意图如下,
由于队列也是线性表的一种,所以也分为顺序队列和链式队列。
顺序队列
顺序队列在C语言下编写的结构代码如下,
typedef int datatype;
#define MAXSIZE 128;
typedef struct
{
datatype data[MAXSIZE];/*队列数据*/
int front;/*队列的起始*/
int rear;/*队列的结尾*/
}sequeue;
顺序队列相较于顺序表来说,多了两个参数front和rear
front指向队头元素的位置
rear指向队尾元素的下一个位置
在队列操作过程中,为了提高效率,以调整指针代替队列元素的移动,并将数组作为循环队列的操作空间
为了区分空队和满队,满队元素个数比数组元素个数少一
以下是元素入队的示意图,
以下是元素出队的示意图,
从这个出队示意图我们发现,如果说再出队一个元素J3的话,那么front指针就和 rear指针重合,也就是相同时,就代表顺序队列是空的。
看下图的情况,此时如果在入队元素的话,rear指针继续++那么就超出了数据的范围,这样肯定不合适,所以实际情况是我们需要在入队时让指针指向最下面的0位置以便后续的元素可以继续入队,这种循环队列的实现,我们选择进行的操作就是进行取余操作,也就是rear = rear%(N+1)
我们继续设想,假如在上述图的情况下,继续入队3个元素的话,那么rear指针就和front指针重合,此时的情况和上述顺序队列为空的情况一样,为了区分,所以有了满队元素个数比数组元素个数少一这个规定。
下面我们将编写一些顺序表相关的函数,在编写时我们一般会创建三个文件,分别是sqlist.h,sqlist.c,test.c,其中sqlist.h用于编写一些数据结构的定义和函数结构,sqlist.c用于编写函数具体实现,test.c用于测试每个函数是否正确。
在sqlist.h文件中,编写的程序如下,
typedef int datatype;
#define MAXSIZE 128;
typedef struct
{
datatype data[MAXSIZE];/*队列数据*/
int front;/*队列的起始*/
int rear;/*队列的结尾*/
}sequeue;
sequeue *sequeue_create();
int ensequeue(sequeue* sq,datatype value);
datatype desequeue(sequeue *sq);
int sequeue_empty(sequeue *sq);
int sequeue_full(sequeue *sq);
int sequeue_clear(sequeue *sq);
sequeue *sequeue_free(sequeue *sq);
在sqlist.h文件中,我们编写相关函数的具体实现,以下是每个操作API函数的实现,
创建顺序队列
/*创建顺序队列*/
sequeue *sequeue_create();
{
sequeue *sq;
/*申请内存*/
if(sq = (sequeue *)malloc(sizeof(sequeue)) == NULL)
{
printf("malloc sequeue fail\n");
return NULL
}
/*将数据用0填充*/
memset(sq->data,0,sizeof(sq->data));
sq->front = 0;
sq->rear = 0;
return sq;
}
入队函数
/*入队*/
int ensequeue(sequeue* sq,datatype value);
{
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
/*判断队列是否满,满的话就无法入队,通过尾指针rear实现,取余的原因可以看上面的解释*/
if((sq->rear+1) % MAXSIZE == sq->front )
{
printf("sequeue is full\n");
return -1;
}
sq->data[sq->rear] = value;
sq->rear = (sq->rear+1) % MAXSIZE;
return 0;
}
出队函数
/*出队函数*/
datatype desequeue(sequeue *sq)
{
datatype value;
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
/*判断队列是否非空*/
if(sq->front == sq->rear)
{
printf("sq is null\n");
return -1;
}
value = sq->data[sq->front];
sq->front = (sq->front+1) % MAXSIZE;
return value;
}
判断函数
/*判断是否队列空,空返回1,不空返回0*/
int sequeue_empty(sequeue *sq)
{
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
return (sq->front == sq->rear?1:0);
}
/*判断队列是否满*/
int sequeue_full(sequeue *sq)
{
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
if((sq->rear+1) % MAXSIZE == sq->front )
{
return 1;
}
else
{
return 0;
}
}
清除函数
/*清除队列,此处清除的是队列的数据,只需要将头尾相等即可*/
int sequeue_clear(sequeue *sq)
{
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
sq->front = sq->rear = 0;
return 0;
}
释放函数
/*释放队列*/
/*函数设置一个参数,是为了给外界用户提供一个接口,防止外界的指针释放以后未置空成为野指针*/
sequeue *sequeue_free(sequeue *sq)
{
/*判断队列指针是否为倥指针*/
if(sq == NULL)
{
printf("sq is NULL\n");
return -1;
}
free(sq);
sq = NULL;
return NULL
}
链式队列
链式队列其实和单链表相似,只不过链式队列规定只能尾部进入,头部出来,也就是插入操作在队尾进行,删除操作在队头进行。
链式队列由队头指针和队尾指针控制队列的操作
以下是链式队列的出队和入队操作示意图,
链式队列在C语言下编写的结构代码如下,
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}linknode,linklist*;
/*这个结构体才是链式队列的本体*/
/*通过两个指针对链表进行控制*/
typedef struct
{
linklist *front;
linklist *rear;
}linkqueue;
以下是链式队列的各个操作的函数编写,
创建链式队列
/*创建链式队列*/
linkqueue *queue_creat()
{
linkqueue *lq;
if(lq = (linkqueue*)malloc(sizeof(linkqueue)) == NULL)
{
printf("malloc linkqueue fail\n");
return NULL;
}
/*此处已经将linkqueue的两个参数进行赋值*/
if(lq->front = lq->rear = (linklist)malloc(sizeof(linknode)) == NULL)
{
printf("malloc linklist fail");
return NULL;
}
/*对linknode的参数辅助*/
lq->front->data = 0;
lq->front->next = NULL;
return lq;
}
入队函数
/*入队函数*/
/*入队时只能从尾部进入*/
int enqueue(linksequeue *lq,datatype value)
{
linklist q;
if(lq == NULL)
{
printf(lq is NULL\n);
return -1;
}
/*把入队的参数封装成一个链表节点*/
if(q = (linklist)malloc(sizeof(linknode)) == NULL)
{
printf("malloc linklist fail");
return NULL;
}
/*对linknode的参数辅助*/
q->data = value;
q->next = NULL;
lq->rear->next = q;
lq->rear = q;
return 0;
}
出队函数
/*出队函数*/
/*出对时只能从头部删除*/
datatype dequeue(linkqueue *lq)
{
linklist p;
datatype value;
if(lq == NULL)
{
printf(lq is NULL\n);
return -1;
}
/*判断队列是否为空*/
if(lq->front->next == NULL)
{
printf("lq data is NULL\n");
return 0;
}
/*思路1,出队时我们可以选择将头指针和后一个链表的连接断开将其连到在后一个链表上*/
/*特殊情况,当数组只有一个元素时,需要移动rear指针*/
/*p = lq->front->next;
if(p->next == NULL)
{
lq->rear = lq->front;
}
else
{
lq->front->next = p->next;
}*/
/*思路2,出对时我们可以直接选择删除头节点,那么要出队的元素就变成了头节点,也就相当于删除了*/
p = lq->front;
lq->front = lq->front->next;
value = p->data;
free(p);
p = NULL;
return value;
}
判断函数
/*判断是否空*/
int queue_empty(linkqueue *lq)
{
if(lq == NULL)
{
printf(lq is NULL\n);
return -1;
}
/*判断队列是否为空*/
if(lq->front->next == NULL)
{
printf("lq data is NULL\n");
return 1;
}
else
{
return 0;
}
}
清除函数
/*清除链式队列*/
/*其实和free操作类似,只是说要留一个头节点,因为队列还要存在*/
int queue_clear(linkqueue *lq)
{
linklist q;
/*由于我们malloc申请了两个部分的空间,所以都要释放*/
if(lq == NULL)
{
printf(lq is NULL\n);
return -1;
}
while(lq->front->next != NULL)
{
q = lq->front;
lq->front = q->next;
free(q);
}
return 0;
}
释放函数
/*释放整个队列空间,也就是清除后队列不存在*/
linkqueue queue_free(linkqueue *lq)
{
linklist q;
/*由于我们malloc申请了两个部分的空间,所以都要释放*/
if(lq == NULL)
{
printf(lq is NULL\n);
return -1;
}
while(lq->front != NULL)
{
q = lq->front;
lq->front = lq->front->next;
free(q);
}
free(lq);
lq = NULL;
return NULL;
}
针对数据结构与算法的学习主要是从代码编程的角度进行学习,建议大家学习完相关理论知识后,还是自己进行敲一下代码,这样对数据结构的学习更有帮助!