【数据结构】队列

一、队列的定义

队列简称队,是一种操作受限的线性表,只能在表的一端进行插入操作,而在另一端进行删除操作。将进行插入操作的一端称为队尾,进行删除操作的一端称为队首。向队中插入新元素称为进队或入队,从队中删除元素称为出队或退队。元素出队后其后继元素就成为新的队首元素。

队的主要特征为“先进先出”,就像排队买票,先来排队的人先买到票离开,因此也被称为先进先出表。队中元素逻辑关系与线性表的相同,可以采用与线性表相同的存储结构。

队的抽象数据类型定义如下:

ADT SQueue
{
    D={ai|1<=i<=n,n>=0,ai为ELeType类型}
    R={<ai,ai+1>|ai~ai+1∈D,i=1,...,n-1}

    InitQueue(&q):初始化队列。构造一个空队列q。
    DestroyQueue(&q):销毁队列。释放队列q占用的存储空间。
    QueueEmpty(q):判断队列是否为空。若队列q为空,则返回真;否则返回假。
    enQueue(&q,e):进队列。将元素e进队作为队尾元素。
    deQueue(&q,&e):出队列。从队列q中出队一个元素,并将其值赋给e。
}

二、队的顺序存储结构及其基本运算的实现

采用顺序存储结构的队列称为顺序队。顺序存储队列即分配一块连续的存储空间来存放队列中的元素,并用两个整型变量来反映队中元素的变化(分别用来储存队首和队尾元素的下标)。

1、结构体定义

typedef struct 
{  
   ElemType data[MaxSize]; 
   int front,rear;//队首、队尾指针
} SqQueue;

注意:

1、rear指向队尾,进队rear增1;元素e进队:rear++; data[rear]=e;

2、front指向当前队首元素的前一位置,出队front增1;元素e出队:front++; e=data[front];

3、rear=MaxSize-1时队满(或假溢出),不能再进栈

4、 队空条件:front = rear

2、顺序队的基本运算

(1)初始化队

SqQueue *InitQueue()
{  SqQueue *q=(SqQueue *)malloc (sizeof(SqQueue));
  q->front=q->rear=-1;
   return q;
}

(2)销毁队

void DestroyQueue(SqQueue *q)
{
    free(q);
}

(3)判断队列是否为空

bool QueueEmpty(SqQueue *q)
{
    return(q->front==q->rear);
}

(4)进队

bool enQueue(SqQueue *q,ElemType e)
{   if (q->rear==MaxSize-1)	//队满上溢出
	return false;
    q->rear++;
    q->data[q->rear]=e;
    return true;
}

(5)出队

bool deQueue(SqQueue *q,ElemType *e)
{   if (q->front==q->rear)  //队空下溢出
	return false;
    q->front++;
    *e=q->data[q->front];
    return true;
}

3、环形队列(循环队列)

采用rear==MaxSize-1作为队满条件可能会出现假溢出现象(如图)。

为了解决这个问题,我们把数组的前端和后端连接起来,形成一个环形的顺序表,即把存储队列元素的表看成一个环,称为环形队列或循环队列。

实际上内存地址一定是连续的,不可能是环形的,这里是通过逻辑方式实现环形队列,也就是将rear++和front++改为:rear=(rear+1)%MaxSize; front=(front+1)%MaxSize

此时

1、队空条件:front = rear;  
2、队满条件:(rear+1)%MaxSize = front;
3、进队e操作:rear=(rear+1)%MaxSize;  将e放在rear处
4、出队操作:front=(front+1)%MaxSize; 取出front处元素e; 

为了区分队满与队空牺牲一个存储空间,否则队满与队空都将满足front

 三、队的链式存储结构及其基本运算的实现

采用链式存储结构的队列称为链队。

1、结构体定义

typedef struct qnode
{  ElemType data;	//数据元素
   struct qnode *next;
}DataNode;//数据结点

typedef struct
{  DataNode *front;	//指向单链表队头结点
   DataNode *rear; 	//指向单链表队尾结点
}LinkQuNode; //链队结点

注意:

1、队空条件:q->front=q->rear=NULL
2、队满条件:不考虑
3、进队e操作:先建立一个结点用于存放元素e(由p指向它),将结点p插入到原单链表表尾后作为新尾节点
4、出队操作:取出队首结点的data值并删除

2、链队的基本运算

(1)初始化队

LinkQuNode *InitQueue()
{  LinkQuNode *q=(LinkQuNode *)malloc(sizeof(LinkQuNode));
   q->front=q->rear=NULL;
   return q;
}

(2)销毁队

void DestroyQueue(LinkQuNode *q)
{  DataNode *p=q->front,*r;  	//p指向队头数据结点
   if  (p!=NULL)			//释放数据结点占用空间
   {  r=p->next;
      while (r!=NULL)
      {  free(p);
	  p=r;r=p->next;
      }
   }
   free(p);  free(q);		 //释放链队结点占用空间
}

(3)判断队列是否为空

bool QueueEmpty(LinkQuNode *q)
{
  return(q->rear==NULL);
}

(4)进队

void enQueue(LinkQuNode *&q,ElemType e)
{  DataNode *p;
   p=(DataNode *)malloc(sizeof(DataNode));
   p->data=e;
   p->next=NULL;
   if (q->rear==NULL)   //若链队为空,新结点是队首结点又是队尾结点
	q->front=q->rear=p;
   else
   {   q->rear->next=p; //将p结点链到队尾,并将rear指向它
       q->rear=p;
   }
}

(5)出队

bool deQueue(LinkQuNode *q,ElemType *e)
{  DataNode *t;
   if (q->rear==NULL) return false;	//队列为空
   t=q->front;		   		//t指向第一个数据结点
   if (q->front==q->rear)  		//队列中只有一个结点时
       q->front=q->rear=NULL;
   else			   		//队列中有多个结点时
       q->front=q->front->next;
   *e=t->data;
   free(t);
   return true;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值