【数据结构和算法】 - 队列

学习目标:

  • 队列的设计和实现

队列的特点:先进先出。

学习内容:

一、队列的头文件

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QDataType;
typedef struct QueueNode
{
    
    
	struct QueueNode* next;
	QDataType data;

}QueueNode;

typedef struct Queue
{
    
    
	QueueNode* head;//头指针
	QueueNode* tail;//尾指针
	//注意:利用头尾指针维护队列。所以通过Queue变量的地址就能操作里面的内容,也就能操作队列。
}Queue;

void QueueInit(Queue* pq);
//如果不定义Queue结构体,则Queue* pq的head和tail由QueueNode** pphead, QueueNode** pptail实现
//void QueueInit(QueueNode** pphead, QueueNode** pptail);
//注意:如果形参是结构体,则实参传结构体地址。如果形参是二级指针,则实参传一级指针的地址。

void QueueDestroy(Queue* pq);

//1、头作队头,尾作队尾 头删出队、尾插入队,头删时间复杂度为O(1),尾插时间复杂度为O(1),适合
//2、头做队尾,尾作对头 尾删出队、头插入队,尾删时间复杂度为O(1),头插时间复杂度为O(1),适合
//注意:这里使用第一种方式。且不需要哨兵位结点。

//队尾入 - 尾插
void QueuePush(Queue* pq, QDataType x);
//队头出 - 头删
void QueuePop(Queue* pq);
//获取队头数据
QDataType QueueFront(Queue* pq);
//获取队尾数据
QDataType QueueBack(Queue* pq);

int QueueSize(Queue* pq);

bool QueueEmpty(Queue* pq);

注意:使用带头结点和尾结点的单链表实现队列结构。


二、代码实现
1、void QueueInit(Queue* pq);初始化队列

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

2、void QueueDestroy(Queue* pq);销毁队列

void QueueDestroy(Queue* pq)
{
    
    
	assert(pq);
	QueueNode* cur = pq->head;
	while (cur != NULL)//注意:不是while (cur != pq->tail)否则最后一个结点没有删除
	{
    
    
		QueueNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = NULL;
	pq->tail = NULL;
}

3、void QueuePush(Queue* pq, QDataType x);队尾入 - 尾插

void QueuePush(Queue* pq, QDataType x)
{
    
    
	assert(pq);
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	if (newnode == NULL)
		return;
	newnode->data = x;
	newnode->next = NULL;
	//没有结点时,空队列
	if (pq->head == NULL)
	{
    
    
		pq->head = pq->tail = newnode;
	}
	else
	{
    
    
		pq->tail->next = newnode;
		pq->tail = newnode;//更新尾节点
	}
}

4、void QueuePop(Queue* pq);队头出 - 头删

//方法1:
void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	//判断队列是否为空,队列为空时,再删队尾的数据,NULL指针访问
	assert(!QueueEmpty(pq));//!QueueEmpty(pq)为假,则QueueEmpty(pq)为真,相当于assert(pq->head);
	//方法1:
	QueueNode* next = pq->head->next;//注意pq->head为空,则解引用报错
	free(pq->head);
	pq->head = next;//当删除最后一个结点的时候,head被置为NULL,但tail并没有置NULL
	//方法2:
	//QueueNode* del = pq->head;
	//pq->head = pq->head->next;//当删除最后一个结点的时候,head被置为NULL,但tail并没有置NULL
	//free(del);

	//即当只有一个元素(结点)的时候,head和tail指向同一个地址。调用QueuePop后,head被置为NULL了。但tail并不没有置空,tail为野指针。
	//故需要这里再判断,当head为空时,将tail也置空。
	if (pq->head == NULL)
		pq->tail = NULL;
	//通过调试观察现象。
}
//方法2:
void QueuePop(Queue* pq)
{
    
    
	assert(pq);
	//队列为空时,再删队尾的数据,NULL指针访问
	assert(pq->head);
	//只有一个结点
	if (pq->head->next == NULL)
	{
    
    
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
    
    
		QueueNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}	
}

5、QDataType QueueFront(Queue* pq);获取队头数据

QDataType QueueFront(Queue* pq)
{
    
    
	assert(pq);
	//队列为空时,再取队尾的数据,NULL指针访问
	assert(!QueueEmpty(pq));//注意pq->head为空,队列为空,解引用报错
	return pq->head->data;
}

6、QDataType QueueBack(Queue* pq);获取队尾数据

QDataType QueueBack(Queue* pq)
{
    
    
	assert(pq);
	//队列为空时,再取队尾的数据,NULL指针访问
	assert(!QueueEmpty(pq));//注意pq->tail为空,队列为空,解引用报错
	return pq->tail->data;
}

7、int QueueSize(Queue* pq);队列大小

int QueueSize(Queue* pq)
{
    
    
	assert(pq);
	int n = 0;
	QueueNode* cur = pq->head;
	while (cur != NULL)//注意:不是while (cur != pq->tail)否则少算了一个
	{
    
    
		++n;
		cur = cur->next;
	}
	return n;

	//或者在Queue结构体加一个size成员变量,在Push或者Pop函数中,++或--
	//那么这里就可以直接使用下面代码
	//return pq->size;
}

8、bool QueueEmpty(Queue* pq);判断队列是否为空

扫描二维码关注公众号,回复: 14879429 查看本文章
bool QueueEmpty(Queue* pq)
{
    
    
	assert(pq);
	return pq->head == NULL;
}

三、代码测试

#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

void TestQueue1()
{
    
    
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	QueuePop(&q);
	//QueuePop(&q);//队列为空,继续删除,报错
	//QueuePop删除最后一个元素,没有将tail置空,当调试发现tail为野指针

	//printf("%d\n", QueueBack(&q));//注释掉:QueueBack中的assert(!QueueEmpty(pq));会打印随机值

	QueuePush(&q, 5);
	QueuePush(&q, 6);

	QueueDestroy(&q);
}
//先进先出
void TestQueue2()
{
    
    
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QDataType front = QueueFront(&q);
	printf("%d ", front);
	QDataType front = QueueFront(&q);
	printf("%d ", front);
	QueuePop(&q);
	QueuePush(&q, 5);

	//遍历队列
	while (!QueueEmpty(&q))
	{
    
    
		QDataType front = QueueFront(&q);
		printf("%d ", front);
		QueuePop(&q);
	}
	printf("\n");//1 2 3 4 5
	//队列的数据是绝对位置

	QueueDestroy(&q);
}
int main()
{
    
    
	//TestQueue1();
	TestQueue2();
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_48163964/article/details/130136504