1.队列
- 和堆栈一样,队列也属于受限制的线性表。
和堆栈不同的是,堆栈只能在一端进行出栈和入栈 (先进后出),而队列只能在尾部插入,在头部删除(先进先出)**。
队列的操作
——入看满不满,出看空不空——
2.队列的顺序储存
-
队列的顺序储存一般由一个
一维数组
+记录头部位置的变量front
+记录尾部位置的变量rear
组成。
对比堆栈,只需要数组和指示栈顶位置的变量Top。 -
front等于第一个元素的前一个下标索引;rear等于最后一个元素的下标索引,且一开始空队列front和rear的值都为-1。
-
数据按照先来后到的原则从头往后排、先入先出,插入元素在尾部插入,与rear相关;删除元素在头部删除,与front相关。
-
当列表的最后方已经没有空间,但是前方还有余下的空间的时候,如何再插入元素呢:
-
顺环队列:
当顺环队列加满的时候,再一次 front == rear 。
解决方法:
1使用额外标记Size或者Tag。Size用来记录数组的大小,Size=0就为空,Size=n就满了;插入一个元素,Tag=1、删除一个元素Tag=0,那么可以根据Tag的值来看当front==rear的时候究竟是空还是满。
2大小为n的数组a[n],可以放入n个元素,但是仅使用n-1个空间,那么就不会出现满的情况。注意不是不可使用头部所空的空间,而是无论是用什么空间,只要保证元素个数不超过n-1即可!
以下代码基于方法2;
初始化
#define MaxSize <最大个数>
struct QNode{
ElementType Data[MaxSize];
int rear;
int front;
};
typedef struct QNode* Queue;
入队列
void AddQ(Queue PtrQ,ElementType item)
{// (PtrQ->rear+1)%MaxSize 实现索引下标的循环
if( (PtrQ->rear+1)%MaxSize == PtrQ->front )
printf("队列已满,不可插入");//检查队列是不是满了
else
{
PtrQ->rear = (PtrQ->rear+1)%MaxSize;
PtrQ->Data[PtrQ->rear] = item;
}
}
比如一个数组a[n = 3],它的元素下标分别为a[0],a[1],a[2]。当rear指向a[2],并且a[0]和a[1]都是空的,要插入一个元素到a[0],(2+1) % 3 = 0!
出队列
删除队列头部的元素,并且返回这个歌元素的值
ElementType DeleteQ(Queue PtrQ)
{
if(PtrQ->front == PtrQ->rear)
printf("队列是空的,无法出队");
else
{
PtrQ->front = (PtrQ->front+1) % MaxSize;//front+1正好就是原来头部元素的位置
result = PtrQ->Data[PtrQ->front];//在这里正好返回
}
return result ;
}
3.队列的链式储存
可以用一个单链表来实现,插入和删除操作在链表的两头进行。
- 链表的Head端对于插入和删除操作都是可以是实现的:插入操作,新节点指向Head指向的结点,Head指向的结点改为指向新节点。删除操作,Head直接跳过第一个节点指向第二个节点。
- 链表的尾端:插入操作,最后一个节点指向新节点。非常不方便进行删除操作,因为这是单向链表,删除最后一个节点之后无法得知上一个节点在哪。
所以只能让链表的Head端做front删除,尾端做rear插入。
理解:队列是一种先进先出(FIFO)的数据结构,应该保持元素的顺序,第一个结点先产生,记为front;最后那个节点记为rear。
初始化
//建立链表的 结点结构
struct Node{
ElementType Data;//每个节点包含的数据
struct Node* Next;//每个节点和后一个节点进行连接
};
//链表队列信息 储存结构--代表队列
struct QNode{
struct Node* rear;//指向链表中作为队列尾的结点
struct Node* front;//指向链表中作为队列头的结点
};
typedef struct QNode* Queue;
Queue PtrQ;
注意,不需要头结点!!
出队
删除头结点(第一个节点),然后返回它的Data值; 出队通常先检查空不空,排除链表为空的情况之后要马上跟上---释放结点的copy、以及return的赋值--,在链表出队程序的--最后再释放空间、return值--。
因为如果在if语句块中再进行则代码需要重复多次。
ElementType DeleteQ(Queue PtrQ)
{
if(PtrQ->front == NULL)//第一步总是要判断是不是空的
printf("这个链表是空的");
return NULL;//队列为空,要执行中断操作
struct Node* Front = PtrQ->front;//保存一下便于后边释放内存
ElementType copy = Front->Data;
if(PtrQ->front == PtrQ->rear)
PtrQ->front = PtrQ->rear = NULL;//考虑这个链表只有一个节点,删除之后front和rear所指的都是NULL,否则的话只要改变front就行了
PtrQ->front = PtrQ->rear = NULL; */
else{
PtrQ->front = Front->Next;
}
free(Front);//释放掉删除的节点的内存
return copy;//返回出队节点的值
}
入队
void AddQ(Queue PtrQ,ElementType item)
{
struct Node* newNode = malloc(sizeof(struct Node));
if (newNode == NULL) { // 检查内存分配是否成功
printf("内存分配失败");
return ;
}
newNode->Data = item;
newNode->Next = NULL;
if(PtrQ->front == NULL)//如果队列为空,front和rear指向的都是NULL
{
PtrQ->front = PtrQ->rear = newNode;//第一个是new最后一个还是new
}
else//至少有一个结点的情况
{//front指向第一个节点不动,rear指向的末结点先指向new,然后再让rear指向new,完成更新
PtrQ->rear->Next = newNode;//将新节点连接到队尾
PtrQ->rear = newNode;//更新rear指针
}
}
易错题:
提示:先插7个,后删3个(圆盘状循环队列)