【数据结构】队列——顺序实现+链式实现(带头结点+不带头结点)入队 出队 初始化 判空 双端队列 完整代码

四 队列

1.基本概念

  • 定义

    只允许在一端进行插入,在另一端删除的线性表。

  • 术语

    • 队头:允许删除的一端。
    • 队尾:允许插入的一端。
  • 特点:先进先出

2.队列的顺序存储

  • 定义

    #define MaxSize 10
    #define ElemType int
    //定义
    typedef struct {
    	ElemType data[MaxSize];//用静态数组存放队列元素
    	int front, rear;//队头指针和队尾指针
    }SqQueue;
    
  • 循环队列

    搞成循环队列可以节约存储空间。

    避免出现队尾指针Q.rear已经到MaxSize时,队头指针Q.front前有剩余空间的情况。

  • 在循环队列下,队列空的条件是

    Q.rear==Q.front

  • 在循环队列下,队列已满的条件是

    (Q.rear+1)%MaxSize==Q.front

    即尾指针加一等于头指针时,队列已满。

    注:此时会浪费一个存储空间(队尾指针没有存数据)。

    • 如果不浪费,即尾指针rear也存元素,则队满和队空判断条件相同,我们无法用逻辑表达区分两种情况。

  • 如果题目要求不能浪费一点点存储空间,则将队列定义为

    typedef struct {
    	ElemType data[MaxSize];//用静态数组存放队列元素
    	int front, rear;//队头指针和队尾指针
        int size;//队列大小
    }SqQueue;
    
    • 此时队满条件:size==MaxSize
    • 队空条件:size==0
  • 完整代码

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define MaxSize 10
#define ElemType int

//定义
typedef struct {
	ElemType data[MaxSize];//用静态数组存放队列元素
	int front, rear;//队头指针和队尾指针
}SqQueue;

//初始化队列
void InitQueue(SqQueue& Q)
{
	//初始时,队头队尾指针都指向0
	Q.front = Q.rear = 0;
}

//判空
bool QueueEmpty(SqQueue Q)
{
	if (Q.rear == Q.front)
		return true;
	else
		return false;
}

//创建
bool CreatQueue(SqQueue& Q)
{
	int n = 5;
	srand(time(0));
	while (n--)
	{
		//队满则报错
		//队满条件:队尾指针的下一个位置是队头
		if ((Q.rear + 1) % MaxSize == Q.front)
			return false;
		Q.data[Q.rear] = rand() % 100 + 1;//新元素插入队尾
		//将存储空间在逻辑上变成环状(循环队列)
		Q.rear = (Q.rear + 1) % MaxSize;//队尾指针加1取模
	}
	return true;
}

//入队
bool EnQueue(SqQueue& Q, ElemType x)
{
	//队满则报错
	//队满条件:队尾指针的下一个位置是队头
	if ((Q.rear + 1) % MaxSize == Q.front)
		return false;
	Q.data[Q.rear] = x;//新元素插入队尾
	//将存储空间在逻辑上变成环状(循环队列)
	Q.rear = (Q.rear + 1) % MaxSize;//队尾指针加1取模
	return true;
}

//出队
bool DeQueue(SqQueue& Q, ElemType& x)
{
	//判空
	if (Q.rear == Q.front)return false;

	x = Q.data[Q.front];//x存出队的元素
	Q.front = (Q.front + 1) % MaxSize;//头指针后移
	return true;
}

//获取队头元素,用x返回
bool GetHead(SqQueue Q, ElemType& x)
{
	if (Q.rear == Q.front)return false;

	x = Q.data[Q.front];
	return true;
}

void QPrint(SqQueue Q) 
{
	for (int i = Q.front; i < Q.rear-1; i=(i+1) % MaxSize)
	{
		printf("%d,", Q.data[i]);
	}
	printf("%d\n", Q.data[Q.rear-1]);
}


int main()
{
	SqQueue Q;
	InitQueue(Q);

	printf("创建:\n");
	CreatQueue(Q);
	QPrint(Q);
	printf("\n");

	printf("请输入入队元素:\n");
	int x;
	scanf_s("%d", &x);
	EnQueue(Q, x);
	QPrint(Q);
	printf("\n");

	int y;
	DeQueue(Q, y);
	printf("出队元素:%d\n", y);
	QPrint(Q);
	printf("\n");

	int z;
	GetHead(Q, z);
	printf("队头元素:%d\n", z);
}

3.队列的链式实现

3.1 定义

分两个结构体

1.节点结构体:存节点数据和下一个节点地址。

2.队列结构体:存队列的头尾指针。

//定义
//链式队列节点
typedef struct LinkNode {
	ElemType data;
	struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
	LinkNode* front;//指向队列队头的指针
	LinkNode* rear;//指向队列队尾的指针
}LinkQueue;
  • 可分为带头结点和不带头结点,两者功能差不多,具体看题目要求选哪个。
  • 因为是链表,所以不存在队满的时候,也不需要用循环队列。

