队列算法的原理和实现,及其企业级应用

2023-11-16

一,队列的原理

队列是一种受限的线性表,(Queue),它是一种运算受限的线性表,先进先出(FIFO First In First Out)

  • 队列是一种受限的线性结构

  • 它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。

在这里插入图片描述

生活中队列场景随处可见: 比如在电影院, 商场, 或者厕所排队。。。。。。

二,队列的算法实现

队列的算法实现1:使用数组

顺序存储

采用数组来保存队列的元素,设立一个队首指针 front ,一个队尾指针 rear,分别指向队首和队尾元素。则

rear-front 即为存储的元素个数!

在这里插入图片描述

在这里插入图片描述

定义方式为:

#define MaxSize 5 //队列的最大容量 

typedef int DataType; //队列中元素类型 

typedef struct Queue { 
    DataType queue[MaxSize]; 
    int front; //队头指针 
    int rear; //队尾指针 
}SeqQueue;
#include <iostream>
#include <Windows.h>

using namespace std;

typedef int Datatype;	//队列中的元素类型
#define MAX_SIZE 5		//队列最大容量

typedef struct _Queue {
	Datatype queue[MAX_SIZE];
	int front;
	int rear;
}Queue;

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

//判断队列是否为空
bool blankQueue(Queue* &Q) {
	if(!Q) return false;
	if(Q->rear == Q->front) return true;
	return false;
}

//判断队列是否塞满
bool isFullQueue(Queue* &Q, Datatype data) {
	if(!Q) return false;
	if(Q->rear == MAX_SIZE) {
		cout << "队列已满,不能将" << data << "排进队列" << endl;	
		return true;
	}
	return false;
}

//排进队列
void queueUp(Queue* &Q, Datatype data) {
	if(!Q) return;
	if(isFullQueue(Q, data)) return;
	Q->queue[Q->rear] = data;
	Q->rear++;
}

//删除队首: 方法一
bool deleteQueueFirst(Queue* &Q, int *data) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return false;
	}
	if(!data) return false;

	int i = Q->front;
	--Q->rear;
	*data = Q->queue[Q->front];
	while(i != Q->rear) {
		Q->queue[i] = Q->queue[i+1];
		i++;
	}
	return false;
}

//删除队首: 方法二
bool deleteQueueFirst2(Queue* &Q, int *data) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return false;
	}
	if(!data) return false;
	if(Q->front == Q->rear) {
		cout << "队列已到尽头!" << endl;
		return false;
	}

	*data = Q->queue[Q->front];
	Q->front++;
	return true;
}

//获取队首元素
bool getFirstElem(Queue* &Q, Datatype *data) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return false;
	}
	if(!data) return false;

	*data = Q->queue[Q->front];
	return true;
}

//队列的长度
int OueueLength(Queue* &Q) {
	if(!Q) return 0;
	return Q->rear - Q->front;
}

//清空队列
void deleteQueue(Queue* Q) {
	Q->front = Q->rear = 0;
}

//打印队列
void printfQueue(Queue* &Q) {
	if(!Q) return;
	cout << "打印队列:";
	if(Q->front == Q->rear) {
		cout << "队列已到尽头!" << endl;
		return ;
	}
	int i = Q->front;
	while(i != Q->rear) {
		cout << Q->queue[i] << " ";
		i++;
	}
	cout << endl;
}

int main(void) {
	Queue *Q = new Queue;
	int data;

	//初始化队列
	initQueue(Q);

	for(int i=0; i<7; i++) {
		queueUp(Q, i);
	}
	printfQueue(Q);

	for(int i=0; i<10; i++) {
		if(deleteQueueFirst2(Q, &data))
			cout << "出队的元素是:" << data << endl;

		if(getFirstElem(Q, &data))
			cout << "队首的元素是:" << data << endl;
		printfQueue(Q);
	}

	system("pause");
	return 0;
}

队列的算法实现2:使用链表

链式存储

队列的链式存储结构,其实就是线性表的单链表,只不过它只是尾进头出而已,我们把它简称为链队列。为了

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

在这里插入图片描述

定义方式为:

typedef int DataType; //队列中元素类型 

typedef struct _QNode { //结点结构 
DataType data; 
	struct _QNode *next; 
}QNode; 

typedef QNode * QueuePtr; 

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

空队列时,front 和 rear 都指向空。

在这里插入图片描述

#include <iostream>
#include <Windows.h>

using namespace std;

typedef int Datatype;	//队列中的数据类型
#define MAX_SIZE     5	//队列中的最大容量

typedef struct _QNode {
	Datatype data;
	struct _QNode *next;
}QNode;

typedef QNode* QueuePtr;

typedef struct _QueueLink {
	int length;		//队列长度
	QueuePtr front; //指向队首
	QueuePtr rear;	//指向队尾
}QueueLink;

//队列初始化
bool initQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	QL->length = 0;
	QL->front = QL->rear = NULL;
	return true;
}

//判断队列是否为空
bool isBlankQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(!QL->front) return true;
	return false;
}

//判断队列是否塞满
bool isFullQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(QL->length == MAX_SIZE) return true;
	return false;
}

