队列及循环队列

1. 队列(Queue)

1.1 基本概念

  • 队列是一种先进先出(FIFO, First In First Out)的线性数据结构。
  • 队列的操作:
    • 增(入队):在队尾添加元素。
    • 删(出队):从队首移除元素。
    • :查看队首元素。
  • 队列通常用于需要按顺序处理元素的场景,如任务调度、缓冲区管理等。

1.2 队列的实现

队列可以通过数组或链表来实现。

1.2.1 顺序队列(基于数组实现)

  • 使用数组实现队列时,需要维护两个指针:
    • front:指向队列的头部元素。
    • rear:指向队列的尾部元素的下一个位置。
  • 入队操作:将元素放入rear位置,然后rear指针后移。
  • 出队操作:返回front位置的元素,然后front指针后移。
  • 判空操作:front=rear;
  • 判满操作:front=max;

问题:数组实现的队列在出队操作后,front指针会不断后移,导致数组前部的空间无法再利用,造成空间浪费。

循环队列可解决

1.2.2 链表实现

  • 使用链表实现队列时,可以避免数组实现中的空间浪费问题。
  • 入队操作:在链表尾部添加新节点。
  • 出队操作:移除链表头部的节点。
代码
#include<stdio.h>
#include<stdlib.h>

typedef struct qnode
{
	int data;
	struct qnode* next;
}qnode,*lqueue;

typedef struct linkqueue//结构体可写可不写
{
	lqueue front, rear;//队首指针是链表的头结点
}linkqueue;

//初始化
void initqueue(linkqueue *q)
{
	q->front = q->rear = (lqueue)malloc(sizeof(qnode));
	q->front->next = NULL;
}

//入队
void enqueue(linkqueue* q, int x)
{
	lqueue s = (lqueue)malloc(sizeof(qnode));
	s->data = x;
	s->next = NULL;
	q->rear->next = s;
	q->rear = s;
}

//出队
void dequeue(linkqueue* q)
{
	//判空
	if (q->front->next==NULL)
	{
		printf("队空\n");
		return;
	}
	lqueue p = q->front->next;
	q->front->next = p->next;
	if (q->rear == p) q->rear = q->front;
	free(p);
}

//查看队首元素
void getfront(linkqueue* q)
{
	//判空
	if (q->front->next == NULL)
	{
		printf("队空\n");
		return;
	}
	printf("%d\n", q->front->next->data);
}

int main()
{
	linkqueue* q = (linkqueue*)malloc(sizeof(linkqueue));
	initqueue(q);
	enqueue(q, 1);
	dequeue(q);
	dequeue(q);
	enqueue(q, 8);
	getfront(q);
	return 0;
}

1.3 队列的操作

  • Enqueue(x):将元素x添加到队列的尾部。
  • Dequeue():移除并返回队列头部的元素。
  • Peek():返回队列头部的元素,但不移除它。
  • IsEmpty():检查队列是否为空。
  • Size():返回队列中元素的数量。

2. 循环队列(Circular Queue)

2.1 基本概念

  • 循环队列是为了解决数组实现队列时空间浪费问题而提出的一种改进方案。
  • 循环队列通过将数组的首尾相连,形成一个环形结构,使得队列在数组空间用尽时能够从头开始重新利用空间。

2.2 循环队列的实现

  • 循环队列仍然使用数组实现,但需要维护两个指针:
    • front:指向队列的头部元素。
    • rear:指向队列的尾部元素的下一个位置。
  • 循环队列的关键在于通过取模运算来实现指针的循环移动。
  • 方法一:使用一个状态记录flag,记录上一次进行的操作是入栈还是出栈;
  • 方法二:牺牲一个空间使得front和rear判空和判满的状态不一样。
  • 方法三:用一个size记录。

2.2.1 入队操作

  • 将元素放入rear位置,然后rear指针后移:rear = (rear + 1) % capacity
  • 如果rear追上front,说明队列已满。
//入队
void enqueue(squeue* q, int x)
{
	//判满
	if (q->f == ((q->r + 1) % maxx))
	{
		printf("已满\n");
		return;
	}
	q->data[q->r] = x;
	q->r = (q->r + 1) % maxx;
}

2.2.2 出队操作

  • 返回front位置的元素,然后front指针后移:front = (front + 1) % capacity
  • 如果front追上rear,说明队列为空。
//出队
void dequeue(squeue* q)
{
	//判空
	if (q->f == q->r)
	{
		printf("队空\n");
		return;
	}
	q->f = (q->f++) % maxx;
}

2.2.3 查看队首元素

//查看队首元素
void getfront(squeue q)
{
	//判空
	if (q.f == q.r)
	{
		printf("队空\n");
		return;
	}
	printf("%d\n", q.data[q.f]);
}

2.3 循环队列的操作

  • Enqueue(x):将元素x添加到队列的尾部。如果队列已满,则抛出异常或返回错误。
  • Dequeue():移除并返回队列头部的元素。如果队列为空,则抛出异常或返回错误。
  • Peek():返回队列头部的元素,但不移除它。
  • IsEmpty():检查队列是否为空。
  • IsFull():检查队列是否已满。
  • Size():返回队列中元素的数量。

2.4 循环队列的优势

  • 空间利用率高:循环队列能够充分利用数组空间,避免了普通队列在出队后空间浪费的问题。
  • 时间复杂度低:入队和出队操作的时间复杂度均为O(1)。

代码

#include<stdio.h>
#include<stdlib.h>
#define maxx 5
typedef struct
{
	int* data;
	int f, r;
}squeue;

//初始化
squeue initqueue()
{
	squeue q;
	q.data = (int*)malloc(sizeof(int) * maxx);
	q.f = q.r = 0;
	return q;
}

//入队
void enqueue(squeue* q, int x)
{
	//判满
	if (q->f == ((q->r + 1) % maxx))
	{
		printf("已满\n");
		return;
	}
	q->data[q->r] = x;
	q->r = (q->r + 1) % maxx;
}
//出队
void dequeue(squeue* q)
{
	//判空
	if (q->f == q->r)
	{
		printf("队空\n");
		return;
	}
	q->f = (q->f++) % maxx;
}

//查看队首元素
void getfront(squeue q)
{
	//判空
	if (q.f == q.r)
	{
		printf("队空\n");
		return;
	}
	printf("%d\n", q.data[q.f]);
}

int main()
{
	squeue q = initqueue();
	enqueue(&q, 1);
	enqueue(&q, 3);
	dequeue(&q);
	enqueue(&q, 4);
	enqueue(&q, 5);
	enqueue(&q, 6);
	dequeue(&q);
	enqueue(&q, 8);
	getfront(q);
	return 0;
}

3. 总结

  • 队列是一种先进先出的数据结构,适用于需要按顺序处理元素的场景。
  • 循环队列通过环形结构提高了数组空间的利用率,避免了普通队列在出队后空间浪费的问题。
  • 队列和循环队列的实现都可以通过数组或链表来完成,具体选择取决于应用场景和性能需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值