前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >【数据结构】队列(C++)

【数据结构】队列(C++)

作者头像
半生瓜的blog
发布2023-05-13 13:29:27
发布2023-05-13 13:29:27
53600
代码可运行
举报
文章被收录于专栏:半生瓜のblog半生瓜のblog
运行总次数:0
代码可运行

队列

队列是一种受限的线性表,它允许在一段进行删除操作,在另一端进行插入操作。

可以用数组实现,也可以用链表实现。

数组实现(顺序存储)

设立一个队头指针front,一个队尾指针rear,分别指向队头元素和队尾元素,rear-front为元素个数。

(数组实现中,其实就是下标。)

代码语言:javascript
代码运行次数:0
复制
#define MAX_SIZE 10
typedef int DataType;

typedef struct Queue
{
   DataType queue[MAX_SIZE];
   int front;
   int rear;
}SeqQueue;

//初始化
void initQueue(SeqQueue* SQ)
{
   if (!SQ)//如果传递进来的是个空指针的话
   {
   	return;
   }
   SQ->front = SQ->rear = 0;
}
//判断队列是否满了
bool isFull(SeqQueue* SQ)
{
   if (!SQ)
   {
   	return false;
   }

   if (SQ->rear == MAX_SIZE)
   {
   	return true;
   }
   return false;
}
//判断队列是否为空
bool isEmpty(SeqQueue* SQ)
{
   if (!SQ)
   {
   	return false;
   }
   if (SQ->rear == SQ->front)
   {
   	return true;
   }
   return false;
}
//入队
bool pushQueue(SeqQueue* SQ,DataType data)
{
   if (!SQ)
   {
   	return false;
   }
   if (isFull(SQ))
   {
   	return false;
   }
   SQ->queue[SQ->rear] = data;
   SQ->rear++;//队尾指针后移,插入之后rear"指向"当前位置的下一个位置。
   return true;
}
//输出
void printQueue(SeqQueue* SQ)
{
   if (isEmpty(SQ))
   {
   	return;
   }
   int i = SQ->front;
   while (i < SQ->rear)
   {
   	cout << SQ->queue[i]<<" ";
   	i++;
   }
   cout << endl;
}

//出队-从表头开始删除
bool popQueue(SeqQueue* SQ)
{
   //就是删除第一个元素
   if (!SQ || isEmpty(SQ))
   {
   	return false;
   }
   for (int i = SQ->front; i < SQ->rear; i++)
   {
   	SQ->queue[i] = SQ->queue[i+1];
   }
   SQ->rear--;
   //直接对front进行操作的话空间会越来越小
   return false;
}
//获取队列首元素
DataType getFront(SeqQueue* SQ)
{
   if (!SQ)
   {
   	return -1;
   }
   return SQ->queue[SQ->front];
}
//清空队列
bool destoryQueue(SeqQueue* SQ)
{
   if (!SQ)
   {
   	return false;
   }
   SQ->front = SQ->rear = 0;
   return true;
}
//获取长度-元素个数
int getElemsNum(SeqQueue* SQ)
{
   if (!SQ)
   {
   	return 0;
   }
   return SQ->rear-SQ->front;//返回SQ->rear也一样,数组下标从0开始
}

链表实现(链式存储)

为了操作上的方便,我们将队头指针指向链队列的头结点,而队尾指针指向终端结点。

就是简化了单链表,加了些条件限制(一边进,一边出。)

代码语言:javascript
代码运行次数:0
复制
typedef int DataType;
 
#define MAX_SIZE 100


//结点结构
typedef struct Qnode
{
	DataType data;
	struct Qnode* next;

}Qnode;


typedef Qnode* QueuePtr;

//用数组实现的队列rear指向的是最后一个的下一个,而用链表实现的队列rear指向的是最后一个

//队列的结构
typedef struct Queue
{
	int length;
	QueuePtr front;//队头指针
	QueuePtr rear;//队尾指针
}LinkQueue;

//初始化队列
bool initLinkQueue(LinkQueue* LQ)
{
	if (!LQ)
	{
		cout << "S";
		return false;
	}
	LQ->length = 0;
	LQ->front = LQ->rear = NULL;//把队头和队尾指针同时置空
	//初始化的置空可以想象成是有个“头结点”,判断队列中是否有元素就看front是否指向的是NULL
	//其实就是有个头结点,开始就已经new出来了
	return true;
}

