数据结构(6)队列的介绍与代码实现(c)

队列

队列是一种运算受限的线性表。在队列上,插入操作限定在某一端进行,删除操作限定在另一端进行。允许插入的一端称为队尾,允许删除的一端称为队头。新插入的结点只能添加到队尾,被删除的只能是排在队头的结点。所以队列是具有先进先出的特性的线性表。

在这里插入图片描述

下面介绍几种常见队列。

顺序队列

顺序队列 由一个一维数组 和 两个分别指向队头和队尾的指针 实现。但是顺序队列在操作时会有一些问题,比如说我们打算在顺序队列中删除队头元素,先假定队头指针不动,当我们删除一个元素后,后面所有的元素向队头移动。比如像下图所示,我有一个空的队列,我向里面插入A,B,C,此时队头指针front指向的是数组下标为0的位置,队尾指针rear指的是下标3的位置;当A,B进行出队操作后,我们需要将c移动到队头的位置,然后队头指针front保持不变,队尾指针向前移动,此时rear指向的是下标为1的位置,…,可以看出来这样实现的时候有一个明显的缺陷就是如果我们要进行大量的出队操作,则需要搬移大量元素。
在这里插入图片描述
为了解决上面说到的问题,我们又想到了另一个方法,即移动队头指针,具体实现是假设还是一个空的队列,向里面插入A、B、C,此时front指向数组下标为0的位置,rear指向数组下标为3的位置;此时我们让A、B出队,为了不移动后面的数据,我们下一步的操作是不改变队尾指针rear,而是改变队头指针front,将他移动到下标为2的位置,…,此时我们就完美解决了上面说到的得移动大量数据的问题,但是此时新的问题出现了,如果下面我们打算向队列中插入D,E,F,G时就会发现,数组不够用了,但实际上数组是够用的,只是因为不能再往C前面放数据,而导致的假溢出情况。
在这里插入图片描述

为了解决顺序队列实现过程中出现的问题,提出了一种新的队列–循环队列

循环队列 (环形队列)

为了解决假溢出问题,能够充分利用数组的存储空间,我们可以把数组的前端和后端连接起来,形成一个环形的表,这个环形的表就称为循环队列。
在这里插入图片描述
对于循环队列定义,如果队列为空的话,front与rear就指向同一处;当队列满的情况下,front与rear也指向同一处,那循环队列如何判断队列空和满呢?有三种方法:

  • 少用一个存储单元
  • 设置一个标记位
  • 设置一个计数器
代码实现循环队列
#define _CRT_SECURE_NO_WARNINGS 1


#include<assert.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define QueueSize 6   //队列的容量
typedef int DataType;

typedef struct SqQueue
{
	DataType data[QueueSize];  //存放队列的元素
	int front, rear;   //队头和队尾指针
}SqQueue;

//为了区分队列空与满,我们少用一个存储单位,这样的话:
//1.队空条件: front = rear
//2.队满条件: front = (rear +1 )%QueueSize
//3.进队操作: rear循环进1
//4.出队操作:front循环进1

//初始化
void InitQueue(SqQueue &qu)
{
	qu.rear = qu.front = 0;//初始化指针
}

//入队:先判断队列是否已满
void Push(SqQueue &qu, DataType x)
{
	if (qu.front == (qu.rear+1)%QueueSize)
	{
		printf("队列已满\n");
		return;
	}
	else //不满,队尾指针+1,该位置存放x
	{
		qu.rear = (qu.rear+1)%QueueSize;//循环+1
		qu.data[qu.rear] = x;
	}

}

//出队:先判断队列是否已空,若不空,让队头指针循环加1;并将该位置的元素赋值给x
void Pop(SqQueue &qu, DataType &x)
{
	if (qu.front == qu.rear)
	{
		printf("队列为空\n");
		return;
	}
	else 
	{
		qu.front = (qu.front + 1) % QueueSize;
		x = qu.data[qu.front];
		return;
	}
}

//查看队列顶元素:先判断队列是否已空,若不空;将队头指针上一个位置的元素返回
DataType GetTop(SqQueue &qu)
{
	if (qu.front == qu.rear)
	{
		printf("队列为空\n");
		return -1;
	}
	else
	{
		int tmp = (qu.front + 1) % QueueSize;
		return qu.data[tmp];
	}
		
}

//判断队列是否为空
void StackEmpty(SqQueue qu)
{
	if (qu.front == qu.rear)
	{
		printf("队列为空\n");
	}
	else
		printf("队列不为空\n");
}

