[数据结构] 队列与循环队列

Posted 哦哦呵呵

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数据结构] 队列与循环队列相关的知识,希望对你有一定的参考价值。

一. 队列概念

  队列只允许在一端进行数据插入,在另一端进行数据删除的线性表,特性为先进先出(FIFO),入队列在队尾进行插入操作,出队列在对头进行删除操作。

二. 队列的实现

  普通队列通过链式进行存储,队列的链式存储有易操作空间大小容易扩充的特性。
  在这里对队列的基本操作,就不再详细解释,直接看代码。

typedef char QDataType;

// 链式结构:表示队列
typedef struct QListNode
{
	struct QListNode* _pNext;
	QDataType _data;
}QNode;
// 队列的结构
typedef struct Queue
{
	QNode* _front;
	QNode* _rear;
}Queue;

QListNode* BuySListNode(QDataType x)
{
	QListNode* pNew = (QListNode*)malloc(sizeof(QListNode));
	pNew->_data = x;
	pNew->_pNext = NULL;

	return pNew;
}
// 初始化队列
void QueueInit(Queue* q)
{
	QNode* pHead = BuySListNode(0);
	if (pHead == NULL)
	{
		return;
	}

	pHead->_pNext = NULL;

	q->_front = q->_rear = pHead;
}

// 队尾入队列
void QueuePush(Queue* q, QDataType data)
{
	assert(q);

	QListNode* newNode = BuySListNode(data);

	q->_rear->_pNext = newNode;
	q->_rear = newNode;
}

// 队头出队列
void QueuePop(Queue* q)
{
	assert(q);

	if (QueueEmpty(q))
	{
		return;
	}

	QListNode* delNode = q->_front->_pNext;
	q->_front->_pNext = delNode->_pNext;

	// 如果队列中只有一个节点,则出队后将队尾指针指向队头
	if (delNode->_pNext == NULL)
	{
		q->_rear = q->_front;
	}
	free(delNode);
}

// 获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q && !QueueEmpty(q));

	return q->_front->_pNext->_data;
}

// 获取队列队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q && !QueueEmpty(q));
	return q->_rear->_data;
}

// 获取队列中有效元素个数
int QueueSize(Queue* q)
{
	assert(q);

	int count = 0;
	QListNode* pCur = q->_front->_pNext;
	
	while (NULL != pCur)
	{
		count++;
		pCur = pCur->_pNext;
	}

	return count;
}

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* q)
{
	assert(q);
	return NULL == q->_front->_pNext;
}

// 销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);

	QListNode* delNode = q->_front;

	while (delNode)
	{
		q->_front = delNode->_pNext;
		free(delNode);
		delNode = q->_front;
	}

	q->_front = q->_rear = NULL;
}

三. 循环队列

1. 存储方式

  在循环队列的实现中,使用数组来实现循环队列,但是在数组中对队列进行出队入队操作时,在出队时将队头指针后移,前方空间就无法使用,则造成了空间的浪费,入队时队尾指针移动,可能会将指针移至超出队列的范围,会造成溢出的情况。
  所以要使用一些算法来解决上述问题。将循环队列中的存储问题分为以下几种情况:

front代表头指针,rear代表尾指针,queueSize代表数组大小
1.队列满:
		1). (rear+1)%queueSize = front (数组会少存储一个元素)
		2). 使用count计数,count = queueSize 
2. 队列空:front = rear
3. 修改队头指针: front = (front+1)%queueSize
4. 修改队尾指针:rear = (rear+1)%queueSize

2. 代码实现

// 循环队列使用数组实现
// 队列满:(rear + 1) % size = front 使用时少存储一个元素
// 队头指针:front = (front + 1) % size
// 队尾指针:rear = (rear + 1) % size

typedef int DataType;

typedef struct {
	DataType* data;
	int front;
	int rear;
	int size;
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
	MyCircularQueue* mcq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	if (NULL == mcq)
	{
		return NULL;
	}

	mcq->data = (DataType*)malloc(k * sizeof(DataType) + 1);
	if (NULL == mcq->data)
	{
		return NULL;
	}

	mcq->front = mcq->rear = 0;
	mcq->size = k + 1;

	return mcq;
}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
	if (obj->front == obj->rear)
	{
		return true;
	}

	return false;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
	if ((obj->rear + 1) % obj->size == obj->front)
	{
		return true;
	}

	return false;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
	if (myCircularQueueIsFull(obj))
	{
		return false;
	}

	obj->rear = (obj->rear + 1) % obj->size;
	obj->data[obj->rear] = value;
	return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
	if (myCircularQueueIsEmpty(obj))
		return false;

	obj->front = (obj->front + 1) % obj->size;
	return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
	return obj->data[obj->front];
}

int myCircularQueueRear(MyCircularQueue* obj) {
	return obj->data[obj->rear];
}

void myCircularQueueFree(MyCircularQueue* obj) {
	free(obj->data);
	obj->data = NULL;
}

以上是关于[数据结构] 队列与循环队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法--队列

C++数据结构——顺序队列(基本代码实现与案例)

Java数据结构——队列

Java数据结构——队列

数据结构与算法—队列queue

循环与循环双端队列