C语言 队列(链式队列)
链式队列----用链表实现,链式队列就是一个操作受限的单向链表,如果读者了解单向链表的建立过程,那理解链式队列就很容易了,先回顾一下单向链表的建立过程
单向链表
单向链表节点的组成部分
1 struct link
2 {
3 int data;
4 struct link *next;
5 };
数据域:data----用来存储节点数据
指针域:struct link *next----用来存储下一个节点的地址
链式队列和单向链表比就多了两个指针,头指针和尾指针(这里我多加了一个length来记录队列的长度)
typedef struct QNode /* 声明链式队列的结点 */
{
int data;
struct QNode *next;
}Node;
typedef struct QueuePoint /* 声明链式队列的首尾指针 */
{
Node *front;
Node *rear;
int length; /* 记录链式队列长度 */
}Queue;
后面就是单向链表的建立了,这里就不再赘述了,重点分析下头指针的尾指针的移动,为了方便理解先附上main函数部分的代码
main()
{
int i = 0;
char c;
Queue q; //链式队列首尾指针 和 长度
q.front = q.rear = NULL; /* 首尾指针初始化 */
q.length = 0; /* 链式队列长度初始化 */
q = init(q); /* 初始化队列 */
printf("Do you want to append a new node(Y/N)?");
scanf_s(" %c", &c);
while (c == 'Y' || c == 'y')
{
q = AppendNode(q); /* 入队*/
DisplyNode(q); /* 按先进先出对队列进行打印 */
printf("Do you want to append a new node(Y/N)?");
scanf_s(" %c", &c);
}
printf("Do you want to delete node(Y/N)?");
scanf_s(" %c", &c);
while (c == 'Y' || c == 'y')
{
q = DeletNode(q);
DisplyNode(q);
printf("Do you want to delete node(Y/N)?");
scanf_s(" %c", &c);
}
return 0;
}
下面上图
(灵魂画手已上线)
简单描述一下上图的步骤
第一步:初始化队列(就是添加一个头节点在队列中),头结点不存储数据,使队首指针和队尾指针都指向头结点
第二步:入队(添加节点),使队尾指针指向头新建的节点,队首指针不变仍然指向头结点
初始化队列和入队----实现代码
//函数功能:初始化队列(其实就是搞个头结点放在队列里面)
//单独弄个子函数来初始化队列是为了方便入队的时候判断队列是否为空
Queue init (Queue p)
{
p.front = p.rear = (Node *)malloc(sizeof(Node));
if (p.front == NULL && p.rear == NULL)
{
printf("initialization failed");
exit(0);
}
p.front->next = NULL;
return p;
}
//函数功能:新建节点并添加到队列中,记录队列长度
Queue AppendNode (Queue p)
{
int data;
Node *q;
q = (Node *)malloc(sizeof(Node));
if (q == NULL) /* 判断分配内存是否失败 */
{
printf("No enough memory to allocate");
exit(0);
}
p.rear->next = q; /* 最后一个节点的指针指向新建节点*/
p.rear = q; /* 队尾指针指向新建节点*/
printf("Input node data\n");
scanf("%d", &data);
p.rear->data = data;
p.rear->next = NULL;
p.length++;
return p;
}
后面来分析下出队时首尾指针的变化,因为后面出队时要用到判断队列是否为空的一个子函数,这里先附上子函数代码
int IsemptyQueue(Queue p)
{
if (p.front == p.rear) /* 队首指针和队尾指针重合队列为空 */
{
return Empty;
}
else
{
return NoEmpty;
}
}
出队时队首指针的位置是不变的,队首指针始终指向头节点,出队时头节点的指针域指向出队节点的后一节点,并将出队的节点用free()函数释放掉,为了方便读者理解下面上图
出队实现代码
Queue DeletNode (Queue p)
{
Node *del;
if (IsemptyQueue(p) == Empty) /* 判断队列是否为空*/
{
printf("队列为空,无法出队 ");
return p;
}
else /* 队列不为空 */
{
if (p.front->next == p.rear) /* 如果出队的节点为最后一个节点 */
{
printf("出队节点的数据为%d----", p.rear->data);
free(p.rear); /* 释放最后一一个节点*/
p.rear = p.front; /* 队首指针和队尾指针都指向头节点 */
p.front->next = NULL;
p.length--;
}
else
{
del = p.front->next;
printf("出队节点的数据为%d----", del->data);
p.front->next = p.front->next->next; /* 使头节点的指针域指向出队节点的下一个节点 */
free(del); /* 释放出队的节点 */
p.length--;
}
return p;
}
}
顺序队列和链式队列首尾指针的比较
1.顺序队列是用数组实现的,首指针在出队的时候移动,尾指针在入队的时候移动,需要考虑队列为空和队列为满的两种情况
2.链式队列是用链表实现的,首指针不移动始终指向头节点,尾指针在入队的时候移动,只考虑队列为空的情况(不用考虑满是因为链表长度在程序运行过程中可以不断增加,只要存储空间够malloc就能申请内存空间来存放节点)
下面附上完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define Empty 0 /* 队列为空 */
#define NoEmpty 1 /* 队列不为空*/
typedef struct QNode /* 声明链式队列的结点 */
{
int data;
struct QNode *next;
}Node;
typedef struct QueuePoint /* 声明链式队列的首尾指针 */
{
Node *front;
Node *rear;
int length; /* 记录链式队列长度 */
}Queue;
void DisplyNode (Queue p); /* 打印队列 */
Queue init (Queue p); /* 初始化队列 */
Queue AppendNode (Queue p); /* 入队 */
Queue DeletNode (Queue p); /* 出队 */
int IsemptyQueue (Queue p); /* 判断队列是否为空*/
main()
{
int i = 0;
char c;
Queue q; //链式队列首尾指针 和 长度
q.front = q.rear = NULL; /* 首尾指针初始化 */
q.length = 0; /* 链式队列长度初始化 */
q = init(q); /* 初始化队列 */
printf("Do you want to append a new node(Y/N)?");
scanf_s(" %c", &c);
while (c == 'Y' || c == 'y')
{
q = AppendNode(q); /* 入队 */
DisplyNode(q); /* 按先进先出对队列进行打印 */
printf("Do you want to append a new node(Y/N)?");
scanf_s(" %c", &c);
}
printf("Do you want to delete node(Y/N)?");
scanf_s(" %c", &c);
while (c == 'Y' || c == 'y')
{
q = DeletNode(q); /* 出队 */
DisplyNode(q); /* 按先进先出对队列进行打印 */
printf("Do you want to delete node(Y/N)?");
scanf_s(" %c", &c);
}
return 0;
}
int IsemptyQueue(Queue p)
{
if (p.front == p.rear) /* 队首指针和队尾指针重合队列为空 */
{
return Empty;
}
else
{
return NoEmpty;
}
}
Queue DeletNode (Queue p)
{
Node *del;
if (IsemptyQueue(p) == Empty) /* 判断队列是否为空*/
{
printf("队列为空,无法出队 ");
return p;
}
else /* 队列不为空 */
{
if (p.front->next == p.rear) /* 如果出队的节点为最后一个节点 */
{
printf("出队节点的数据为%d----", p.rear->data);
free(p.rear); /* 释放最后一一个节点*/
p.rear = p.front; /* 队首指针和队尾指针都指向头节点 */
p.front->next = NULL;
p.length--;
}
else
{
del = p.front->next;
printf("出队节点的数据为%d----", del->data);
p.front->next = p.front->next->next; /* 使头节点的指针域指向出队节点的下一个节点 */
free(del); /* 释放出队的节点 */
p.length--;
}
return p;
}
}
//函数功能:初始化队列(其实就是搞个头结点放在队列里面)
//单独弄个子函数来初始化队列是为了方便入队的时候判断队列是否为空
Queue init (Queue p)
{
p.front = p.rear = (Node *)malloc(sizeof(Node));
if (p.front == NULL && p.rear == NULL)
{
printf("initialization failed");
exit(0);
}
p.front->next = NULL;
return p;
}
//函数功能:新建节点并添加到队列中,记录队列长度
Queue AppendNode (Queue p)
{
int data;
Node *q;
q = (Node *)malloc(sizeof(Node));
if (q == NULL) /* 判断分配内存是否失败 */
{
printf("No enough memory to allocate");
exit(0);
}
p.rear->next = q; /* 最后一个节点的指针指向新建节点*/
p.rear = q; /* 队尾指针指向新建节点*/
printf("Input node data\n");
scanf("%d", &data);
p.rear->data = data;
p.rear->next = NULL;
p.length++;
return p;
}
//函数功能:按照先进先出原则对队列进行打印
void DisplyNode (Queue p)
{
if (IsemptyQueue(p) == Empty)
{
printf("队列为空,无法打印\n");
}
else
{
p.front = p.front->next;
printf("当前队列中的%d个节点[", p.length);
while (p.front != NULL)
{
printf("%d->", p.front->data);
p.front = p.front->next;
}
putchar(']');
putchar('\n');
}
}
程序试运行结果: