【数据结构】-链队列(带头结点)

链队列-带头结点

  • 1.头文件及类型定义
  • 2.链队列类型定义
  • 3.函数声明
  • 4.基本操作
    • 4.1 初始化队列
    • 4.2 判空
    • 4.3 入队
    • 4.4 出队
    • 4.5 获取队头元素
    • 4.6 main函数
  • 5.小结

1.头文件及类型定义

#include<stdio.h>
#include<stdlib.h>
#define ElemType int

2.链队列类型定义

typedef struct LinkNode {
     		//链式队列的结点类型定义
	ElemType data;				//每个结点存放一个数据元素
	struct LinkNode* next;		//指向下一个结点的指针
}LinkNode;

typedef struct {
     				//联式队列定义
	LinkNode* front, * rear;	//队列的队头指针和队尾指针 
}LinkQueue;

3.函数声明

/*函数声明*/
void InitQueue(LinkQueue& Q);				//1.初始化队列
bool LiQueueEmpty(LinkQueue Q);				//2.判空
bool EnQueue(LinkQueue& Q, ElemType x);		//3.入队操作
bool ExQueue(LinkQueue& Q, ElemType& x);	//4.出队操作
bool GetHead(LinkQueue Q, ElemType& x);		//5.获取队头元素

4.基本操作

4.1 初始化队列

//1.初始化队列(带头结点) 
void InitQueue(LinkQueue& Q) {
     
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));		//初始化时front、rear都指向头结点
	Q.front->next = NULL;	//头结点的next初始化为NULL,表示头结点后暂时没有其他结点
}

4.2 判空

//2.判空(带头结点)
bool LiQueueEmpty(LinkQueue Q) {
     
	return (Q.front == Q.rear);
	//或者return (Q.front->next == NULL);
}

4.3 入队

//3.入队操作(无头结点)
//3.入队操作(带头结点)
bool EnQueue(LinkQueue& Q, ElemType x) {
     
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	if (s == NULL)
		return false;		//内存不足,分配失败
	s->data = x;			//为新结点赋值
	s->next = NULL;			//因为新结点只能从队尾插入,即新结点为最后一个结点,其next指针为NULL
	Q.rear->next = s;		//新结点插入到rear之后:原先队尾指针所指结点的next指向新结点
	Q.rear = s;				//修改队尾结点指针,指向新的队尾结点
	return true;
}

4.4 出队

//4.出队操作(带头结点)
bool ExQueue(LinkQueue& Q, ElemType& x) {
     
	if (Q.front == Q.rear)
		return false;	//队空,报错
	LinkNode* p = Q.front->next;	//找到队头元素
	x = p->data;				//变量x返回队头元素
	Q.front->next = p->next;	//修改头结点指针:将队头元素的后继结点赋给头结点的next指针
	if (Q.rear == p)			//如果此次为最后一个结点出队	
		Q.rear = Q.front;		//修改rear指针,队列置空
	free(p);		//释放结点空间
	return true;	
}

4.5 获取队头元素

//5.获取队头元素
bool GetHead(LinkQueue Q, ElemType& x) {
     
	if (Q.front->next == NULL)		//队空,报错
		return false;
	LinkNode* p = Q.front->next;	//找到队头元素
	x = p->data;		//x返回队头元素
	return true;
}

4.6 main函数

int main() {
     
	LinkQueue Q;		//声明一个链式队列(不分配内存空间)

	/*1、初始化队列*/
	InitQueue(Q);

	/*2、判空*/
	if (LiQueueEmpty(Q))
		printf("当前队列为空!\n");
	else
		printf("当前队列非空!\n");

	/*3、入队操作*/
	ElemType e1;
	printf("请输入入队元素的值:");
	scanf("%d", &e1);
	if (EnQueue(Q, e1))
		printf("新元素入队成功!\n");
	else
		printf("队列已满,新元素入队失败!\n");

	/*4、读取队头元素*/
	ElemType e2 = -1;
	if (GetHead(Q, e2))
		printf("读取队头元素成功,当前队头元素值为:%d\n", e2);
	else
		printf("队列已空,读取队头元素失败!\n");

	/*5、出队操作*/
	ElemType e3 = -1;
	if (ExQueue(Q, e3))
		printf("队头元素出队成功,出队元素值为:%d\n", e3);
	else
		printf("队列已空,队头元素出队失败!\n");

	/*6、读取队头元素*/
	ElemType e4 = -1;
	if (GetHead(Q, e4))
		printf("读取队头元素成功,当前队头元素值为:%d\n", e4);
	else
		printf("队列已空,读取队头元素失败!\n");

	return 0;
}

5.小结

  1. 关于不带头结点和带头结点的区别
    其实和带头结点与不带头结点的单链表的区别没有大的区别。
    (1)入队无区别:对无头结点的链队列来说,入队(插入)操作的对象如果是第一个结点需要单独处理,而带头结点的链队列则不需要,因为头指针永远指向头结点。
    (2)出队有区别:而在引入队尾指针后,无论是带头结点还是不带头结点,在出队(删除)操作的对象是最后一个结点时也需要单独进行处理,修改队尾指针。
  2. 有关队列的应用说明
    (1)在计算机系统中的应用
    ①缓冲区队列
    ②CPU资源竞争
    ③页面置换算法中的FIFO
    (2)在层次遍历中的应用
    树的层次遍历
    图的广度优先遍历
    关于队列在层次遍历中的作用,会在后续的文章中重点介绍。

你可能感兴趣的:(数据结构,数据结构,队列)