队列(基于数组实现和链表实现)和循环队列

在这里插入图片描述

一、基于数组实现的队列

#include<stdio.h>
#include<stdlib.h>

#define size 10  // 宏定义一个大小为10的队列

// 基于数组实现的队列称为顺序队列
// 定义一个队列的数组结构体
typedef struct queue
{
	int data[size];  // 利用数组实现队列元素的定位
	int head;  // 队头下标
	int tail;  // 队尾下标
}Array_queue;

// 建立一个空队列
Array_queue * Create_queue()
{
	// 申请空间
	Array_queue* q = (Array_queue*)malloc(sizeof(Array_queue));
	if (q == NULL)
	{
		return 0;
	}
	q->head = q->tail = 0;  // 队头和队尾下标都初始化为0
	return q;
}

// 入队
void enqueue(Array_queue* q, int x)
{
	// q->tail = size表示队末没有空间了
	if (q->tail == size)
	{
		// 若队列前面有空余空间,则将队内所有元素往前移
		for (int i = q->head; i < q->tail; i++)
		{
			q->data[i - q->head] = q->data[i];  
		}
		// 搬移完后更新队头和队尾指针
		q->tail -= q->head;
		q->head = 0;
	}
	q->data[q->tail] = x;
	q->tail++;
}

// 出队(队内元素索引出队) 
void dequeue(Array_queue* q, int x)  // 传入队内元素并使其出队
{
	while (x != q->data[q->head])  // 移动队头指针
	{
		q->head++;
	}
	q->head++; 
}

// 打印队列
void Print_queue(Array_queue* q)
{
	int i = q->head;  // 从队头开始打印
	while (i != q->tail)
	{
		printf("%d ", q->data[i]);
		i++;
	}
	printf("\n");
}

