队列

队列:与栈相反,数据是“先进先出”,它只允许在队列的一端(队尾)进行插入,而在另一端(队头)删除元素。


一、顺序队列

队尾:可插入数据的位置,即最后一个数据的后一个格子

队头:第一个数据的位置



在顺序表中实现队列,队列的长度是有限的,当队头的数据被删除时,后面的数据需要先前移动,以保证队尾总能空出至少一个位置来存放插入的数据。


但是,在这种“线性”设计中,数据只能从队头删除(出队),所以时间复杂度为O(1),数据同时也只能从队尾插入(入队),多了移动整组数据的操作,因而时间复杂度为O(n),那么有没有更好的设计方法能够使插入数据的时间复杂度也变为O(n)?

其实,在线性表的基础上,将整个表设计为“环形”更加方便、快捷。同时,需要两个指针分别指向队头和队尾,只是这个指针指的实际上是数据的下标,并且为了方便区分“队列空”和“队列满”,队列中单独空出一个空间来标记结尾


也就是说,在环形队列中,随着数据的插入和删除,队头和队尾的位置可以随时变化,而整个队列仍然有序。

下面就来看看在环形队列中的基本操作吧。

声明:

//环形队列
#define SIZE 4

typedef struct Queue
{
int elem[SIZE];
int front;//队头指针,第一个数据的下标
int rear;//队尾指针,最后一个数据后的第一个格子的下标
}Queue,*PQueue;


void Initqueue(PQueue pq);

bool Push(PQueue pq, int val);

bool IsEmpty(PQueue pq);

bool GetTop(PQueue pq, int *rtval);

bool Pop(PQueue pq, int *rtval);

void Destroy(PQueue pq);

void Show(PQueue pq);


1.环形队列的结构体表示:

typedef struct Queue
{
int elem[SIZE];
int front;//队头指针,第一个数据的下标
int rear;//队尾指针,最后一个数据后的第一个格子的下标
}Queue,*PQueue;


2.初始化:

void Initqueue(PQueue pq)
{
assert(pq!=NULL);

pq->elem[SIZE] = 0;
pq->front = 0;
pq->rear = 0;
}




初始化时,队头指针和队尾指针同时指向0号下标。

3.入队

//判满
static bool IsFull(PQueue pq)
{
return (pq->rear+1)%SIZE == pq->front;
}


//入队
bool Push(PQueue pq, int val)
{
if(IsFull(pq))//队满
{
return false;
}

pq->elem[pq->rear] = val;
pq->rear++;

return true;
}

入队时,一般情况下,在队尾插入数据之后,队尾指针需要向后移动一格。同时,我们需要考虑“队列满”时,数据不能在插入的情况。




4.判空

bool IsEmpty(PQueue pq)
{
return pq->front==pq->rear;
}

环形队列为了区分“队列空”和“队列满”,专门空出一个格子来标志结尾。除去初始化之后的front == rear,队列中的数据不论怎么删减、插入,只要队列中还有数据,那么front != rear是永远成立的,即判空条件为“front == rear”。



5.得到队顶的数据,不删除

bool GetTop(PQueue pq, int *rtval)
{
if(IsEmpty(pq))
{
return false;
}

*rtval = pq->elem[pq->front];//通过输出参数传出数据
return true;
}



6.得到队顶的数据,删除

bool Pop(PQueue pq, int *rtval)
{
if(IsEmpty(pq))
{
return false;
}

*rtval = pq->elem[pq->front];//通过输出参数传出数据

pq->front++;//队头指针向后移动

return true;
}

在得到队顶的元素之后,将其删去。




7.销毁队列

void Destroy(PQueue pq)
{
pq->rear = pq->front;//依照“队列空”的判断条件:pq->rear == pq->front
}


队列中的数据是从队头开始排列的,所以销毁一个队列只需要将其队头置为队尾就可以了。

8.打印

void Show(PQueue pq)

{
int i;
for(i=pq->front; i<pq->rear; i++)
{
printf("%d ",pq->elem[i]);
}
printf("\n");
}


二、链式队列

在上述中的环形队列,队列的长度是有一个最大限度的,如果无法预估所用队列的最大长度,可以选择用单链表来实现队列——链式队列

链式队列的结构如下:


从图中我们可以看出,链式队列和单链表的结构还是有一些差别的。

1.头节点:队列的头节点里面存放的是两个指针的内容,队头、队尾指针的指向。

2.尾指针:队列增加了一个指针——尾指针,用来标记结尾,方便在队尾插入数据。因为在环形队列中我们实现了队头插入、队尾删除的时间复杂度都为O(1),而在单链表中,每一次都需要对链表进行遍历找到尾节点再进行数据插入,正是为了简便这个过程,提高时间复杂度,我们增添了一个尾指针,那么队尾插入的时间复杂度也就是O(1)了。


下面就让我们看看在链式队列中如何实现基本操作吧。


声明:

//带尾指针的单链表
typedef struct QNode//定义数据节点
{
int data;
struct QNode *next;
}QNode;

typedef struct HNode//单独定义头结点
{
QNode *front;//队头指针
QNode *rear;//队尾指针
}HNode,*PLQueue;


void L_Initqueue(PLQueue pq);

bool L_Push(PLQueue pq, int val);

bool L_IsEmpty(PLQueue pq);

bool L_GetTop(PLQueue pq, int *rtval);

bool L_Pop(PLQueue pq, int *rtval);

void L_Destroy(PLQueue pq);

void L_Show(PLQueue pq);


1.链式队列的结构体表示:

typedef struct QNode//定义数据节点
{
int data;
struct QNode *next;
}QNode;


typedef struct HNode//单独定义头结点
{
QNode *front;//队头指针
QNode *rear;//队尾指针
}HNode,*PLQueue;

由于头节点和各个数据结点之间结构的差异,他们也是分开各自定义的。


2.链式队列的初始化

void L_Initqueue(PLQueue pq)
{
assert(pq!=NULL);

pq->front = NULL;
pq->rear = NULL;
}


3.入队

bool L_Push(PLQueue pq, int val)
{
QNode *p;
p = (QNode*)malloc(sizeof(HNode));//创建新结点
p->data = val;//放入数据
p->next = NULL;

if(pq->front==NULL && pq->rear==NULL)//第一次插入
{
pq->front = p;
pq->rear = p;
}
else
{
pq->rear->next = p;
pq->rear = p;
}

return true;
}




4.判空
bool L_IsEmpty(PLQueue pq)
{
return pq->front ==NULL;//没有队头
}



5.取队顶元素,不删除

bool L_GetTop(PLQueue pq, int *rtval)
{
if(pq->front==NULL)//队列为空
{
return false;
}


*rtval = pq->front->data;//取数据


return true;
}

6.取队顶元素,删除
bool L_Pop(PLQueue pq, int *rtval)
{
if(pq->front==NULL)//队列为空
{
return false;
}


*rtval = pq->front->data;//取数据

//删数据
QNode *p;
p = pq->front;
pq->front = pq->front->next;//绑气球
free(p);

return true;
}

7.销毁队列
void L_Destroy(PLQueue pq)
{
QNode *p;//从第一个数据结点开始,总是删除第一个数据结点

while(pq->front!=NULL)
{
p = pq->front;

pq->front = p->next;
free(p);
}

pq->rear = NULL;//队尾指针置空
}

8.打印
void L_Show(PLQueue pq)
{
for(QNode *p=pq->front; p!=NULL; p=p->next)
{
printf("%d ",p->data);
}
printf("\n");
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值