一、链对列的定义
1、队列类似线性表和栈,也是定义在线性结构上的ADT,与线性表和栈的区别在于,元素的插入和删除分别在表的两端进行。类似日常生活中排队,允许插入的一端为队尾(rear),允许删除端称队头(front)
2、特性:First In First Out先进先出,链对列有头指针和尾指针,其中,头指针指向头结点,头结点的下一个才是第一个元素,尾指针直接指向最后一个元素。
链对列只能从队头删除,从队尾插入。(先进先出)
3、为什么要设立尾指针呢?
队列是先入先出, 从头出, 从尾入,如果只有一个指针, 那么另外一个操作就要遍历整个链表, 导致效率降低.,所以要设立尾指针。
#include <bits/stdc++.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
typedef int Status;
typedef ?? QElemType;
using namespace std;
typedef struct QNode
{
QElemType data;
struct QNode *next;
}QNode,*QueuePtr;
typedef struct
{
QueuePtr front;//队头指针
QueuePtr rear;//队尾指针
}LinkQueue;// 链队列
1、链对列的初始化
Status InitQueue(LinkQueue &Q)
{
Q.front=Q.rear=(QueuePtr)malloc(sizeof(QNode));
if(!Q.front) exit(OVERFLOW);
Q.front->next=NULL;//这里千万不要忘记;
return OK;
}//时间复杂度O(1)
这里赋初值的方式和顺序栈区别开,顺序栈是先给base赋初值,判断base是否分配成功,然后让top=base。但是这里是直接让两者连等,判断的是front是否分配成功,之后对front进行操作。
2、链对列的销毁(会第一种方式就行,需要再写,不熟练)
Status DestoryQueue(LinkQueue &Q)
{
QueuePtr p=Q.front,postp;
while(p)//因为是销毁,所以销毁的时候带着头结点也要一起销毁掉;
{
postp=p->next;
free(p);
p=postp;
}
Q.front=NULL;
Q.rear=NULL;
return OK;
}//复杂度O(n)
或者
Status DestoryQueue(LinkQueue &Q)
{
while(Q.front)
{
Q.rear=Q.front->next;
free(Q.front);
Q.front=Q.rear;
}
return OK;
}
销毁链对列的时候,要把头结点也销毁掉,所以while中不是Q.front->next
而是Q.front
3、链对列的清空(需要再看)
Status ClearQueue(LinkQueue &Q)
{
QueuePtr p=Q.front->next,postp;
while(p!=Q.rear)
{
postp=p->next;
free(p);
p=postp;
}
Q.rear=Q.front;
return OK;
}
清空需保留头结点,并让Q.rear指向头结点。
4、入队
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;
}//复杂度O(1)
(1)在队尾插入元素;
(2)千万别忘记p->next=NULL;
p->next=NULL;这里总是忘记
5、链对列删除
Status DeQueue(LinkQueue &Q,QElemType &e)
{
if(Q.front==Q.rear) return ERROR;//空队列
QueuePtr p;
p=Q.front->next;
e=p->data;
Q.front->next=p->next;
if(p==Q.rear) Q.rear=Q.front;//只1个结点时改尾指针//因为尾指针指向的地方是最后一个元素,所以比较的是p和Q.rear,而不是p->next和Q.rear;
free(p);
return OK;
}//复杂度O(1)
不要忘记 if(p==Q.rear) Q.rear=Q.front;这一步的判断。
6、链对列的输出
void PrintQueue(LinkQueue Q)
{
QueuePtr p;
p=Q.front->next;
while(p!=Q.rear)
{
printf("%d ",p->date);
p=p->next;
}
printf("%d\n",p->date);
}
二、循环队列的定义
1、为什么不用顺序队列呢?
因为是队头删除,队尾插入,所以如果使用的是顺序队列的话,虽然内存可能是够的,但是,如果,队头前面可能会有没法用的空间,队尾也可能会有没法用的空间,所以说,需要使用循环对列,这样的话,就不会浪费空间。同时,可能造成队尾在队头的情况,这个时候队头后面有元素,队尾前面有元素,中间没有元素。
2、用循环对列如何判断队空和如何判队列“真”满呢?
为区分队空和队真满,约定只剩一个元素空间时为队满
队空 : front == rear
队满 :(rear+1)%MAXQSIZE==front//M是最大的空间的大小
3、循环队列设有头指针和尾指针,并且,头指针的队列不空则指向队列头元素,尾指针,队列不空则指向队列尾元素下一位置。
#include <bits/stdc++.h>
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OVERFLOW -2
#define MAXQSIZE 100//最大对列长度
typedef int Status;
typedef int QElemType;
using namespace std;
typedef struct
{
QElemType *base;
int front;// 头指针,队列不空则指向队列头元素
int rear;//尾指针,队列不空则指向队列尾元素下一位置
}SqQueue;
注意,这里只有对列的最长限度,不会扩展。
1、构造一个空队列
Status InitQueue(SqQueue &Q)
{
Q.base=(QElemType*)malloc(MAXQSIZE*sizeof(QElemType));
if(!Q.base) exit(OVERFLOW);
Q.front=Q.rear=0;
return OK;
}//复杂度O(1)
2、销毁一个对列
Status DestroyQueue(SqQueue &Q)
{
free(Q.base);
Q.front=Q.rear=0;
return OK;
}//时间复杂度O(1),比链队列快
4、置空队列(这个需要再看)
Status ClearQueue(SqQueue &Q)
{
Q.front=Q.rear=0;//只要想等即可
return OK;
}//复杂度O(1)比链队列快
5、循环队列的插入
Status EnQueue(SqQueue &Q,QElemType e)
{
if((Q.rear+1)%MAXQSIZE==Q.front) return ERROR;
Q.base[Q.rear]=e;
Q.rear=(Q.rear+1)%MAXQSIZE;
return OK;
}//时间复杂度O(1)
6、循环队列的删除
Status DeQueue(SqQueue &Q,QElemType &e)
{
if(Q.front==Q.rear) return ERROR;
e=Q.base[Q.front];
Q.front=(Q.front+1)%MAXQSIZE;//因为可能越界,所以取余;
return OK;
}//时间复杂度O(1)
7、返回循环队列的长度
int QueueLength(SqQueue Q)
{
return (Q.rear-Q.front+MAXQSIZE)%MAXQSIZE;
}//时间复杂度O(1),比链队列快,可修改链队列定义
8、判读循环对列是否为空
Status QueueEmpty(SqQueue Q)
{
if(Q.rear==Q.front) return TRUE;
else return FALSE;
}//O(1)
9、返回循环对列首个元素
Status GetHead(SqQueue Q,QElemType &e)
{
if(Q.front==Q.rear) return FALSE;
e=Q.base[Q.front];
return OK;
}// O(1)若要修改对头元素的值可新设SetHead(&Q,e)