//判断队列是否为空
bool isEmpty(LinkQueue* LQ)
{
	if (!LQ)
	{
		return false;
	}
	if (!LQ->front)
	{
		return true;
	}
	return false;
}
//判断队列是否满了
bool isFull(LinkQueue* LQ)
{
	if (!LQ)
	{
		return false;
	}
	if (LQ->length == MAX_SIZE)
	{
		return true;
	}
	return false;
}
//入队-从尾部
bool pushQueue(LinkQueue* LQ, DataType num)
{
	if (!LQ)
	{
		return false;
	}
	Qnode* node = new Qnode;
	node->data = num;
	node->next = NULL;

	if (isEmpty(LQ))
	{
		LQ->front = LQ->rear = node;
	}
	else
	{
		LQ->rear->next = node;
		LQ->rear = node;//定位到新的末尾结点
	}
	LQ->length++;
	return true;
}
//出队-从头部
bool popQueue(LinkQueue* LQ)
{
	if (!LQ || isEmpty(LQ))
	{
		return false;
	}
	Qnode* tempnode = LQ->front;
	LQ->front = LQ->front->next;
	delete tempnode;
	LQ->length--;
	//如果出了这个元素后为空,需要将rear也置空
	if (!LQ->front)
	{
		LQ->rear = NULL;
	}
	return true;
}
//打印输出
void printQueue(LinkQueue* LQ)
{
	if (!LQ || isEmpty(LQ))
	{
		return;
	}
	Qnode* tempnode = NULL;
	tempnode = LQ->front;
	while (tempnode)
	{
		cout << tempnode->data<<" ";
		tempnode = tempnode->next;
	}
	cout << endl;
}
//清空队列
bool clearQueue(LinkQueue* LQ)
{
	if (!LQ)
	{
		return false;
	}
	Qnode* tempnode = NULL;
	while (LQ->front)
	{
		tempnode = LQ->front->next;
		delete LQ->front;
		LQ->front = tempnode;
	}
	LQ->front = LQ->rear = NULL;
	LQ->length = 0;
	return true;
}
//获取队列的首元素
bool getFront(LinkQueue* LQ, DataType* recv)
{
	if (!LQ || isEmpty(LQ))
	{
		return false;
	}
	if (!recv)
	{
		return false;
	}
	*recv = LQ->front->data;
	return false;
}
//获取队列元素个数
int getNums(LinkQueue* LQ)
{
	if (!LQ)
	{
		return 0;
	}
	return LQ->length;
}

实际应用

线程池中的任务队列

线程池——由一个任务队列和一组处理队列的线程组成。一旦工作进程需要处理某个可能“阻塞”的 操作,不用自己操作,将其作为一个任务放到线程池的队列,接着会被某个空闲线程提取处理。

循环队列

与数组实现队列中,出队方式相关,直接移动front(假溢出),front前的元素全部抛弃,认为是空,下次直接覆盖上去。

判断是否满了,rear+1是否等于front

用模运算来循环

代码语言:javascript
代码运行次数:0
复制
typedef int DataType;

#define MAX_SIZE 10

typedef struct Queue
{
	DataType queue[MAX_SIZE];
	int front;
	int rear;//rear一直在追front
}SeqQueue;

//初始化队列
bool initQueue(SeqQueue* SQ)
{
	if (!SQ)
	{
		return false;
	}
	SQ->front = SQ->rear = 0;

	return 0;
}
//是否为满了
bool isFull(SeqQueue* SQ)
{
	if (!SQ)
	{
		return false;
	}
	if ((SQ->rear + 1) % MAX_SIZE == SQ->front)//因为rear要指向front前的一个空的位置,所以真正只可以push进MAX_SIZE-1个
	{
		return true;
	}
	return false;
}
//是否为空
bool isEmpty(SeqQueue* SQ)
{
	if (!SQ)
	{
		return false;
	}
	if (SQ->front == SQ->rear)
	{
		return true;
	}
	return false;
}
//入队
bool pushQueue(SeqQueue* SQ, DataType m_data)
{
	if (!SQ || isFull(SQ))
	{
		return false;
	}
	SQ->queue[SQ->rear] = m_data;
	//移动队尾指针
	SQ->rear = (SQ->rear + 1) % MAX_SIZE;
	return true;
}
//出队
bool popQueue(SeqQueue* SQ)
{
	if (!SQ || isEmpty(SQ))
	{
		return false;
	}
	//队首指针后移
	SQ->front = (SQ->front + 1) % MAX_SIZE;
	return true;
}
//打印输出
//从第一个位置之后,rear都指向一个空的位置
bool printQueue(SeqQueue* SQ)
{
	if (!SQ)
	{
		return false;
	}
	int index = SQ->front;
	while (index != SQ->rear)
	{
		cout << SQ->queue[index] << " ";
		index = (index + 1) % MAX_SIZE;
	}
	cout << endl;
	return false;
}
//计算元素个数
int getElemsNum(SeqQueue* SQ)
{
	if (!SQ)
	{
		return 0;
	}
	return (SQ->rear - SQ->front + MAX_SIZE) % MAX_SIZE;
}

优先队列

它的入队顺序没有变化,但是出队的顺序是根据优先级的高低来决定的,优先级高的先出。

还是要对比顺序存储和链式存储rear指向位置。 顺序存储rear指向最后一个的下一个位置 链式存储rear指向最后一个

代码语言:javascript
代码运行次数:0
复制
using namespace std;

#define MAX_SIZE 10

typedef int DataType;

typedef struct Qnode//结点结构
{
   int priority;//优先级
   DataType data;
   struct Qnode* next;
}Qnode;

typedef struct Queue//队列结构
{
   int length;
   Qnode* front;
   Qnode* rear;
}Queue;

