数据结构--队列

什么是队列

队列只允许在一端进行插入数据这一端叫队尾,在另一端进行删除数据,这一端叫队头,的特殊线性表。
遵循先进先出的原则。
在这里插入图片描述

入队

在这里插入图片描述

出队

在这里插入图片描述

队列的实现

队列也可以用数组和链表实现,但是用链表更优,在对头的处理上效率更高。

标记一个头指针,一个尾指针

typedef int QueueDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QueueDataType data;
}QN;

typedef struct Queue
{
	QN* tail;
	QN* head;
}Queue;

相关函数接口的声明

void QueueInit(Queue* pq);//初始化队列
void QueueDestroy(Queue* pq);//销毁队列
void QueuePush(Queue* pq, QueueDataType x);//入队
void QueuePop(Queue* pq);//出队
QueueDataType QueueFront(Queue* pq);//队头数据
QueueDataType QueueBack(Queue* pq);//队尾数据
bool QueueEmpty(Queue* pq);//判空
int QueueSize(Queue* pq);//队列数据数量

函数定义

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->tail = pq->head = NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QN* cur = pq->head;
	while (cur)
	{
		QN* prev = cur->next;
		free(cur);
		cur = prev;
	}
	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QueueDataType x)//入队列
{
	assert(pq);
		QN* new = (QN*)malloc(sizeof(QN));
			if (new == NULL)
			{
				perror("malloc fail:");
				exit(-1);
			}
			else
			{
				new->data = x;
				new->next = NULL;
			}//若开辟节点成功,就初始化节点

			if (pq->tail == NULL && pq->head == NULL)
			{
				pq->head = pq->tail = new;
			}
			else
			{
				pq->tail->next = new;
				pq->tail = new;
			}
			
	
}
void QueuePop(Queue* pq)//出队列
{
	assert(pq);
	assert(!QueueEmpty(pq));
	if (pq->head->next == NULL)//要分只有一个节点的情况
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else //如果不去分析,这样虽然head指向空,但是tail不指向空,会有内存泄露的风险
	{
		QN* del = pq->head;
			pq->head = pq->head->next;
			free(del);
	}
}
QueueDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->head->data;//如果没有节点不不就存在对NULL的解引用了嘛?
}
QueueDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head==NULL && pq->tail == NULL;

}
int QueueSize(Queue* pq)
{
	assert(pq);
	QN* cur = pq->head;
	int n = 0;
	while (cur)
	{
		n++;
		cur = cur->next;
	}
	return n;
}

循环队列

它的长度不变,逻辑上成环状。
在这里插入图片描述

循环队列可以用数组也可以用链表来实现。

用数组实现:让back 走到超出长度下标,就又要回到前面0下标
用队列实现:使用循环链表实现,即让尾的next指向头
但是无论哪个,判空和判满怎么区别呢?
空:front 与back 在一开始都初始化为0,
或者都指向NULL; 两者相等。
满:front 在头,back 在尾的下一个,即为头,
或者back的next 又指向了头。
解决方法:
1.加一个size
空:0
满:队列长度
2.增加一个空间,满的时候永远保留一个空位置
(比如,队列长度为4,就要开5个空间)
当back 的下一个位置是front 就为满了

对于链表,如果选择去多留一个空间的话,那么实现队列就要去实现一个队尾数据读取的功能,此时的back 是队尾数据的下一个,链表无法倒回前一个,要么去再有一个指针记录;但对于数组,读取队尾,只要让下标减一即可。
所以如果要用链表去实现的话,最后用size ,但是也很麻烦,要去遍历计数。

总之,对于实现循环队列,一般地,标准的去实现,应该用数组并且增加一个空位置的方法。

这里的N记录的是总共的空间

typedef struct {
    int *a;
    int front;
    int back;
    int N;
} MyCircularQueue;

创建一个队列,初始化 front 和 back
这里的参数k 是能存储数据的长度


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int *)malloc((k+1)*sizeof(int));
    obj->front=obj->back=0;
    obj->N=k+1;
    return obj;
}

队列的判空判满

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {//判空
    return obj->front==obj->back;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {//判满
    return obj->front==(obj->back+1)%obj->N;
}

插入删除数据
获取队首队尾数据
注意:走到末尾下标加一要回到头,
走到队前下标减一要回末尾,注意考虑!!!用%

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//插入数据
    //判满
    if(myCircularQueueIsFull(obj))
    {
        return false;
    }
    else
    {
        obj->a[obj->back]=value;
        obj->back++;
        obj->back%=obj->N;
        return true;
    }

}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {//删除数据
    //判空
    if(myCircularQueueIsEmpty(obj))
    {
        return false;
    }
    else 
    {
        obj->front++;
        obj->front%=obj->N;
        return true;
    }
}

int myCircularQueueFront(MyCircularQueue* obj) {//队首获取数据
     //判空
     if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    {
        return obj->a[obj->front];
    }

}

int myCircularQueueRear(MyCircularQueue* obj) {//队尾获取数据
     //判空
      if(myCircularQueueIsEmpty(obj))
    {
        return -1;
    }
    else
    return obj->a[(obj->back-1+obj->N)%(obj->N)];
}

最后释放
malloc几次就释放几次

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

一循环队列,队头指针为front,队尾指针为rear
队列的长度为N(假设该循环队列多给了一个空间,N表示实际空间大小)
那么,对内的有效长度为:

(rear-front+N)%N;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值