//元素进入队列
bool enterElemQueueLink(QueueLink* &QL, Datatype data) {
	if(!QL) return false;
	if(isFullQueueLink(QL)) {
		cout << "队列已满!无法将元素" << data << "插入到队列中!" << endl;
		return false;
	}
	QNode* node = new QNode;
	node->data = data;

	if(isBlankQueueLink(QL)) {
		QL->front = QL->rear = node;
	}else {
		QL->rear->next = node;
		QL->rear = node;
	}
	node->next = NULL;
	QL->length++;
	return true;
}

//元素出队列
bool deleteElemQueueLink(QueueLink* &QL, Datatype *data) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}
	if(!data) return false;

	QNode *tmp = NULL;
	tmp = QL->front;

	QL->front = QL->front->next;
    if(!QL->front) QL->rear = NULL;
    
	*data = tmp->data;
	delete tmp;
	QL->length--;
	return true;
}

//获取队首元素
bool getFirstElemQueueLink(QueueLink* &QL, Datatype *data) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}
	if(!data) return false;

	*data = QL->front->data;
	return true;
}

//清空队列
void clearQueueLink(QueueLink* &QL) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return;
	}

	QNode *p = QL->front;

	while(p) {
		cout << "删除" << p->data << endl;
		QL->front = QL->front->next;
		delete p;
		 p = QL->front;
	}
	/*while(QL->front){ 
		QueuePtr tmp = QL->front->next; 
		delete QL->front; 
		QL->front = tmp; 
	}*/

	QL->front = QL->rear = NULL;
	QL->length = 0;

}

//队列长度
int getLengthQueueLink(QueueLink* QL) {
	if(!QL || isBlankQueueLink(QL)) return 0;

	int i=0;
	QueuePtr p = QL->front;
	while(p) {
		i++;
		p = p->next;
	}
	return i;
}

//打印队列
void printfQueueLink(QueueLink* &QL) {
	cout << "打印队列:";
	if(!QL) return;
	if(isBlankQueueLink(QL)) return;
	
	QNode *p = QL->front;
	while(p) {
		cout << p->data << " ";
		p = p->next;
	}

	cout << endl;
}

int main(void) {
	QueueLink *QL = new QueueLink;
	initQueueLink(QL);
	int x = -2;

	for(int i=0; i<7; i++) {
		if(enterElemQueueLink(QL, i)) {
			cout << "添加[" << i << "]成功!" << endl;
		}else {
			cout << "添加失败!" << endl;
		}
		printfQueueLink(QL);
		if(getFirstElemQueueLink(QL, &x)) {
			cout << "获取队首元素【" << x << "】,成功!" << endl;
		}else{
			cout << "获取队首元素失败" << endl;
		}
		cout << "队列长度:" << getLengthQueueLink(QL) << endl;
		cout << endl;
	}

	/*int data = -1;
	for(int i=0; i<7; i++) {
		if(deleteElemQueueLink(QL, &data)) {
			cout << data << "出队成功!" << endl;
		}else {
			cout << "出队失败!" << endl;
		}
		printfQueueLink(QL);
		if(getFirstElemQueueLink(QL, &x)) {
			cout << "获取队首元素【" << x << "】,成功!" << endl;
		}else{
			cout << "获取队首元素失败" << endl;
		}
		cout << "队列长度:" << getLengthQueueLink(QL) << endl;
		cout << endl;
	}*/
	clearQueueLink(QL);
	printfQueueLink(QL);
	delete QL;
	system("pause");
	return 0;
}

三,队列的企业级应用案例

一,线程池中的任务队列

线程池 - 由一个任务队列和一组处理队列的线程组成。一旦工作进程需要处理某个可能“阻塞”的

操作,不用自己操作,将其作为一个任务放到线程池的队列,接着会被某个空闲线程提取处理。

在这里插入图片描述

定义方式:

typedef struct _QNode { //结点结构 
    int id; 
    void (*handler)(); 
    struct _QNode *next; 
}QNode; 

typedef QNode * QueuePtr; 

typedef struct Queue { 
    int length; //队列的长度 
    QueuePtr front; //队头指针 
    QueuePtr rear; //队尾指针 
}LinkQueue;
#include <iostream>
#include <Windows.h>

using namespace std;

#define MAX_SIZE     5	//队列中的最大容量

typedef struct _QNode {
	int fd;
	void (*handler)();
	struct _QNode *next;
}QNode;

typedef QNode* QueuePtr;

typedef struct _QueueLink {
	int length;		//队列长度
	QueuePtr front; //指向队首
	QueuePtr rear;	//指向队尾
}QueueLink;

//分配线程中的任务节点
QueuePtr thread_task_alloc() {
	QNode *task = (QNode*)calloc(1, sizeof(QNode));;
	if(!task) return NULL;
	return task;
}

//队列初始化
bool initQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	QL->length = 0;
	QL->front = QL->rear = NULL;
	return true;
}

//判断队列是否为空
bool isBlankQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(!QL->front) return true;
	return false;
}