int main()
{
	int j = 10;
	Array_queue* q = Create_queue();
	printf("开始入队:\n");
	while (j--)
	{
		// 在进行入队之前加个判断,看队列是否已满,防止数组溢出
		if ((q->head == 0) && (q->tail == size))
		{
			printf("队列已满!\n");
			break;
		}
		enqueue(q, 10 - j);  // 入队
		printf("%d入队后,队内元素为:", 10 - j);
		Print_queue(q);
	}
	dequeue(q, 5);  // 出队
	printf("元素5出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	puts("队列末尾已经无空间,再次执行入队操作时,需将队内元素整体往前移");
	puts("使队头指针重新初始化为0");
	// 重新执行入队操作
	enqueue(q, 110);
	printf("元素110入队后,队内元素为:");
	Print_queue(q);
	printf("队尾还剩4个数据空间\n");
	return 0;
}

运行结果:
在这里插入图片描述

基于数组实现的队列在进行入队操作时,需要警惕的一点就是数组溢出问题,因为基于数组实现的队列是通过下标的移动来进行的,当队列满了之后,队尾指针已经达到数组的最大值,这时就需要进行数据的迁移,若队头前面还有剩余空间,则将队列元素整体往前移,让队头下标重新置为0,否则重新申请一块更大的数组空间来存放队列。

二、基于链表实现的队列

#include<stdio.h>
#include<stdlib.h>

//基于链表实现的队列称为链式队列
// 定义队列结点
typedef struct Node  
{
	int data;  // 元素
	struct Node* next;
}Node;

// 定义队列的结构体
typedef struct LinkList_queue
{
	Node* head;  // 队头指针
	Node* tail;  // 队尾指针
}LinkList_queue;

LinkList_queue * Create_queue()
{
	// 申请空间
	LinkList_queue* q = (LinkList_queue*)malloc(sizeof(LinkList_queue));
	if (q == NULL)
	{
		return;
	}
	q->head = q->tail = NULL;  // 将队头队尾初始化为NULL
	return q;  // 返回q
}

// 入队
void enqueue(LinkList_queue* q, int x)
{
	Node* p = (Node*)malloc(sizeof(Node));  // 申请一个结点空间
	p->data = x;  // 往结点内传值
	p->next = NULL;  // 结点的next设为NULL
	if ((q->tail == NULL) && (q->head == NULL))  // 判断队头和队尾是否为空,用于第一次入队
	{
		q->head = p;  // 队头和队尾都要指向p结点
		q->tail = p;
	}
	else
	{
		q->tail->next = p;  // 让p成为当前尾部的下一结点
		q->tail = p;  // 尾部指针指向p
		// 队头和队尾通过p结点相连,在内存空间地址上是相同的
		// 我们只需要移动队尾结点,使其指向队尾元素即可
	}
	
}

// 出队 按元素来索引出队
void dequeue(LinkList_queue* q, int x)
{
	if (q->head == NULL)  // 判断队列是否为空
	{
		return;
	}
	else
	{
		// 移动队头指针
		while (q->head->data != x)
		{
			q->head = q->head->next;
		}
		q->head = q->head->next;  // 只移动队头指针,不移动队尾指针
		// 如果q->head == NULL说明队列已经被清空,若此时想要再重新入队
		// 则根据前面入队的代码需将q->tail重新置为空
		if (q->head == NULL)  
		{
			q->tail = NULL;
		}
		// 出队时,若队列未清空,则不涉及队尾指针的修改
	}
}

void Print_queue(LinkList_queue* q)
{
	Node* p = (Node*)malloc(sizeof(Node));  // 申请一个结点空间
	p = q->head;
	if (q->head == NULL)  // 如果队列为空
	{
		printf("队列已经被清空!");
	}
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

int main()
{
	int i = 5;
	LinkList_queue* q = Create_queue();  // 先创建一个空队列
	printf("开始入队:\n");
	while (i--)
	{
		enqueue(q, 5 - i);  // 进队
		printf("%d入队后,队内元素为:", 5 - i);
		Print_queue(q);  // 打印队列
	}
	enqueue(q, 6);
	printf("6入队后,队内元素为:");
	Print_queue(q);
	dequeue(q, 4);  // 出队
	printf("元素4出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	dequeue(q, 6);  // 出队
	printf("元素6出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	// 重新执行入队操作
	enqueue(q, 7);
	printf("元素7入队后,队内元素为:");
	Print_queue(q);  // 打印队列
	dequeue(q, 7);  // 出队
	printf("元素7出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	free(q);  // 养成好习惯,清空q,防止内存溢出
	return 0;
}

运行结果:
在这里插入图片描述

基于链表实现的队列没有空间限制,出队即删除元素,无法找回,只能重新入队。

三、循环队列

#include<stdio.h>
#include<stdlib.h>

#define size 5  // 宏定义一个大小为5的队列

// 定义一个队列的数组结构体
typedef struct queue
{
	int data[size];  // 储存队列元素
	int head;  // 队头下标
	int tail;  // 队尾下标
	int n;  // 记录队列容量
}Loop_queue;

// 建立一个空队列
Loop_queue* Create_queue()
{
	// 申请空间
	Loop_queue* q = (Loop_queue*)malloc(sizeof(Loop_queue));
	if (q == NULL)
	{
		return 0;
	}
	q->head = q->tail = q->n = 0;  // 队头和队尾下标都初始化为0
	return q;
}

// 入队
void enqueue(Loop_queue* q, int x)
{
	if (((q->tail + 1) % size) == q->head)  // 判断队列是否已满
	{
		// if(ture) 说明队列已满,使队列容量+1
		// 用于跳出主函数的循环并打印队列已满的提示
		q->n++;
	}
	else
	{
		q->data[q->tail] = x;
		/*循环队列入队操作的核心代码*/
		q->tail = (q->tail + 1) % size;
		q->n++;  // 每入一次队就使队列容量+1
	}
}

// 出队(按元素索引出队)
void* dequeue(Loop_queue* q, int x)
{
	while (x--)
	{
		/*循环队列出队操作的核心代码*/
		q->head = (q->head + 1) % size;
		// 每出一次队,就让队列的容量-1
		q->n--;
	}

}

// 打印队列
void Print_queue(Loop_queue* q)
{
	int i = q->head; // 从队头开始打印
	int j = q->n;
	if (q->head == q->tail)
	{
		printf("队列为空,无法执行出队操作!");
	}
	else if (q->head == size - 1)
	{
		printf("队列已经被清空!");
		q->head = q->tail = q->n = 0;  // 队头和队尾下标以及队列容量都初始化为0
	}
	else
	{
		while (j--)
		{
			if (i < size)
			{
				printf("%d ", q->data[i]);
				i++;
			}
			else
			{
				printf("%d ", q->data[i - size]);
				i++;
			}
		}
	}
	printf("\n");
}

int main()
{
	int j = 5;
	Loop_queue* q = Create_queue();
	puts("***循环队列***");
	puts("开始入队:");
	while (j--)
	{
		enqueue(q, 5 - j);  // 入队
		// 入队后判断队列是否已满
		if ((q->n % size) == q->head)  // 易错点:q->tail从入队函数出来后已经是4了
		{
			printf("队列已满!请等待队首元素出队后再执行入队操作\n");
			q->n -= 1;  // 使队列容量恢复回正常值
			break;
		}
		printf("%d入队后,队内元素为:", 5 - j);
		Print_queue(q);
	}
	dequeue(q, 3);
	printf("元素3出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	enqueue(q, 5);
	printf("元素5入队后,队内元素为:");
	Print_queue(q);
	enqueue(q, 6);
	printf("元素6入队后,队内元素为:");
	Print_queue(q);
	dequeue(q, 6);
	printf("元素6出队后,队内元素为:");
	Print_queue(q);  // 打印队列
	free(q);
	return 0;
}

运行结果:
在这里插入图片描述

1.循环队列操作要点

  • 循环队列需要额外定义一个变量来记录队列的大小
  • 队头指针(head)和队尾指针(tail)以及队列大小都初始化为0,队列每增加一个元素就+1,每减少一个元素就-1
  • 入队操作核心代码:tail = (tail + 1) % size,size是队列的大小
  • 出队操作核心代码:head = (head + 1) % size
  • 队满条件:head == tail
  • 队空条件:(tail + 1) % n = head
  • 队满与队空的条件一定要分析好,否则程序可能会运行出错!!!

2.运行结果的判断

  • 看队首和队尾元素是否都能出队?
  • 队首元素出队后是否能继续入队?
  • 是否能持续进行出队和入队操作?
  • 队满和队空的时候是否能给出提示?

3.循环队列特点:

(1)与<顺序队列>相比,循环队列在进行数据的搬移时更加地自然,不需要重新申请一个额外的存储空间;
(2)由于入队操作的的原因,当队满时,队尾指针指向的位置实际上并没有数据,所以循环队列会浪费一个存储空间;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值