队列
-
队列既可以用链式结构存储也可以用顺序结构存储,
【---跟栈相反的是,栈我们一般用顺序表实现,而队列我们常用链表来实现,简称为链队列 --】
第一种:顺序队列:
老规矩,先看它长得易接受不^V^ ( 自己画的图有些丑~^~) :
- 队头不动,只有rear移动:
加入新节点步骤 :Q[rear]=data rear++;
删除新节点步骤: 需从第一个位置开始,大量移动元素,所有数据向后移动一位 for(int i=front;i<rear;i++){ Q.[i]=Q.[i+1]; }
-
然后rear--;
- 队头front与rear都可以移动:
加入新节点步骤 :Q[rear]=data rear++;
删除新节点步骤:front++;
【注意】:第二种头可动的情况第四个图出现了传说中的“假溢出”de问题:
简单来说就是,J放进队列尾部后,rear指针要继续后移,但此时已不能继续指向下一个空间了。
但是:如图所示,明明front下面还有那么多可以利用的空间啊,因此被称为“假溢出”。而真正的溢出人家长得是很丰满的好吧!!!
那么小伙伴们思考下如何打"假"呢??? 后面第三种队列将会解决这个问题。不要急,骚等啦~
第二种:链队列:
A: 定义方式(有些特别O)
typedef struct Qnode{
Elemtype data;
struct Qnode *next;
}Qnode,*Qlinklist;
typedef struct LinkQ{
Qlinklist front,rear;//定义队列的头指针front尾指针rear
}LinkQ;
//此队列规定在队头出列(删除),在队尾入列(插入)
B:创建(初始化)链表
int initQueue(LinkQ *q){
q->front=q->rear=(Qlinklist)malloc(sizeof(Qnode));//初始化时首尾合一,链表空
if(!q->front){
exit(0);
}
q.front->next=NULL; //头节点即尾结点,指针域都设置为NULL
return 1;
}
C:插入操作
int InsertQueue(LinkQ *q,Elemtype e){
Qlinklist p=(Qlinklist)malloc(sizeof(Qnode));
if(p=NULL){
exit(0);
}
p->data=e;
p->next=NULL;//等会新节点要插在尾结点之后,用来充当新的尾结点 ,所以指针指向空
//队列的插入操作是在尾部进行的
q->rear->next=p;
q->rear=p;
}
D:删除操作
int DeleteQueue(LinkQ *q,Elemtype &e){
Qlinklist p;
if(q->front==q->rear)//队列空
{
return -1;
}
//先用p保存当前第一个节点即需出列的队头,用e返回其数据
p=q->front->next;
*e=p->data;
//真正的删除开始啦
q->front->next=p->next;
if(q->rear==p){
//这是队列只有一个节点的情况
q->rear=q->front; //出列后队空,所以要首尾合一
}
free(p);
}
E:销毁队列
//由于链队列建立在内存的动态区,因此当队列不在有用时应及时销毁,以免过多占用内存空间
int DestroyQueue(LinkQ *q){
while(q->front){
q->rear=q->front->next;//尾指针直接与头指针汇合,相当于删除中间节点
free(q->front);//删除头结点
q->front=q->rear;//表空,首尾合一
}
}
F:完整测试程序(AC)
#include<stdio.h>
#include<malloc.h>
#define Elemtype int
typedef struct Qnode{
Elemtype data;
struct Qnode *next;
}Qnode,*Qlinklist;
typedef struct{
Qlinklist front,rear;//定义队列的头指针front尾指针rear
}LinkQ;
void initQueue(LinkQ *Q){
Q->front=(Qlinklist)malloc(sizeof(Qnode));
Q->rear=Q->front;
Q->front->next=NULL;
}
int InsertQueue(LinkQ *Q,Elemtype e){
Qlinklist p=(Qlinklist)malloc(sizeof(Qnode));
if(p==NULL){
return -1;
}
p->data=e;
p->next=NULL;//等会新节点要插在尾结点之后,用来充当新的尾结点 ,所以指针指向空
//队列的插入操作是在尾部进行的
Q->rear->next=p;
Q->rear=p;
return 0;
}
int DeleteQueue(LinkQ *Q,Elemtype &e){
Qlinklist p;
if(Q->front==Q->rear)//队列空
{
return -1;
}
//先用p保存当前第一个节点即需出列的队头,用e返回其数据
p=Q->front->next;
e=p->data;
//真正的删除开始啦
Q->front->next=p->next;
if(Q->rear==p){
//这是队列只有一个节点的情况
Q->rear=Q->front; //出列后队空,所以要首尾合一
}
free(p);
return 1;
}
//由于链队列建立在内存的动态区,因此当队列不在有用时应及时销毁,以免过多占用内存空间
int DestroyQueue(LinkQ *Q){
while(Q->front){
Q->rear=Q->front->next;//尾指针直接与头指针汇合,相当于删除中间节点
free(Q->front);//删除头结点
Q->front=Q->rear;//表空,首尾合一
}
return 1;
}
//遍历并计算队列长度
void visit(LinkQ *Q){
Qlinklist p;
int i=0;
p=Q->front->next;
while(p!=NULL){
printf("%d ",p->data);
i++;
p=p->next;
}
printf("队列中有%d个元素\n",i);
}
int main(){
LinkQ Q;int n,i,a[100],e;
printf("你想建立多长的队列?\n");
initQueue(&Q);
scanf("%d",&n);
for(i=0;i<n;i++){
scanf("%d",&a[i]);
InsertQueue(&Q,a[i]);
}
visit(&Q);
DeleteQueue(&Q,e);
printf("出列节点数据为%d\n",e);
visit(&Q);
DestroyQueue(&Q);
return 0;
}
第三种:循环队列
----->https://blog.csdn.net/qq_38193883/article/details/97792298
- 又到了正经致谢的神圣时刻!哈哈哈哈哈啊哈哈~