队列的模拟实现

普通队列

队列是一种先进先出的数据结构, 不支持随机访问,只能查看队首和队尾

 每次只能从队尾插入数据,从队首删除数据

队列可以使用链表作为底层实现,也可以使用动态数组进行实现,由于队首只能出,不能进,使用动态数组则会导致每次出队列之后,前面的空间空着无法使用,所以使用链表来实现。

实现

定义

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int MQElemType;

typedef struct Node {
	MQElemType val;		//数据域
	struct Node* next;	//指针域
}Node;

typedef struct MyQueue {
	Node* front;		//指向头
	Node* back;			//指向尾
	size_t size;		//链表大小
}MyQueue;
//创建队列
MyQueue* myQueueCreate();
//添加元素
void myQueuePush(MyQueue* obj, MQElemType x);
//弹出元素
MQElemType myQueuePop(MyQueue* obj);
//查看队首元素
MQElemType myQueueFront(MyQueue* obj);
//查看队尾元素
MQElemType myQueueBack(MyQueue* obj);
//释放队列空间
void myQueueFree(MyQueue* obj);
//判断队列是否为空
bool myQueueIsEmpty(MyQueue* obj);

创建队列

MyQueue* myQueueCreate(){
	MyQueue* p = (MyQueue*)malloc(sizeof(MyQueue));
	assert(p);//保证空间开辟成功
	p->back = NULL;
	p->front = NULL;
	p->size = 0;
	return p;
}

添加元素

void myQueuePush(MyQueue* obj, MQElemType x){
	assert(obj);	//保证操作对象为真
	Node* newNode = (Node*)malloc(sizeof(Node));
	assert(newNode);
	newNode->val = x;
	newNode->next = NULL;
	//如果此时队列为空
	if (obj->size == 0){
		obj->front = newNode;
		obj->back = newNode;
	}
	else{	//否则,此时队尾一定不为空,链接到队列即可
		obj->back->next = newNode;
		obj->back = newNode;
	}	
	obj->size++;
}

弹出元素

MQElemType myQueuePop(MyQueue* obj){
	assert(obj);
	//如果此时队列为空,返回-1
	if (obj->size == 0){
		return -1;
	}
	//否则,删除队首元素,修改队首指向的位置,返回被删除的元素
	MQElemType tmp = obj->front->val;
	Node* pCurrent = obj->front;
	obj->front = pCurrent->next;
	free(tmp);
	obj->size--;
	//此时删除完毕,如果刚才删除的是队中的最后一个元素,那么队尾也应该变为空
	if (obj->size == 0){
		obj->back = NULL;
	}
	return tmp;
}

查看队首元素

MQElemType myQueueFront(MyQueue* obj){
	assert(obj);
	if (obj->size == 0){
		return -1;
	}
	else {
		return obj->front->val;
	}
}

查看队尾元素

MQElemType myQueueBack(MyQueue* obj) {
	assert(obj);
	if (obj->size == 0) {
		return -1;
	}
	else {
		return obj->back->val;
	}
}

释放队列空间

void myQueueFree(MyQueue* obj){	
    Node* pCurrent = obj->front;
	while (obj->front != NULL){
		obj->front = pCurrent->next;
		free(pCurrent);
		pCurrent = obj->front;
	}
	free(obj);
}

判断队列是否为空

bool myQueueIsEmpty(MyQueue* obj) {
	return obj->size == 0;
}

循环队列

循环队列可以看成一个环,在使用数组实现时,可以将元素插入到之前出队的位置

 所以在创建环形队列时,应给其一个指定大小,同时需要在每次操作的时候,判断当前队列的状态。

实现

定义

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef struct {
	int* arr;   //数组对象
	int n;  //存储最大数据
	int front;  //头
	int back;   //尾
} MyCircularQueue;
//创建环形队列
MyCircularQueue* myCircularQueueCreate(int k);
//入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);
//出队
bool myCircularQueueDeQueue(MyCircularQueue* obj);
//查看队首元素
int myCircularQueueFront(MyCircularQueue* obj);
//查看队尾元素
int myCircularQueueRear(MyCircularQueue* obj);
//判断队空
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
//判断队满
bool myCircularQueueIsFull(MyCircularQueue* obj);
//释放队列
void myCircularQueueFree(MyCircularQueue* obj);

创建环形队列

若使用传统方式来判断当前队列的元素数目,也就是如果队列为空那么尾和头都指向同一个下标,则会导致无法判断是空队列还是满队列,所以当队列为空时我将其头尾都指向-1,那么在入队时,先给back添加,后操作,可以保证back所指的就是最后一个元素,同时也便于区分队列的状态

MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* p = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    assert(p);    //保证开辟成功
	p->arr = (int*)calloc(k, sizeof(int));
    assert(p->arr);    //保证开辟成功
	p->n = k;
	p->back = -1;   //此时没有元素
	p->front = -1;
	return p;
}

入队

入队时需要先判断队是否满了,如果满了则失败,否则成功,当队满的时候有一下两种:

1、如果此时back在front前,那么则一定有back + 1 == front

2、如果此时back在front后,那么则有back 与 front之间的元素数目刚好时最大容量,即back == front + n - 1

特殊的是,当这个队的容量只有一个时,此时如果为空,即back和front都是-1,则第二个条件恒成立,我们单独判断这种情况,也就是当front为-1时直接为空

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	//判断队列是否为满,如果队满了,则无法入队,返回假
    if ((obj->back + 1 == obj->front + obj->n || obj->front - 1 == obj->back) && (obj->front != -1)) {
		return false;
	}
    //此时可以入队返回空
	if (obj->front == -1) {
		//如果此时队列为空,则需要同时修改front和back
		obj->front = 0;
		obj->back = 0;
		obj->arr[0] = value;
		return true;
	}
	//否则此时只修改back指针即可
	obj->back++;
	//判断此时back位置,并进行修正
	obj->back %= obj->n;    //构成一个循环时指针回到0,否则还是自己
	obj->arr[obj->back] = value;
	return true;
}

出队

出队时,我们需要考虑当前队列出队后是否为空队列,有两种情况

1、back和front不在最后一个位置

 2、back和front在最后一个位置

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (obj->front == -1) {
		return false;
	}
	obj->front++;   //出队,则访问不到前一个元素
	//如果当前front为n,则说明这个队列循环了一次
	obj->front %= obj->n;
	//此时出队,需要判断front 和 back之间的关系,看其是否为空
	//此操作为删除,且目前已经删除了,那么只有当front恰好比back大1,或者 front为0,而back为n-1时,为空 
	//若back的位置刚好在front的前一个,则说明为空了
	int t = obj->back + 1;
	t %= obj->n;
	if (t == obj->front)
	{
		obj->back = -1;
		obj->front = -1;
	}
	return true;
}

获取队首元素

int myCircularQueueFront(MyCircularQueue* obj) {
	if (obj->front == -1)
	{
		return -1;
	}
	return obj->arr[obj->front];
}

获取队尾元素

int myCircularQueueRear(MyCircularQueue* obj) {
	if (obj->front == -1)
	{
		return -1;
	}
	return obj->arr[obj->back];
}

判断队满

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	return (obj->back + 1 == obj->front + obj->n || obj->front - 1 == obj->back) || (obj->front == obj->back && obj->front != -1 && obj->n == 1);
}

判断队空

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	return obj->front == -1;
}

释放队列

void myCircularQueueFree(MyCircularQueue* obj) {
	free(obj->arr);
	free(obj);
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值