数据结构与算法学习笔记(C语言)
1.定义:队列是一种先进先出(FIFO)的线性表,它只允许从一端插入,而在另一端删除删除元素。
这和我们日常生活中的排队是一样的,排在第一个的人先办事,后来的人排在队尾。
在队列中,允许插入的一段叫队尾(rear),允许删除的一端叫队头(front)。 插入元素到队尾叫入队,删除队头的元素叫出队。
队列的抽象数据类型定义:
ADT Queue {
数据对象:D = {a1, a2, a3...}
数据关系:R = {<a1, a2>,<a2, a3>...}
基本操作:
InitQueue(*Q)
DestroyQueue(*Q)
ClearQueue(*Q)
QueueEmpty(Q)
QueueLength(Q)
GetHead(Q, *e)
EnQueue(*Q, e)
DeQueue(*Q, *e)
QueueTraverse(Q)
}ADT Queue
2.队列的链式存储
一个链队列需要两个分别指示队头和队尾的指针唯一确定,为了操作方便,我们也给链队列添加一个头结点 如果没有头节点,那么初始化之后,入队第一个元素的时候,要把头指针指向新添加的节点,出队最后一个元素的时候,又和出队其他节点的操作不一样 链队列的C语言描述
typedef struct QNode {
QElemType data;
struct QNode *next;
}QNode, *QueuePtr;
typedef struct {
QueuePtr front;
QueuePtr rear;
}LinkQueue;
基本操作的函数原型说明
Status InitQueue(LinkQueue *Q)
Status DestroyQueue(LinkQueue *Q)
Status ClearQueue(LinkQueue *Q)
bool QueueEmpty(LinkQueue Q)
int QueueLength(LinkQueue Q)
Status GetHead(LinkQueue Q, QElemType *e)
Status EnQueue(LinkQueue *Q, QElemType e)
Status DeQueue(LinkQueue *Q, QElemType *e)
void QueueTraverse(LinkQueue Q)
1.队列初始化
Status InitQueue(LinkQueue *Q)
{
Q->front = (QueuePtr)malloc(sizeof(QNode));
if (!Q->front) exit(OVERFLOW);
Q->rear = Q->front;
Q->front->next = NULL;
return OK;
}
2.销毁队列
Status DestroyQueue(LinkQueue *Q)
{
while(Q->front) {
Q->rear = Q->front->next;
free(Q->front);
Q->front = Q->rear;
}
return OK;
}
3.清空队列
Status ClearQueue(LinkQueue *Q)
{
QueuePtr p;
if (Q->front == Q->rear) return OK;
p = Q->front;
Q->rear = p->next;
while (Q->rear) {
Q->front = Q->rear->next;
free(Q->rear)
Q->rear = Q->front;
}
Q->front = p;
Q->front->next = NULL;
}
4.判断队列是否为空
bool QueueEmpty(LinkQueue Q)
{
if (Q.front == Q.rear) return true;
else return false;
}
5.求队列长度
int QueueLength(LinkQueue Q)
{
int i = 0;
Q.rear = Q.front->next;
while(Q.rear) {
++i;
Q.rear = Q.rear->next;
}
return 0;
}
6.取队头元素
Status GetHead(LinkQueue Q, QElemType *e)
{
if (Q.front == Q.rear) return ERROR;
*e = Q.front->next->data;
return OK;
}
7.入队
Status EnQueue(LinkQueue *Q, QElemType e)
{
QueuePtr p;
p = (QueuePtr)malloc(sizeof(QNode));
if (!p) exit(OVERFLOW);
p->data = e;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
return OK;
}
8.出队
Status DeQueue(LinkQueue *Q, QElemType *e)
{
QueuePtr p;
if (Q->rear == Q->front) return ERROR;
p = Q->front->next;
*e = p->data;
Q->front->next = p->next;
if (p == Q->rear) Q->rear = Q->front;
free(p);
return OK;
}
9.打印队列中的元素
void QueueTraverse(LinkQueue Q)
{
Q.rear = Q.front->next;
while (Q.rear) {
printf("%c ", Q.rear->data);
}
printf("\n");
}
队列的顺序存储 1.定义:用一组地址连续的存储单元依次存放从队列到队尾的元素,附设两个整形变量作为队头和队尾的标记,记录队头、队尾的下标
队列为空时,Q.rear = Q.front = 0;队列满时,Q.rear = MAXSIZE; 但是,这里明显有一个大问题,因为元素出队,Q.front++,最后Q.front前面的存储单元都用不了了,造成队列“假空”,为了解决这个问题,我们定义循环队列
将申请的连续空间假想成环状的,0存储单元和最后一个存储单元首尾相接 注意:指针malloc申请的空间一定是地址连续的存储单元,想象成环状是我们在逻辑上这么想,与之相反的就像地球是圆的,我们可以认为汽车是在平地上行驶(不太恰当,但就这样吧)
从图中,可以看出,当循环队列为空时,Q.front = Q.rear = 0; 当循环队列满的时候,在环状空间逻辑上,Q.front 在Q.rear的下一个存储单元, Q.front = (Q.rear + 1) % MAXSIZE;
循环队列的C语言描述
#define MAXSIZE 100
typedef struct {
QElemType *base;
int front;
int rear;
}SqQueue;
循环队列各种操作的实现 1.初始化队列
Status InitQueue(SqQueue *Q)
{
Q->base = (QElemType *)malloc(MAXSIZE * sizeof(QElemType));
if (!Q->base) exit(OVERFLOE);
Q->front = Q->rear = 0;
return OK;
}
2.销毁队列
Status DestroyQueue(SqQueue *Q)
{
Q->front = Q->rear = 0;
free(Q->base);
Q->base = NULL;
return OK;
}
3.清空队列
Status ClearQueue(SqQueue *Q)
{
Q->front = Q->rear = 0;
return OK;
}
4.判断队列是否为空
bool QueueEmpty(SqQueue Q)
{
if (Q.front == Q.rear) return true;
else return false;
}
5.返回队列元素的个数
int QueueLength(SqQueue Q)
{
return (Q.rear - Q.front + MAXSIZE) % MAXSIZE;
}
6.取队头元素
Status GetHead(SqQueue Q, QElemType *e)
{
if (Q.front == Q.rear) return ERROR;
*e = Q.base[Q.front];
return OK;
}
7.元素入队
Status EnQueue(SqQueue *Q, QElemType e)
{
if (Q->front == (Q->rear + 1) % MAXSIZE) return ERROR;
Q->base[Q->rear] = e;
Q->rear = (Q->rear + 1) % MAXSIZE;
return OK;
}
8.元素出队
Status DeQueue(SqQueue *Q, QElemType *e)
{
if (Q->front == Q->rear) return ERROR;
*e = Q->base[Q->front];
Q->front = (Q->front + 1) % MAXSIZE;
return OK;
}
9.遍历队列元素
void QueueTraverse(SqQueue Q)
{
if (Q.rear == Q.front) return;
else if (Q.rear > Q.front) {
for (i = Q.front; i < Q.rear; i++)
printf("%c ", Q.base[i]);
}
else {
for (i = Q.front; i < MAXSIZE; i++)
printf("%c ", Q.base[i]);
for (i = 0; i < Q.rear; i++)
printf("%c ", Q.base[i]);
}
}
队列这部分比较难以理解的可能就是循环队列这一部分了,结合钟表的表盘,理解一下取余对于求真实差值的意义,那么循环队列也不是很难理解,加油!
|