3.2 带头结点

3.2.1 初始化

1.创建一个头节点;

2.将头尾指针front和rear都指向头节点;

3.头节点的next赋空值NULL。

//初始化队列
void InitQueue(LinkQueue& Q)
{
	//初始时 front、rear都指向头结点
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
}
3.2.2 判空

当头尾指针都指向同一节点时,链表为空。

//判空
bool IsEmpty(LinkQueue& Q)
{
	if (Q.front == Q.rear)
		return true;
	else
		return false;
}
3.2.3 入队
  • 步骤

    1.创建新节点,存数据+初始化指针域。

    2.新节点插入到原rear之后。

    3.更新rear,即使尾结点指向新插入节点。

void EnQueue(LinkQueue& Q, ElemType x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	Q.rear->next = s;//新节点插入到rear之后
	Q.rear = s;//修改表尾指针
}
3.2.4 出队

根据队列中是否只有一个结点分两种情况。

  • 有多个结点时

    1.头结点和出队结点的下一个结点相连。

    2.释放出队结点。

  • 只有一个结点时

    1.尾结点指向头结点。

    2.释放出队结点。

    • 此时相当于空队列。

bool DeQueue(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;//变成空队列
	free(p);//释放出队节点
	return true;
}
3.2.5 完整代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ElemType int

//定义
//链式队列节点
typedef struct LinkNode {
	ElemType data;
	struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
	LinkNode* front;//指向队列队头的指针
	LinkNode* rear;//指向队列队尾的指针
}LinkQueue;

//初始化队列
void InitQueue(LinkQueue& Q)
{
	//初始时 front、rear都指向头结点
	Q.front = Q.rear = (LinkNode*)malloc(sizeof(LinkNode));
	Q.front->next = NULL;
}

//判空
bool IsEmpty(LinkQueue& Q)
{
	if (Q.front == Q.rear)
		return true;
	else
		return false;
}

//创建
bool CreatQueue(LinkQueue& Q)
{
	int n = 5;
	srand(time(0));
	while (n--)
	{
		LinkNode* t = (LinkNode*)malloc(sizeof(LinkNode));
		t->data = rand() % 100 + 1;
		t->next = NULL;
		Q.rear->next = t;
		Q.rear = t;
	}
	return true;
}

//新元素入队
void EnQueue(LinkQueue& Q, ElemType x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	Q.rear->next = s;//新节点插入到rear之后
	Q.rear = s;//修改表尾指针
}

//出队
//用x返回元素
bool DeQueue(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;//变成空队列
	free(p);//释放出队节点
	return true;
}

void QPrint(LinkQueue&Q)
{
	LinkNode* p = Q.front->next;
	while (p != Q.rear)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("%d\n\n", Q.rear->data);
}

int main()
{
	LinkQueue Q;
	InitQueue(Q);

	CreatQueue(Q);
	QPrint(Q);

	printf("请输入要入队的元素:\n");
	int x;
	scanf_s("%d", &x);
	EnQueue(Q, x);
	QPrint(Q);

	int y;
	DeQueue(Q, y);
	printf("出队元素为:%d\n", y);
	QPrint(Q);
}

3.3 不带头结点

3.3.1 初始化

//初始化队列
void InitQueue(LinkQueue& Q)
{
	//初始时 front、rear都指向NULL
	Q.front = NULL;
	Q.rear = NULL;
}
3.3.2 入队

根据入队元素是否是队列中第一个结点分两种情况。

  • 如果入队结点是第一个结点

    则头尾指针都指向入队结点。

  • 如果不是

    则新结点插入到队尾结点之后,并更新队尾结点

void EnQueue(LinkQueue& Q, ElemType x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	//在空队列中插入第一个元素
	if (Q.front == NULL)
	{
		Q.front = s;//队头队尾指针都指向第一个元素
		Q.rear = s;
	}
	else
	{
		Q.rear->next = s;//新节点插入到队尾节点之后
		Q.rear = s;//更新队尾节点
	}
}
3.3.3 出队
  • 如果是最后一个结点出队,则需要恢复成空队状态。
bool DeQueue(LinkQueue& Q, ElemType& x)
{
	//空队
	if (Q.front == NULL)
		return false;

	LinkNode* p = Q.front;//p指向此次出队的节点
	x = p->data;//用变量x返回队头元素
	Q.front = p->next;//头指针指向出队节点的下一个节点
	
	//如果是最后一个节点出队
	//恢复成空队状态
	if (Q.rear == p)
	{
		Q.front = NULL;//front指向NULL
		Q.rear = NULL;//rear指向NULL
	}
	free(p);//释放节点空间
	return true;
}
3.3.4 完整代码
#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ElemType int

//定义
//链式队列节点
typedef struct LinkNode {
	ElemType data;
	struct LinkNode* next;
}LinkNode;
//链式队列
typedef struct {
	LinkNode* front;//指向队列队头的指针
	LinkNode* rear;//指向队列队尾的指针
}LinkQueue;