//判断队列是否塞满
bool isFullQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(QL->length == MAX_SIZE) return true;
	return false;
}

//元素进入队列
bool enterElemQueueLink(QueueLink* &QL, QNode *node) {
	if(!QL || !node) return false;
	if(isFullQueueLink(QL)) {
		cout << "队列已满!无法将任务:" << node->handler << "插入到队列中!" << endl;
		return false;
	}

	if(isBlankQueueLink(QL)) {
		QL->front = QL->rear = node;
	}else {
		QL->rear->next = node;
		QL->rear = node;
	}
	node->next = NULL;
	QL->length++;
	return true;
}

//元素出队列
QueuePtr PopQueueLink(QueueLink* &QL) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}

	QNode *tmp = NULL;
	tmp = QL->front;

	QL->front = tmp->next;
	if(!QL->front) QL->rear = NULL;

	QL->length--;
	return tmp;
}

//获取队首元素
QueuePtr getFirstElemQueueLink(QueueLink* &QL) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return NULL;
	}
	return QL->front;
}

//清空队列
void clearQueueLink(QueueLink* &QL) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return;
	}

	QNode *p = QL->front;

	while(p) {
		cout << "删除" << p->handler << endl;
		QL->front = QL->front->next;
		delete p;
		 p = QL->front;
	}
	/*while(QL->front){ 
		QueuePtr tmp = QL->front->next; 
		delete QL->front; 
		QL->front = tmp; 
	}*/

	QL->front = QL->rear = NULL;
	QL->length = 0;

}

//队列长度
int getLengthQueueLink(QueueLink* QL) {
	if(!QL || isBlankQueueLink(QL)) return 0;

	/*int i=0;
	QueuePtr p = QL->front;
	while(p) {
		i++;
		p = p->next;
	}*/
	return QL->length;
}

//打印队列
void printfQueueLink(QueueLink* &QL) {
	cout << "打印任务队列:";
	if(!QL) return;
	if(isBlankQueueLink(QL)) return;
	
	QNode *p = QL->front;
	while(p) {
		cout << p->fd << " ";
		p = p->next;
	}

	cout << endl;
}

void task1(){ 
	printf("我是任务 1 ...\n"); 
}

void task2(){ 
	printf("我是任务 2 ...\n"); 
}

int main(void) {
	QueueLink *QL = new QueueLink;

	//初始化队列
	initQueueLink(QL);

	//任务1入队
	QNode *task = thread_task_alloc();
	task->fd = 1;
	task->handler  = &task1;
	enterElemQueueLink(QL, task);

	//任务2入队
	task = thread_task_alloc();
	task->fd = 2;
	task->handler  = &task2;
	enterElemQueueLink(QL, task);

	cout << "队列中的任务长度为:" << getLengthQueueLink(QL) << endl;
	printfQueueLink(QL);
	cout << endl;

	//执行任务队列
	while((task=PopQueueLink(QL))) {
		task->handler();
		delete task;
	}

	delete QL;
	system("pause");
	return 0;
}

二,循环队列

循环队列

在队列的顺序存储中,采用出队方式 2, 删除 front 所指的元素,然后加 1 并返回被删元素。这样可以避免元素

移动,但是也带来了一个新的问题“假溢出”。

在这里插入图片描述

能否利用前面的空间继续存储入队呢?采用循环队列可以解决这个问题.

在这里插入图片描述

循环队列入队, 队尾循环后移SQ->rear =(SQ->rear+1)%MAX_SIZE;

循环队列出队, 队首循环后移SQ->front =(SQ->front+1)%MAX_SIZE;

队空SQ.front=SQ.rear; // SQ.rear 和 SQ.front 指向同一个位置

队满(SQ.rear+1) %MAX_SIZE=SQ.front; // SQ.rear 向后移一位正好是 SQ.front

计算元素个数:

可以分两种情况判断:

  • 如果 SQ.rear>= SQ.front:元素个数为 SQ.rear-SQ.front;

  • 如果 SQ.rear<SQ.front:元素个数为 SQ.rear-SQ.front+ MAX_SIZE;

采用取模的方法把两种情况统一为:(SQ.rear-SQ.front+MAX_SIZE)% MAX_SIZE

#include <iostream>
#include <Windows.h>

using namespace std;

typedef int Datatype;	//队列中的元素类型
#define MAX_SIZE 5		//队列最大容量

typedef struct _Queue {
	Datatype queue[MAX_SIZE];
	int front;
	int rear;
}Queue;

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

//判断队列是否为空
bool blankQueue(Queue* &Q) {
	if(!Q) return false;
	if(Q->rear == Q->front) return true;
	return false;
}

//判断队列是否塞满
bool isFullQueue(Queue* &Q) {
	if(!Q) return false;
	if((Q->rear + 1)%MAX_SIZE == Q->front) return true;
	return false;
}

//进入队列
void queueUp(Queue* &Q, Datatype data) {
	if(!Q) return;
	if(isFullQueue(Q)) {
	cout << "队列已满,不能将" << data << "排进队列" << endl;	
		return;
	}

	Q->queue[Q->rear] = data;
	Q->rear = (Q->rear + 1)%MAX_SIZE;
}

