【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性

队列是一种特殊的线性表,特殊之处在于它只允许头部和尾部进行操作。


Table of Contents

顺序队列

顺序循环队列

链式队列


顺序队列

队列定义:

  1. 队列(queue)是只允许在一端(一般为尾部)进行插入操作,另一端(一般是头部)进行删除操作的线性表。
  2. 队列是一种先进先出的线性表,允许插入的一端称为队尾(rear),允许删除的一端称为队头(front)
  3. 队列是一种运算受限制的线性表,所以又叫先进先出表(First In First Out),简称FIFO表
  4. 例如,排队打饭、买票就是一种队列。

队列的特性:

  1. 向队列中插入元素称为入队,从队列中删除元素称为出队。
  2. 当队列中没有元素时称为空队列。
  3. 队列的操作是按先进先出的原则进行的,即新添加的元素总是加到队尾(rear),每次离开的元素总是队头(front)的元素。

什么是顺序队列:

  1. 顺序队列 = 数组 + 队列的思想
  2. 假设一个队列有n个元素,则顺序存储的队列需要建立一个大于n的数组,并把队列的所有元素存储在数组的前n个单元,数组下标为0的一端即为队头(front),数组下标为n-1的一端为队尾(rear)
  3. 在数组的基础上,使用front和rear限定增删操作,就为顺序队列
  4. 所谓的入队,就是在队尾追加一个元素,不需要移动任何元素。队尾下标加一,将元素存入该下标所在的数组中。时间复杂度为O(1)。
  5. 所谓的出队,就是在队头(front)减一即可。

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第1张图片

顺序队列的代码定义

#define SIZE 20

typedef struct sequence_queue
{/*algorithm:本质上是一个数组,用两个数来保存数组现在位置的下标;*/
	int data[SIZE];
	int front;
	int rear;
}node;

顺序队列特性:

  1. front 等于 rear 时,不是队列中有一个元素,而是表示空队列
  2. front一定是在rear的前面,或者是相等。
  3. 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 
#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;
}
 

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第2张图片


顺序循环队列

定义:在顺序队列的基础上行,把队列的这种头尾相接的顺序存储结构称为循环队列。即把rear指向0位置。

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第3张图片

基础操作(设队列的最大尺寸为QueueSize):

  1. 初始化:q.front = q.rera = 0;
  2. 队列满的判断条件改为(rear+1)%QueueSize == front
  3. 队列的长度为(rear - front + QueueSize)% QueueSize
  4. 入队时的队尾指针加 1 操作修改为  q.rear  = (q.rear + 1) % QueueSize
  5. 出队时的队首指针加 1 操作修改为  q.front = (q.front + 1) % QueueSize

 如何区分队空还是队满

由于形成了一个环,rear == front无法判定是队空还是队满。

解决方案:

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第4张图片

 

第一个顺序循环队列 

第一个顺序循环队列
#include 
#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;
}

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第5张图片

 


链式队列

链式队列:

  1. 链式队列 = 单链表 + 队列的思想
  2. 只不过它只能是尾进头出而已。将队头指针指向链队列的头结点,而队尾指针指向终端结点。
  3. 通常将链式队列设计成一个带头结点的单链表,因为这样使得插入和删除统一。
  4. 链式队列适合数据元素变动比较大的情形。

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第6张图片

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第7张图片

链式队列的代码定义

//本质:一个单链表加上一个队列首尾指针;

typedef struct link_queue
{//定义存储数据的单链表;
	int data;
	struct link_queue* next;
}node;

typedef struct
{//定义队首队尾指针;
	node* front, * rear;//申明两个node型结构体指针,front用来保存头,rear用来保存尾巴;
}queue;

入队操作

链式队列的入队操作其实就是在链表尾部插入结点:

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第8张图片

出队操作

出队操作时,就是头结点的后继结点出队若链表除头结点外只剩一个元素时,则需将rear指向头结点:

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第9张图片

第一个链式队列

#include 
#include 
#include 

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;
}

【线性表】队列:顺序队列、顺序循环队列、链式队列的基本特性_第10张图片


 

你可能感兴趣的:(DataStructure,数据结构,队列,顺序队列,顺序循环队列,链式队列)