//初始化
bool initQueue(Queue* Q)
{
   if (!Q)
   {
   	return false;
   }
   Q->front = Q->rear = NULL;
   Q->length = 0;
   return true;

}
//是否为空
bool isEmpty(Queue* Q)
{
   if (!Q)
   {
   	return false;
   }
   if (Q->front == NULL)//空的,返回真,不空返回假,if(isEmprt(Q) != NULL)就是: 真的就直接return 
   {
   	return true;
   }
   return false;
}
//是否满了
bool isFull(Queue* Q)
{
   if (!Q)
   {
   	return false;
   }
   if (isEmpty(Q))
   {
   	return false;
   }
   if (Q->length == MAX_SIZE)
   {
   	return true;
   }
   return false;
}
//入队
bool pushQueue(Queue* Q, DataType _data, int _priority)
{
   if (!Q)
   {
   	return false;
   }
   if (isFull(Q))
   {
   	return false;	
   }

   Qnode* _node = new Qnode;
   _node->priority = _priority;
   _node->data = _data;
   _node->next = NULL;
   
   //空队列
   if (isEmpty(Q))
   {
   	Q->front = Q->rear = _node;
   }
   else
   {
   	Q->rear->next = _node;
   	Q->rear = _node;//定位到新的结尾
   }
   Q->length++;

   return true;
}
//出队列——按照优先级
bool popQueue(Queue* Q)
{
   if (!Q || isEmpty(Q))
   {
   	return false;
   }
   
   Qnode* recvPrev = NULL;//存储要删除结点的前一个结点
   Qnode* start = NULL;
   Qnode* startNext = NULL;
   Qnode* deleteTmp = NULL;

   start = Q->front;
   startNext = start->next;

   recvPrev = start;//默认要删除的是第二个,所以存的是第一个结点

   //特殊情况1,当前队列中就一个结点
   if (!startNext)
   {
   	delete start;
   	Q->front = Q->rear = NULL;
   	Q->length--;
   	return true;
   }

   //正常情况,完整的遍历一遍
   while (startNext)
   {
   	if (startNext->priority > start->priority)
   	{
   		recvPrev = start;
   	}
   	start = startNext;
   	startNext = startNext->next;		
   }

   //特殊情况2,第一个结点是优先级最高的
   //这样得到的recvPrev是第一个结点了,也有可能是为了删除第二个结点
   //所以要进行一下验证,验证是否要删除的是第一个结点
   Qnode* difFandS = NULL;
   difFandS = Q->front->next;
   if (recvPrev->priority > difFandS->priority)
   {
   	//如果第一个结点的优先级大于第二个结点
   	delete recvPrev;
   	Q->front = difFandS;
   	Q->length--;
   	return true;
   }

   //出来后recvPrev就是要删除结点的前一个结点
   deleteTmp = recvPrev->next;
   recvPrev->next = deleteTmp->next;
   delete deleteTmp;

   Q->length--;
   return true;
}
//打印
bool printQueue(Queue* Q)
{
   if (!Q)
   {
   	return false;
   }
   //补充提示注意:根据具体的返回内容来书写判断语句条件
   if (isEmpty(Q))//空的,返回真,不空返回假,if(isEmprt(Q))就是: 返回了真的就直接return 
   {
   	cout << "空的,别打印了" << endl;
   	return false;
   }
   Qnode* tempnode = Q->front;
   while (tempnode)
   {
   	cout << "<" << tempnode->data << "," << tempnode->priority << ">" << " ";
   	tempnode = tempnode->next;
   }
   cout << endl;
   return true;
}
//清空整个队列
bool clearQueue(Queue* Q)
{
   if (!Q)
   {
   	return false;
   }
   
   Qnode* tempnode = NULL;
   while (Q->front)
   {
   	tempnode = Q->front->next;
   	delete Q->front;
   	Q->front = tempnode;
   }
   
   Q->front = Q->rear = NULL;
   Q->length = 0;
   return false;
   
}

动态顺序队列

使用链式存储(链表)实现的队列即为动态顺序队列,前面已经实现过,不再重复。

高并发WEB服务器队列的应用

在高并发 HTTP 反向代理服务器 Nginx 中,存在着一个跟性能息息相关的模块 ——文件缓存。 经常访问到的文件会被 Nginx 从磁盘缓存到内存,这样可以极大的提高 Nginx 的并发能力,不过因为 内存的限制, 当缓存的文件数达到一定程度的时候就会采取淘汰机制, 优先淘汰进入时间比较久或是最近访问很少(LRU)的队列文件。

具体实现方案:

使用双向循环队列保存缓存的文件节点,这样可以实现多种淘汰策略: 比如:如果采用淘汰进入时间比较久的策略,就可以使用队列的特性,先进先出 如果要采用按照 LRU(进入时间 or 最近访问较少),就遍历链表,找到节点删除。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-10-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 队列
    • 数组实现(顺序存储)
    • 链表实现(链式存储)
    • 实际应用
      • 线程池中的任务队列
      • 循环队列
      • 优先队列
      • 动态顺序队列
      • 高并发WEB服务器队列的应用
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档