//删除队首
bool deleteQueueFirst(Queue* &Q, int *data) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return false;
	}
	if(!data) return false;
	
	*data = Q->queue[Q->front];
	Q->front = (Q->front+ 1)%MAX_SIZE;
	return true;
}

//获取队首元素
bool getFirstElem(Queue* &Q, Datatype *data) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return false;
	}
	if(!data) return false;

	*data = Q->queue[Q->front];
	return true;
}

//队列的长度
int OueueLength(Queue* &Q) {
	if(!Q) return 0;
	return (Q->rear - Q->front + MAX_SIZE)%MAX_SIZE;
}

//清空队列
void deleteQueue(Queue* Q) {
	Q->front = Q->rear = 0;
}

//打印队列
void printfQueue(Queue* &Q) {
	if(!Q || blankQueue(Q)){
		cout << "队列为空!" << endl;
		return ;
	}
	cout << "打印队列:";
	
	int i = Q->front;
	while(i != Q->rear) {
		cout << Q->queue[i] << " ";
		i = (i + 1)%MAX_SIZE;
	}
	cout << endl;
}

int main(void) {
	Queue *Q = new Queue;
	int data;

	//初始化队列
	initQueue(Q);

	for(int i=0; i<7; i++) {
		queueUp(Q, i);
	}
	cout << "队列长度:" << OueueLength(Q) << endl;
	printfQueue(Q);

	cout << endl;

	for(int i=0; i<5; i++) {
		if(deleteQueueFirst(Q, &data))
			cout << "出队的元素是:" << data << endl;

		if(getFirstElem(Q, &data))
			cout << "队首的元素是:" << data << endl;
		cout << "队列长度:" << OueueLength(Q) << endl;
		printfQueue(Q);
		cout << endl;
	}

	for(int i=0; i<7; i++) {
		queueUp(Q, i);
	}
	cout << "队列长度:" << OueueLength(Q) << endl;
	printfQueue(Q);

	cout << endl;

	for(int i=0; i<5; i++) {
		if(deleteQueueFirst(Q, &data))
			cout << "出队的元素是:" << data << endl;

		if(getFirstElem(Q, &data))
			cout << "队首的元素是:" << data << endl;
		cout << "队列长度:" << OueueLength(Q) << endl;
		printfQueue(Q);
		cout << endl;
	}
	
    delete Q;
    
	system("pause");
	return 0;
}

三,优先队列

优先队列

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

优先出队。

定义方式为:

typedef int Datatype;	//队列中的数据类型
#define MAX_SIZE     5	//队列中的最大容量

typedef struct _QNode {
	int priority;	//每个节点的优先级,9 最高优先级,0 最低优先级,优先级相同, 取第一个节点
	Datatype data;
	struct _QNode *next;
}QNode;

typedef QNode* QueuePtr;

typedef struct _QueueLink {
	int length;		//队列长度
	QueuePtr front; //指向队首
	QueuePtr rear;	//指向队尾
}QueueLink;

在链表队列中,主要改动出列的代码,改动之后如下:

//元素出队列
bool deleteElemQueueLink(QueueLink* &QL, Datatype *data) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}
	if(!data) return false;

	QNode **prev = NULL, *prev_node = NULL;
	QNode *tmp = NULL, *last = NULL;

	prev = &(QL->front);
	cout << "第一个结点的优先级是: " << (*prev)->priority << endl;
	
	last = QL->front;
	tmp = last->next;

	while(tmp) {
		if(tmp->priority > (*prev)->priority) {
			cout << "抓到一个更大的优先级:" << tmp->priority << endl;
			prev = &(last->next);
			prev_node = last;
		}
		last = tmp;
		tmp = tmp->next;
	}

	tmp = *prev;
	*prev = (*prev)->next;
	*data = tmp->data;
	delete tmp;
	QL->length--;

	//如果只有一个节点
    if(!QL->length) QL->rear = NULL;

	//如果删除的是最后一个节点
	//if(prev_node && !prev_node->next) QL->rear = prev_node; 与下面这个写法可以达到相同的效果
	if(!*prev) QL->rear = prev_node;
    
	return true;
}

完整代码:

#include <iostream>
#include <Windows.h>

using namespace std;

typedef int Datatype;	//队列中的数据类型
#define MAX_SIZE     5	//队列中的最大容量

typedef struct _QNode {
	int priority;	//优先级
	Datatype data;
	struct _QNode *next;
}QNode;

typedef QNode* QueuePtr;

typedef struct _QueueLink {
	int length;		//队列长度
	QueuePtr front; //指向队首
	QueuePtr rear;	//指向队尾
}QueueLink;

//队列初始化
bool initQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	QL->length = 0;
	QL->front = QL->rear = NULL;
	return true;
}

//判断队列是否为空
bool isBlankQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(!QL->front) return true;
	return false;
}

//判断队列是否塞满
bool isFullQueueLink(QueueLink* &QL) {
	if(!QL) return false;
	if(QL->length == MAX_SIZE) return true;
	return false;
}