//初始化队列
void InitQueue(LinkQueue& Q)
{
	//初始时 front、rear都指向NULL
	Q.front = NULL;
	Q.rear = NULL;
}
//判空
bool IsEmpty(LinkQueue& Q)
{
	if (Q.front == NULL)
		return true;
	else
		return false;
}

//创建
bool CreatQueue(LinkQueue& Q)
{
	int n = 5;
	srand(time(0));
	while (n--)
	{
		LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
		s->data = rand() % 100 + 1;
		s->next = NULL;
		//在空队列中插入第一个元素
		if (Q.front == NULL)
		{
			Q.front = s;//队头队尾指针都指向第一个元素
			Q.rear = s;
		}
		else
		{
			Q.rear->next = s;//新节点插入到队尾节点之后
			Q.rear = s;//更新队尾节点
		}
	}
	return true;
}

//入队
void EnQueue(LinkQueue& Q, ElemType x)
{
	LinkNode* s = (LinkNode*)malloc(sizeof(LinkNode));
	s->data = x;
	s->next = NULL;
	//在空队列中插入第一个元素
	if (Q.front == NULL)
	{
		Q.front = s;//队头队尾指针都指向第一个元素
		Q.rear = s;
	}
	else
	{
		Q.rear->next = s;//新节点插入到队尾节点之后
		Q.rear = s;//更新队尾节点
	}
}

//出队
bool DeQueue(LinkQueue& Q, ElemType& x)
{
	//空队
	if (Q.front == NULL)
		return false;

	LinkNode* p = Q.front;//p指向此次出队的节点
	x = p->data;//用变量x返回队头元素
	Q.front = p->next;//头指针指向出队节点的下一个节点
	
	//如果是最后一个节点出队
	//恢复成空队状态
	if (Q.rear == p)
	{
		Q.front = NULL;//front指向NULL
		Q.rear = NULL;//rear指向NULL
	}
	free(p);//释放节点空间
	return true;
}

void QPrint(LinkQueue& Q)
{
	LinkNode* p = Q.front;
	while (p != Q.rear)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("%d\n\n", Q.rear->data);
}

int main()
{
	printf("不带头节点:\n");

	LinkQueue Q;
	InitQueue(Q);

	CreatQueue(Q);
	QPrint(Q);

	printf("请输入要入队的元素:\n");
	int x;
	scanf_s("%d", &x);
	EnQueue(Q, x);
	QPrint(Q);

	int y;
	DeQueue(Q, y);
	printf("出队元素为:%d\n", y);
	QPrint(Q);
}

4.双端队列

  • 定义

    允许从两端插入删除的线性表。

  • 衍生

    • 输出受限的双端队列

      只允许从一端插入,两端删除。

    • 输出受限的双端队列

      只允许从两端插入,一端删除。

在这里插入图片描述

  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
这里是使用C语言实现队列完整代码,包括初始化入队出队和销毁队列操作。采用链式存储结构实现队列。 ```c #include <stdio.h> #include <stdlib.h> // 队列结点结构体 typedef struct node { int data; // 数据域 struct node *next; // 指针域 } Node; // 队列结构体 typedef struct { Node *front; // 队头指针 Node *rear; // 队尾指针 } Queue; // 初始化队列 void initQueue(Queue *q) { q->front = NULL; q->rear = NULL; } // 入队操作 void enQueue(Queue *q, int data) { // 创建新结点 Node *newNode = (Node *)malloc(sizeof(Node)); if (newNode == NULL) { printf("Error: memory allocation failed.\n"); return; } newNode->data = data; newNode->next = NULL; // 将新结点插入队尾 if (q->rear == NULL) { // 队列为空,插入第一个结点 q->rear = newNode; q->front = newNode; } else { q->rear->next = newNode; q->rear = newNode; } } // 出队操作 int deQueue(Queue *q) { if (q->front == NULL) { // 队列为空 printf("Error: queue is empty.\n"); return -1; } // 取出队头结点 Node *temp = q->front; int data = temp->data; q->front = temp->next; // 如果队列只有一个结点,则出队队列为空 if (q->front == NULL) { q->rear = NULL; } free(temp); // 释放出队结点的内存 return data; } // 销毁队列 void destroyQueue(Queue *q) { while (q->front != NULL) { Node *temp = q->front; q->front = q->front->next; free(temp); } q->rear = NULL; } // 测试队列操作 int main() { Queue q; initQueue(&q); enQueue(&q, 1); enQueue(&q, 2); enQueue(&q, 3); printf("%d\n", deQueue(&q)); // 输出1 printf("%d\n", deQueue(&q)); // 输出2 enQueue(&q, 4); printf("%d\n", deQueue(&q)); // 输出3 printf("%d\n", deQueue(&q)); // 输出4 printf("%d\n", deQueue(&q)); // 输出错误信息 destroyQueue(&q); return 0; } ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值