昨天没写文章,今天加罚几篇 T^T
导读
本篇文章首先将会链表、链栈、链队进行区分总结,然后将会总结链队列的类型定义,以及常用的操作(初始化、出队、入队......),部分函数写的时候还发现了一些问题,在对应部分也有补充探究,同时,文章将会包含完整的C代码以及相关效果的测试图,可以直接复制,并进行自行测试。
三种链式结构总结图
数据域打阴影表示此结点的数据域无意义,比如在单链表中,此结点就是头结点
链队列
链队列和链栈是一样的,也是先定义链表结点,再定义一个含两个链表结点指针类型的成员的结构体即最终的队列的定义,如下所示
typedef struct QNode{
int data;
struct QNode* next;
}QNode,*QNodePtr;
typedef struct {
QNodePtr front;
QNodePtr rear;
}LinkQueue,*QueuePtr;
下面依次介绍其函数:
1.初始化函数:
由上面那个图可以看出,链队中始终让Q->front指向一个数据域无意义的结点,所以对其进行初始化只需让front和rear同时指向此结点即可,在入队过程中,再让rear往后移动即可。
QueuePtr InitQueue(void)
{
QueuePtr Q=(QueuePtr)malloc(sizeof(LinkQueue));
Q->front=Q->rear=(QNodePtr)malloc(sizeof(QNode));
if(!Q->front) exit(OVERFLOW);
Q->front->next=NULL;
return Q;
}
2.销毁队列和清空队列
Status DestroyQueue(QueuePtr Q)
{
while(Q->front)
{
Q->rear=Q->front->next;
free(Q->front);
Q->front=Q->rear;
}
return OK;
}
Status ClearQueue(QueuePtr Q)
{
QNodePtr p=Q->front->next;
while(p)
{
Q->rear=p->next;
free(p);
p=Q->rear;
}
Q->rear=Q->front;
return OK;
}
3.判空、求长:
Status QueueEmpty(QueuePtr Q)
{
if(Q->front==Q->rear) return TRUE;
else return FALSE;
}
int QueueLength(QueuePtr Q)
{
int cnt=0;
QNodePtr p=Q->front->next;
while(p)
{
cnt++;
p=p->next;
}
return cnt;
}
4.遍历、入队、出队(核心操作)
在出队函数中,有一个非常重要的地方,就是如果队列中仅有一个元素,即p==Q->front->next==Q->rear,则必须必须把Q->rear=Q->front
Status QueueTraverse(QueuePtr Q)
{
if(QueueEmpty(Q))
{
printf("队列为空!\n");
return OK;
}
QNodePtr p=Q->front->next;
printf("队列中的元素有:");
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
return OK;
}
Status EnQueue(QueuePtr Q,int e)
{
QNodePtr p=(QueuePtr)malloc(sizeof(QNode));
if(!p) exit(OVERFLOW);
p->data=e; p->next=NULL;
Q->rear->next=p;
Q->rear=p;
return OK;
}
Status DeQueue(QueuePtr Q,int *e)
{
if(QueueEmpty(Q)) return ERROR;
QNodePtr p=Q->front->next;
*e=p->data;
Q->front->next=p->next;
if(Q->rear==p) Q->rear=Q->front;
free(p);
return OK;
}
老样子,如果主函数如下:
int main()
{
int e;
QueuePtr Q;
Q=InitQueue();
QueueTraverse(Q);
EnQueue(Q,1);
EnQueue(Q,2);
EnQueue(Q,3);
QueueTraverse(Q);
DeQueue(Q,&e);
QueueTraverse(Q);
return 0;
}