//元素进入队列
bool enterElemQueueLink(QueueLink* &QL, Datatype data, int priority) {
	if(!QL) return false;
	if(isFullQueueLink(QL)) {
		cout << "队列已满!无法将元素" << data << "插入到队列中!" << endl;
		return false;
	}
	QNode* node = new QNode;
	node->data = data;
	node->priority = priority;

	if(isBlankQueueLink(QL)) {
		QL->front = QL->rear = node;
	}else {
		QL->rear->next = node;
		QL->rear = node;
	}
	node->next = NULL;
	QL->length++;
	return true;
}

//元素出队列
bool deleteElemQueueLink(QueueLink* &QL, Datatype *data) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}
	if(!data) return false;

	QNode **prev = NULL, *prev_node = NULL;
	QNode *tmp = NULL, *last = NULL;

	prev = &(QL->front);
	cout << "第一个结点的优先级是: " << (*prev)->priority << endl;
	
	last = QL->front;
	tmp = last->next;

	while(tmp) {
		if(tmp->priority > (*prev)->priority) {
			cout << "抓到一个更大的优先级:" << tmp->priority << endl;
			prev = &(last->next);
			prev_node = last;
		}
		last = tmp;
		tmp = tmp->next;
	}

	tmp = *prev;
	*prev = (*prev)->next;
	*data = tmp->data;
	delete tmp;
	QL->length--;

	//如果只有一个节点
    if(!QL->length) QL->rear = NULL;

	//如果删除的是最后一个节点
	//if(prev_node && !prev_node->next) QL->rear = prev_node; 与下面这个写法可以达到相同的效果
	if(!*prev) QL->rear = prev_node;
    
	return true;
}

//获取队首元素
bool getFirstElemQueueLink(QueueLink* &QL, Datatype *data) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return false;
	}
	if(!data) return false;

	*data = QL->front->data;
	return true;
}

//清空队列
void clearQueueLink(QueueLink* &QL) {
	if(!QL || isBlankQueueLink(QL)) {
		cout << "队列为空" << endl;
		return;
	}

	QNode *p = QL->front;

	while(p) {
		cout << "删除" << p->data << endl;
		QL->front = QL->front->next;
		delete p;
		 p = QL->front;
	}
	/*while(QL->front){ 
		QueuePtr tmp = QL->front->next; 
		delete QL->front; 
		QL->front = tmp; 
	}*/

	QL->front = QL->rear = NULL;
	QL->length = 0;

}

//队列长度
int getLengthQueueLink(QueueLink* QL) {
	if(!QL || isBlankQueueLink(QL)) return 0;

	/*int i=0;
	QueuePtr p = QL->front;
	while(p) {
		i++;
		p = p->next;
	}
	return i;*/
	return QL->length;
}

//打印队列
void printfQueueLink(QueueLink* &QL) {
	cout << "打印队列:";
	if(!QL) return;
	if(isBlankQueueLink(QL)) return;
	
	QNode *p = QL->front;
	while(p) {
		cout << p->data <<"["<< p->priority <<"]" << " ";
		p = p->next;
	}

	cout << endl;
}

int main(void) {
	QueueLink *QL = new QueueLink;
	initQueueLink(QL);
	int x = -2;


	if(enterElemQueueLink(QL, 8, 6)) {
			cout << "添加[" << 8 << "]成功!" << endl;
		}else {
			cout << "添加失败!" << endl;
		}
		printfQueueLink(QL);

		if(enterElemQueueLink(QL, 0, 9)) {
			cout << "添加[" << 0 << "]成功!" << endl;
		}else {
			cout << "添加失败!" << endl;
		}
		printfQueueLink(QL);

	for(int i=0; i<7; i++) {
		if(enterElemQueueLink(QL, i+10, i)) {
			cout << "添加[" << i+10 << "]成功!" << endl;
		}else {
			cout << "添加失败!" << endl;
		}
		printfQueueLink(QL);
		if(getFirstElemQueueLink(QL, &x)) {
			cout << "获取队首元素【" << x << "】,成功!" << endl;
		}else{
			cout << "获取队首元素失败" << endl;
		}
		cout << "队列长度:" << getLengthQueueLink(QL) << endl;
		cout << endl;
	}

	int data = -1;
	for(int i=0; i<7; i++) {
		if(deleteElemQueueLink(QL, &data)) {
			cout << data << "出队成功!" << endl;
		}else {
			cout << "出队失败!" << endl;
		}
		printfQueueLink(QL);
		if(getFirstElemQueueLink(QL, &x)) {
			cout << "获取队首元素【" << x << "】,成功!" << endl;
		}else{
			cout << "获取队首元素失败" << endl;
		}
		cout << "队列长度:" << getLengthQueueLink(QL) << endl;
		cout << endl;
	}
	clearQueueLink(QL);
	printfQueueLink(QL);

	delete QL;
	system("pause");
	return 0;
}

四,动态顺序队列

使用链表动态存储的队列即为动态顺序队列,前面已经实现,故不再重复!

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