//打印
void SeqListPrint(const SqQueue qu)
{
	int i = 0;
	printf("队列元素为:");
	for (i = 1; i < QueueSize; i++)
	{
		printf("%d ", qu.data[i]);
	}
	printf("\n");
}

void Test()
{
	SqQueue s;
	SqQueue &ps = s;
	
	InitQueue(ps);
	
	Push(ps, 1);
	Push(ps, 2);
	Push(ps, 3);
	Push(ps, 4);
	Push(ps, 5);
	SeqListPrint(s);

	printf("此时队列顶元素为:%2d\n", GetTop(s));

	int x = 0;
	int &px = x;
	Pop(ps, px);
	printf("出队列元素为:%2d,", px);
	printf("此时队列顶元素为:%2d\n", GetTop(s));

	StackEmpty(s);
}

int main()
{
	Test();
	system("pause");
	return 0;
}

链式队列

链式队列实际上是一个同时带有一个首指针和尾指针的单链表,首指针指向头结点,尾指针指向队尾节点。

代码实现链式队列
#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>

typedef int DataType;

//队列中结点的结构体
typedef struct SLQNode
{
	struct SLQNode *next;  //指针域
	DataType data;  //数据域

}SLQNode;

//队头队尾指针的结构体
typedef struct SLQptr
{
	SLQNode *front;
	SLQNode *rear;
}SLQptr;

//  分析:
//  队空的条件:front == rear == NULL;
//   一般不会出现队满的情况
//  进队:创建结点并将其插入到队尾
//  出队:删除队头的节点

//初始化
void InitQueue(SLQptr *&lq)
{
	lq = (SLQptr*)malloc(sizeof(SLQptr));
	lq->rear = lq->front = NULL;
}

//入队:创建结点并将其插入到队尾
void Push(SLQptr *&lq, DataType x)
{
	SLQNode *newNode = (SLQNode*)malloc(sizeof(SLQNode));
	newNode->data = x;
	newNode->next = NULL;

	if (NULL == lq->front && NULL == lq->rear)//空队
	{
		lq->front = lq->rear = newNode;
	}
	else 
	{
		lq->rear->next = newNode;
		lq->rear = newNode;
	}

}

//出队:删除队头的节点
void Pop(SLQptr *&lq)
{
	SLQNode *newNode = lq->front;
	if (NULL == lq->front && NULL == lq->rear)
	{
		printf("队列为空,不可以出队\n");
		return;
	}
	else if (lq->front == lq->rear)//队列只有一个节点,删除后变为空队列
	{
		lq->front = lq->rear = NULL;
		printf("出队成功\n");
	}
	else
	{		
		lq->front = newNode->next;
		printf("出队成功\n");
	}
	free(newNode);
}

//查看队列顶元素
void GetTop(SLQptr *lq)
{
	int x = -1;
	if (NULL == lq->front && NULL == lq->rear)
	{
		printf("队列为空\n");
		return;
	}
	else
	{
		x = lq->front->data;
		printf("队列的顶元素为:%2d\n",x);
		
	}

}

//判断队列是否为空
void QueueEmpty(SLQptr *lq)
{
	if (NULL == lq->front && NULL == lq->rear)
	{
		printf("队列为空\n");
		return;
	}
	else
		printf("队列不为空\n");
}

//打印
void SLQueuePrint(SLQptr *lq)
{
	SLQNode * cur = lq->front;
	for (; cur != NULL; cur = cur->next)
	{
		printf("%2d->", cur->data);
	}
	printf("NULL\n");

}

void Test()
{
	SLQptr *s;
	SLQptr *&ps = s;

	InitQueue(ps);

	Push(ps, 1);
	Push(ps, 2);
	Push(ps, 3);
	Push(ps, 4);
	Push(ps, 5);
	SLQueuePrint(s);

	GetTop(s);
	Pop(ps);
	GetTop(s);
	Pop(ps);
	GetTop(s);

	QueueEmpty(s);

}

int main()
{
	Test();
	system("pause");
	return 0;
}

优先级队列

带有优先级的队列称为优先级队列,队列具有先进先出的特性,即最先进入队列的元素将被最先出队列,有时也需要把进入队列中的元素分优先级(比如线程调度),出队列时首先 选择优先级最高的元素出队列,对于优先级相同的 元素则按照先进先出的原则出队列。而优先队列一般由二叉堆实现。可以点击优先级队列了解更多关于优先级队列。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值