队列是一种特殊的线性表,特殊之处在于它只允许头部和尾部进行操作。
Table of Contents
顺序队列
队列定义:
- 队列(queue)是只允许在一端(一般为尾部)进行插入操作,另一端(一般是头部)进行删除操作的线性表。
- 队列是一种先进先出的线性表,允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)。
- 队列是一种运算受限制的线性表,所以又叫先进先出表(First In First Out),简称FIFO表。
- 例如,排队打饭、买票就是一种队列。
队列的特性:
- 向队列中插入元素称为入队,从队列中删除元素称为出队。
- 当队列中没有元素时称为空队列。
- 队列的操作是按先进先出的原则进行的,即新添加的元素总是加到队尾(rear),每次离开的元素总是队头(front)的元素。
什么是顺序队列:
- 顺序队列 = 数组 + 队列的思想
- 假设一个队列有n个元素,则顺序存储的队列需要建立一个大于n的数组,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即为队头(front),数组下标为n-1的一端为队尾(rear)。
- 在数组的基础上,使用front和rear限定增删操作,就为顺序队列
- 所谓的入队,就是在队尾追加一个元素,不需要移动任何元素。队尾下标加一,将元素存入该下标所在的数组中。时间复杂度为O(1)。
- 所谓的出队,就是在队头(front)减一即可。
顺序队列的代码定义
#define SIZE 20
typedef struct sequence_queue
{/*algorithm:本质上是一个数组,用两个数来保存数组现在位置的下标;*/
int data[SIZE];
int front;
int rear;
}node;
顺序队列特性:
- 当 front 等于 rear 时,不是队列中有一个元素,而是表示空队列。
- front一定是在rear的前面,或者是相等。
- rear会有数组溢出的风险。
- 队列的数据区为:[sq.data[0], sq.data[MAXSIZE -1]]
- 队头指针:sq.front
- 队尾指针:sq.rear
- 置空队则(初始化):sq.front=sq.rear=-1;
- 队中元素的个数:m=(sq.rear)-(q.front);
- 队满时:m= MAXSIZE; 队空时:m=0。
- 在不考虑溢出的情况下,入队操作队尾指针加1,指向新位置后,元素入队。操作如下:
sq.rear++; sq.data[sq.rear]=x;
- 在不考虑队空的情况下,出队操作队头指针加1,表明队头元素出队。操作如下:
sq.front++; x=sq.data[sq.front];
第一个顺序队列
第一个顺序队列
#include <stdio.h>
#define MAX 10
typedef struct node
{
int data[MAX]; //数组
int front; //队头
int rear; //对尾
}queue;
int main()
{
queue MyQueue;
//初始化
MyQueue.front = -1;
MyQueue.rear = -1;
//入队
MyQueue.rear++;
MyQueue.front++;
MyQueue.data[MyQueue.rear] = 100;
MyQueue.data[++MyQueue.rear] = 200;
MyQueue.data[++MyQueue.rear] = 300;
//出队
MyQueue.front++;
//遍历
for (int i = MyQueue.front; i <= MyQueue.rear; i++)
{
printf("%d ", MyQueue.data[i]);
}
return 0;
}
顺序循环队列
定义:在顺序队列的基础上行,把队列的这种头尾相接的顺序存储结构称为循环队列。即把rear指向0位置。
基础操作(设队列的最大尺寸为QueueSize):
- 初始化:q.front = q.rera = 0;
- 队列满的判断条件改为(rear+1)%QueueSize == front
- 队列的长度为(rear - front + QueueSize)% QueueSize
- 入队时的队尾指针加 1 操作修改为 q.rear = (q.rear + 1) % QueueSize
- 出队时的队首指针加 1 操作修改为 q.front = (q.front + 1) % QueueSize
如何区分队空还是队满
由于形成了一个环,rear == front无法判定是队空还是队满。
解决方案:
第一个顺序循环队列
第一个顺序循环队列
#include <stdio.h>
#define QueueSize 10
typedef struct node
{
int data[QueueSize]; //数组
int front; //队头
int rear; //对尾
}LoopQueue;
int main()
{
LoopQueue MyQueue;
//初始化
MyQueue.front = 0;
MyQueue.rear = 0;
//入队
MyQueue.data[MyQueue.rear] = 100;
MyQueue.rear = (MyQueue.rear + 1) % QueueSize; //不可以直接++,因为循环队列 最后一个元素的下一个下标可能是0号元素
MyQueue.data[MyQueue.rear] = 200;
MyQueue.rear = (MyQueue.rear + 1) % QueueSize;
MyQueue.data[MyQueue.rear] = 300;
MyQueue.rear = (MyQueue.rear + 1) % QueueSize;
//出队
MyQueue.front = (MyQueue.front + 1) % QueueSize;
int len = (MyQueue.rear - MyQueue.front + QueueSize) % QueueSize;
printf("此时循环队列长度:%d\n", len);
//遍历
for (int i = MyQueue.front; i != MyQueue.rear; i = (i + 1) % QueueSize)
{
printf("%d ", MyQueue.data[i]);
}
return 0;
}
链式队列
链式队列:
- 链式队列 = 单链表 + 队列的思想
- 只不过它只能是尾进头出而已。将队头指针指向链队列的头结点,而队尾指针指向终端结点。
- 通常将链式队列设计成一个带头结点的单链表,因为这样使得插入和删除统一。
- 链式队列适合数据元素变动比较大的情形。
链式队列的代码定义
//本质:一个单链表加上一个队列首尾指针;
typedef struct link_queue
{//定义存储数据的单链表;
int data;
struct link_queue* next;
}node;
typedef struct
{//定义队首队尾指针;
node* front, * rear;//申明两个node型结构体指针,front用来保存头,rear用来保存尾巴;
}queue;
入队操作
链式队列的入队操作其实就是在链表尾部插入结点:
出队操作
出队操作时,就是头结点的后继结点出队,若链表除头结点外只剩一个元素时,则需将rear指向头结点:
第一个链式队列
#include <stdio.h>
#include <malloc.h>
#include <assert.h>
typedef struct link_queue
{ //定义存储数据的单链表;
int data;
struct link_queue* next;
}node;
typedef struct
{ //定义队首队尾指针;
node* front, * rear;//申明两个node型结构体指针,front用来保存头,rear用来保存尾巴;
}queue;
void push(queue* MyQueue, int value)
{
assert(MyQueue != NULL);
node* NewNode = (node*)malloc(sizeof(node));
assert(NewNode != NULL);
NewNode->data = value;
if (MyQueue->front == NULL && MyQueue->rear == NULL) //空队列
{
MyQueue->front = NewNode;
}
else
{
MyQueue->rear->next = NewNode;
}
MyQueue->rear = NewNode;
}
int pop(queue* MyQueue)
{
assert(MyQueue != NULL);
node* tmp = MyQueue->front;
int value = tmp->data;
MyQueue->front = tmp->next;
if (MyQueue->front == NULL) // 最后一个节点出队的时候
{
MyQueue->rear = NULL;
}
free(tmp);
tmp = NULL;
return value;
}
int main()
{
queue* MyQueue = (queue*)malloc(sizeof(queue));
assert(MyQueue != NULL);
MyQueue->front = NULL;
MyQueue->rear = NULL;
push(MyQueue, 100);
push(MyQueue, 200);
push(MyQueue, 300);
push(MyQueue, 400);
pop(MyQueue);
pop(MyQueue);
node* p = MyQueue->front;
assert(p != NULL);
while (p != MyQueue->rear->next)
{
printf("%d ", p->data);
p = p->next;
}
return 0;
}