在高并发 HTTP 反向代理服务器 Nginx 中,存在着一个跟性能息息相关的模块 - 文件缓存。

在这里插入图片描述

经常访问到的文件会被 nginx 从磁盘缓存到内存,这样可以极大的提高 Nginx 的并发能力,不过因为

内存的限制,当缓存的文件数达到一定程度的时候就会采取淘汰机制,优先淘汰进入时间比较久或是最近

访问很少(LRU)的队列文件。

具体实现方案:

  1. 使用双向循环队列保存缓存的文件节点,这样可以实现多种淘汰策略:

比如:如果采用淘汰进入时间比较久的策略,就可以使用队列的特性,先进先出

如果要采用按照 LRU,就遍历链表,找到节点删除。

nginx_queue.h

#ifndef _NGX_QUEUE_H_INCLUDED_ 
#define _NGX_QUEUE_H_INCLUDED_ 

typedef struct ngx_queue_s ngx_queue_t; 

struct ngx_queue_s { 
    	ngx_queue_t *prev; 
        ngx_queue_t *next; 
};

#define ngx_queue_init(q) \ 
		(q)->prev = q; \ 
    	(q)->next = q 
        
#define ngx_queue_empty(h) \ 
        (h == (h)->prev) 
        
#define ngx_queue_insert_head(h, x) \ 
        (x)->next = (h)->next; \ 
        (x)->next->prev = x; \ 
        (x)->prev = h; \ 
        (h)->next = x 

#define ngx_queue_insert_after 
        ngx_queue_insert_head 
        
#define ngx_queue_insert_tail(h, x) \ 
        (x)->prev = (h)->prev; \ 
        (x)->prev->next = x; \ 
        (x)->next = h; \ 
        (h)->prev = x 
        
#define ngx_queue_head(h) \ 
        (h)->next 
        
#define ngx_queue_last(h) \ 
        (h)->prev

#define ngx_queue_sentinel(h) \ 
        (h) 
        
#define ngx_queue_next(q) \ 
        (q)->next 
        
#define ngx_queue_prev(q) \ 
        (q)->prev 
        
#define ngx_queue_remove(x) \ 
        (x)->next->prev = (x)->prev; \ 
        (x)->prev->next = (x)->next 
        
#define ngx_queue_data(q, type, link) \ 
        (type *) ((char *) q - offsetof(type, link)) 
#endif

Nginx_双向循环队列.cpp

#include <Windows.h> 
#include <stdlib.h> 
#include <iostream> 
#include "nginx_queue.h" 
#include <time.h> 

using namespace std; 

typedef struct ngx_cached_open_file_s { 
    //其它属性省略... 
    int fd; 
    ngx_queue_t queue; 
}ngx_cached_file_t;

typedef struct { 
    //其它属性省略... 
    ngx_queue_t expire_queue; 
    //其它属性省略... 
} ngx_open_file_cache_t; 

int main(void){ 
    ngx_open_file_cache_t *cache = new ngx_open_file_cache_t; 
    ngx_queue_t *q; 
    ngx_queue_init(&cache->expire_queue); //1. 模拟文件模块,增加打开的文件到缓存中 
    
    for(int i=0; i<10; i++){ 
        ngx_cached_file_t *e = new ngx_cached_file_t; 
        e->fd = i; 
        ngx_queue_insert_head(&cache->expire_queue, &e->queue); 
    }
    
    //遍历队列 
    for(q=cache->expire_queue.next; q!=ngx_queue_sentinel(&cache->expire_queue); 
        q=q->next){ 
        
        printf("队列中的元素:%d\n", (ngx_queue_data(q, ngx_cached_file_t, queue))->fd); 	  }
    
    //模拟缓存的文件到期,执行出列操作 
    while(!ngx_queue_empty(&cache->expire_queue)){ 
        q=ngx_queue_last(&cache->expire_queue); 
        ngx_cached_file_t *cached_file = ngx_queue_data(q, ngx_cached_file_t, queue); 		  printf("出队列中的元素:%d\n", cached_file->fd); ngx_queue_remove(q); 				delete(cached_file); 
    }
    system("pause"); 
    return 0; 
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

队列算法的原理和实现,及其企业级应用 的相关文章

  • 使用 Unity 在构造函数中使用属性依赖注入

    好的 我在基类中定义了一个依赖属性 我尝试在其派生类的构造函数内部使用它 但这不起作用 该属性显示为 null Unity 在使用 container Resolve 解析实例后解析依赖属性 我的另一种选择是将 IUnityContaine
  • Unix网络编程澄清

    我正在翻阅这本经典书籍Unix网络编程 https rads stackoverflow com amzn click com 0139498761 当我偶然发现这个程序时 第 6 8 节 第 179 180 页 include unp h
  • 向 Nhibernate 发出 SQL 查询

    如何将此 SQL 查询发送给 Nhibernate SELECT Customer name FROM Company INNER JOIN Customer ON Company CompanyId Customer CompanyId
  • 如何修复此错误“GDI+ 中发生一般错误”?

    从默认名称打开图像并以默认名称保存 覆盖它 我需要从 Image Default jpg 制作图形 将其放在 picturebox1 image 上并在 picurebox1 上绘制一些图形 它有效 这不是我的问题 但我无法保存 pictu
  • 在新的浏览器进程中打开 URL

    我需要在新的浏览器进程中打开 URL 当浏览器进程退出时我需要收到通知 我当前使用的代码如下 Process browser new Process browser EnableRaisingEvents true browser Star
  • C++中的类查找结构体数组

    我正在尝试创建一个结构数组 它将输入字符串链接到类 如下所示 struct string command CommandPath cPath cPathLookup set an alarm AlarmCommandPath send an
  • 使用 C 语言使用 strftime() 获取缩写时区

    我看过this https stackoverflow com questions 34408909 how to get abbreviated timezone and this https stackoverflow com ques
  • 如何在 C# 中定义文本框数组?

    您好 当我在 Windows 申请表上创建文本框时 我无法将其命名为 box 0 box 1 等 我这样做的目的是因为我想循环使用它们 其实我发现TextBox array firstTextBox secondTextBox 也有效
  • 如何在 Linq 中获得左外连接?

    我的数据库中有两个表 如下所示 顾客 C ID city 1 Dhaka 2 New york 3 London 个人信息 P ID C ID Field value 1 1 First Name Nasir 2 1 Last Name U
  • 未定义的行为或误报

    我 基本上 在野外遇到过以下情况 x x 5 显然 它可以在早期版本的 gcc 下编译干净 在 gcc 4 5 1 下生成警告 据我所知 警告是由 Wsequence point 生成的 所以我的问题是 这是否违反了标准中关于在序列点之间操
  • 上下文敏感与歧义

    我对上下文敏感性和歧义如何相互影响感到困惑 我认为正确的是 歧义 歧义语法会导致使用左推导或右推导构建多个解析树 所有可能的语法都是二义性的语言是二义性语言 例如 C 是一种不明确的语言 因为 x y 总是可以表示两个不同的事物 如下所述
  • 等待线程完成

    private void button1 Click object sender EventArgs e for int i 0 i lt 15 i Thread nova new Thread Method nova Start list
  • 私有模板函数

    我有一堂课 C h class C private template
  • std::async 与重载函数

    可能的重复 std bind 重载解析 https stackoverflow com questions 4159487 stdbind overload resolution 考虑以下 C 示例 class A public int f
  • 用于 C# 的 TripleDES IV?

    所以当我说这样的话 TripleDES tripledes TripleDES Create Rfc2898DeriveBytes pdb new Rfc2898DeriveBytes password plain tripledes Ke
  • Process.Start() 方法在什么情况下返回 false?

    From MSDN https msdn microsoft com en us library e8zac0ca v vs 110 aspx 返回值 true 表示有新的进程资源 开始了 如果由 FileName 成员指定的进程资源 St
  • 有没有办法强制显示工具提示?

    我有一个验证字段的方法 如果无法验证 该字段将被清除并标记为红色 我还希望在框上方弹出一个工具提示 并向用户显示该值无效的消息 有没有办法做到这一点 并且可以控制工具提示显示的时间 我怎样才能让它自己弹出而不是鼠标悬停时弹出 If the
  • 如何将 Roslyn 语义模型返回的类型符号名称与 Mono.Cecil 返回的类型符号名称相匹配?

    我有以下代码 var paramDeclType m semanticModel GetTypeInfo paramDecl Type Type Where paramDeclType ToString returns System Col
  • 当另一个线程可能设置共享布尔标志(最多一次)时,是否可以读取共享布尔标志而不锁定它?

    我希望我的线程能够更优雅地关闭 因此我尝试实现一个简单的信号机制 我不认为我想要一个完全事件驱动的线程 所以我有一个工作人员有一种方法可以使用关键部分优雅地停止它Monitor 相当于C lock我相信 绘图线程 h class Drawi
  • 防止在工厂方法之外实例化对象

    假设我有一个带有工厂方法的类 class A public static A newA Some code logging return new A 是否可以使用 a 来阻止此类对象的实例化new 那么工厂方法是创建对象实例的唯一方法吗 当

随机推荐

  • 可信执行环境(TEE):深入探讨安全计算的未来

    摘要 本文将详细介绍可信执行环境 TEE 的概念 原理和功能 我们将讨论TEE的应用场景 以及如何使用TEE来保护敏感数据和代码的安全 此外 我们还将探讨TEE的挑战和未来发展 1 引言 随着计算设备的普及和云计算技术的快速发展 如何保护数
  • python b 'string'

    str literals a sequence of Unicode characters UTF 16 or UTF 32 depending on how Python was compiled bytes b literals a s
  • 软件测试之自动化测试

    目录 1 什么是自动化测试 2 selenium java环境搭建 3 熟悉selenium的API 定位元素 添加等待 打印信息 浏览器的相关操作 键盘组合键用法 鼠标事件 特殊场景 定位一组元素 多层框架定位 下拉框处理 弹窗处理 上传
  • MATLAB算法实战应用案例精讲-【深度学习】归一化

    目录 归一化基础知识点 1 什么是归一化 2 为什么要归一化
  • Linux中部署软件时提示空间不足的应急方案

    1 df h 查看剩余空间 2 我们看到 home 下面的空间有很多 注 如果 home 下的控件时充足 且可以分配出去多余的空间 可以继续往下看 如自己 不够用或不够往外分配 可以外接新硬盘 3 在根号下执行 cp r home home
  • 【深度学习标注数据处理】imgaug Augment Polygons 对标注图片和 polygon 的数据增强

    对于本地化进行图像的增强 大家都是非常好操作的 但是 对于标注信息一起增强 还是稍微有一些难度的 麻烦很多 我是遇到一个数据集非常少的任务 只有40张图 就直接标记了去训练 发现几乎不拟合 当然这里使用的是yolo v8 而不是UNet 于
  • Gson的使用

    1 添加Gson 库 右键app open module settings dependncies com goolel code gson gson 2 2 4 2 对象转Json 保存至文件 使用Misc中的方法 Misc gson s
  • 3.2 图像分类

    文章目录 LeNet 小图像 LeNet在手写数字识别上的应用 LeNet在眼疾识别数据集iChallenge PM上的应用 数据集准备 查看数据集图片 定义数据读取器 启动训练 AlexNet 大图像 VGG 深度 GoogLeNet 深
  • 基于变分模态分解和麻雀算法优化长短期记忆网络的多维时间序列预测,VMD-SSA-LSTM多维时间序列预测。MATLAB代码(含LSTM、VMD-LSTM、VMD-SSA-LSTM三个模型的对比)

    clc clear all close all VMD SSA LSTM多维时间序列预测 tic load data mat load vmd data mat load LSTM mat disp disp VMD SSA LSTM预测
  • sqli-labs/Less-18

    这一关和前面的所有关卡都不一样 我们试一试先成功登录进去看看 结果除了iD地址之外还有一个信息回显了 那就是user agent所以我们抓包试一试 抓包后再user agent注入试试看 我尝试了许多注入方法 发现大部分方法都不能看出我注入
  • jdk、jre环境变量配置

    1 jdk和jre的区别 jdk Java 开发工具包 jre Java 的运行环境 只需这么记就可以了 想深入了解得自行查询相关资料 2 jdk是包含jre的 所以只需下载jdk 官方网址 https www oracle com cn
  • QT样式翻译

    Qt4 7文档翻译 Qt样式单参考 Qt Style Sheets Reference 转载于 http 2845385 blog 51cto com 2835385 1080560 0 tsina 1 14777 397232819ff9
  • 深入理解warp shuffle

    warp shuffle 相关函数学习 shfl up sync 0xffffffff lane val i 是CUDA函数之一 用于在线程束内的线程之间交换数据 其中 0xffffffff是掩码参数 指示线程束内所有线程都参与数据交换 一
  • C++线程处理函数的返回值

    引言 关于线程处理函数 常见的可能是返回值为void类型 那线程处理函数是否能定义自己想要的返回值类型呢 这里请看下面的说明 C 线程返回值 应用环境 1 传统的方式获取线程返回值 2 使用C Promise和future方式 3 prom
  • 服务器怎么和网站接入,网站服务器的带宽怎么接入呢?

    zzzooo 当下许多企业为了将事务上云 开端租借效劳器建立网站 可是因为各个运营商机房种类都不相同 带宽线路也有区别 许多用户不清楚网站效劳器的带宽要怎样接入 今天小编就和我们聊一聊当下商场几种比较抢手的几种带宽接入方式 一 BGP多线带
  • 使用请求队列实验

    关于块设备架构就讲解这些 接下来我们使用开发板上的 RAM 模拟一段块设备 也就是ramdisk 然后编写块设备驱动 首先是传统的使用请求队列的时候 也就是针对机械硬盘的时候如何编写驱动 先分段分析一下驱动代码 1 重要的数据结构及宏定义
  • 简单好用的小控件------自定义checkbox

    1 首先在drawable文件夹中添加drawable文件checkbox style xml html view plain copy
  • Centos7关闭防火墙时遇到的错误Failed to start firewalld.service: Unit not found. Unit firewalld.service could no

    Centos7关闭防火墙 今天在centos上想要关闭防火墙 查了一些博客 执行命令 systemctl status firewalld service 时报错Unit firewalld service could not be fou
  • 常见缺少msvcp140.dll问题及解决方法,分享多种方法帮你解决

    在日常使用电脑的过程中 我们可能会遇到各种问题 比如电脑提示msvcp140 dll文件丢失 这个问题通常是由于某些程序或游戏需要这个dll文件来正常运行 但是由于某种原因 这个文件被误删或者损坏了 那么 如何解决这个问题呢 本文将为您提供
  • 队列算法的原理和实现,及其企业级应用

    目录 一 队列的原理 二 队列的算法实现 队列的算法实现1 使用数组 队列的算法实现2 使用链表 三 队列的企业级应用案例 一 线程池中的任务队列 二 循环队列 三 优先队列 四 动态顺序队列 五 高并发 WEB 服务器队列的应用 一 队列