队列

队列
队列是一种受限的线性表,(Queue),它是一种运算受限的线性表,规则是先进先出;
1.队列是一种受限的线性结构
2.它只允许在表的前端(front)进行删除,在表的后端(rear)进行插入操作;

数组实现队列:

#include <iostream>
#include <string>

using namespace std;

//创建一个数组队列
typedef int DATA_type;

#define MAX_SIZE 5

typedef struct{
	DATA_type data[MAX_SIZE];//数组
	int fount;//第一个
	int reat;//最后一个
}Que;

//初始化数组队列
bool Init_Que(Que* sq) {

	if(!sq){
		return false;
	}

	sq->fount = sq->reat = 0;

	return true;
}

//判断数组队列是否为空
bool Judge_Que_NULL(Que* sq) {
	
	if (!sq) {
		return false;
	}

	if (sq->fount == sq->reat) {
		return true;
	}
	return false;
}

//判断数组队列是否已满
bool Judge_Que_FULL(Que* sq) {

	if (!sq) {
		return false;
	}

	sq->fount = (sq->reat + 1) % MAX_SIZE;

	return true;
}

//打印数组队列
void Print_Que(Que* sq) {

	if (!sq) {
		return;
	}
	
	for (int i = sq->fount; i < sq->reat; i++) {
		cout << sq->data[i] << ",";
	}
	cout << endl;
}

//入队
bool Insert_Que(Que* sq, DATA_type elem) {

	if (!sq) {
		return false;
	}
	
	if (Judge_Que_FULL(sq)) {
		return false;
	}
	
	sq->data[sq->reat] = elem;
	sq->reat++;

	return true;
}

//出队
bool Delete_Que_fout(Que* sq, DATA_type* data) {
	
	if (!sq || Judge_Que_NULL(sq)) {
		return false;
	}

	if (sq->fount ==sq->reat) {
		return false;
	}

	*data = sq->data[sq->fount];
	sq->fount = sq->fount + 1;

	return true;
}

//出队
bool Delete_Que_back(Que* sq, DATA_type* data) {
	
	if (!sq || Judge_Que_NULL(sq)) {
		return false;
	}

	if (!data) {
		return false;
	}

	*data = sq->data[sq->fount];

	for (int i = sq->fount + 1; i < sq->reat; i++) {
		sq->data[i - 1] = sq->data[i];
	}

	sq->reat--;

	return true;
}

//清空队列
void Clear_Que(Que* sq) {
	
	if (!sq) {
		return;
	}
	sq->fount = sq->reat = 0;
}

//获取首元素的数据
int Getfount(Que* sq) {
	
	if (!sq) {
		return 0;
	}

	return sq->data[sq->fount];
}

//获取队列中的元素个数
int Getlength(Que* sq) {

	if (!sq) {
		return 0;
	}

	return sq->reat - sq->fount;
}

链表实现队列:

//创建一个链表队列,链表队列也叫动态队列
typedef int DataType;//队列中的元素

typedef struct _QNode{
	//队列节点的结构体
	DataType data;
	struct _QNode* next;
}QNode;

typedef QNode* QuePtr;//使用类型转换方便后期维护

typedef struct {
	int length;//队列的长度
	QuePtr front;//队列头指针
	QuePtr rear;//队列尾指针
}LinkQue;

//初始化队列链表
void initQue(LinkQue* LQ) {
	
	if (!LQ) {
		return;
	}

	LQ->length = 0;
	LQ->front = LQ->rear = NULL;//头尾指针同时为空
}

//判断队列是否为空
int IsEmpty(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if (LQ->front == NULL) {
		return 1;
	}

	return 0;
}

//判断队列是否为满
int IsFull(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if (LQ->length == MAX_SIZE) {
		return 1;
	}

	return 0;
}

//入队
int EnterQue(LinkQue* LQ, DataType data) {

	if (!LQ) {
		return 0;
	}

	if (IsFull(LQ)) {
		return 0;
	}

	//定义一个新节点
	QNode* node = new QNode;
	node->data = data;
	node->next = NULL;

	if (IsEmpty(LQ)) {//空队列
		LQ->front = LQ->rear = node;
	}
	else {
		LQ->rear->next = node;//在队尾插入节点node
		LQ->rear = node;//队尾指向新插入的节点
	}
	LQ->length++;

	return 1;
}

//出队
int DeleteQue(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ)||!data) {
		return 0;
	}

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

	LQ->front = tmp->next;
	if (!LQ->front) {
		//如果队头出列后不存在其他元素,rear节点也要置空
		LQ->rear = NULL;
	}

	data = tmp->data;
	LQ->length--;

	delete[] tmp;

	return 1;
}

//打印队列中的元素
void PrintQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	if (LQ->front == NULL) {
		cout << "队列为空!" << endl;
		return;
	}

	QuePtr tmp;
	tmp = LQ->front;
	while (tmp) {
		cout << tmp->data << ",";
		tmp = tmp->next;
	}
	cout << endl;
}

//获取队首元素
int GetHead(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ)||!data) {
		return 0;
	}

	data = LQ->front->data;

	return 1;
}

//获取队尾元素
int GetEnd(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	data = LQ->rear->data;

	return 1;
}

//清空队列
void ClearQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	while (LQ->front) {
		//定义一个临时指针变量,指向队首的下一个节点
		QuePtr tmp = LQ->front->next;
		//释放队首的节点
		delete[] LQ->front;
		//继续往后清空
		LQ->front = tmp;
	}

	LQ->front = LQ->rear = NULL;
	LQ->length = 0;
}

//获取队列中的元素个数
int GetLength(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	return LQ->length;
}

队列在企业级应用的案例:
任务队列
线程池中的任务队列: 线程池是由任务队列和一组处理队列的线程组成,一旦工作进程需要处理某个可能"阻塞"的操作,不用自己操作,将其作为一个任务放到线程池的队列,接着会被某个空闲线程提取处理;
模拟代码:

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

using namespace std;

//队列的最大容量
#define MAX_SZIE 64

//任务节点的结构体
typedef struct _QNode {
	int id;//任务的编号
	void (*handler)(void);//执行的任务函数
	struct _QNode* next;//指向下一个节点的指针
}QNode;

typedef QNode* QuePtr;

//队列的结构体
typedef struct Que {
	int length;//队列的长度
	QuePtr front;//队首指针
	QuePtr rear;//队尾指针
}LinkQue;

//分配线程执行的任务节点
QuePtr thread_task() {
	QNode* task;
	task = new QNode;
	if (!task) {
		return NULL;
	}
	return task;
}

//队列初始化,将队列初始化为空队列
void initQue(LinkQue* LQ) {
	if (!LQ) {
		return;
	}
	LQ->length = 0;
	LQ->front = LQ->rear = NULL;
}

//判断队列是否为空
int IsNull(LinkQue* LQ) {
	if (!LQ) {
		return 0;
	}
	if (LQ->front == NULL) {
		return 1;
	}
	return 0;
}

//判断队列是否已满
int IsFull(LinkQue* LQ) {
	if (!LQ) {
		return 0;
	}
	if (LQ->length == MAX_SZIE) {
		return 1;
	}
	return 0;
}

//入队,将元素data插入到队列LQ中
int EnterQue(LinkQue* LQ, QNode* data) {
	if (!LQ || !data) {
		return 0;
	}
	if (IsFull(LQ)) {
		return 0;
	}
	data->next = NULL;
	if (IsNull(LQ)) {
		LQ->front = LQ->rear = data;
	}
	else {
		LQ->rear->next = data;
		LQ->rear = data;
	}
	LQ->length++;
	return 1;
}

//出队,将队列中队头的节点出队,返回头节点
QNode* PopQue(LinkQue* LQ) {
	QNode* tmp = NULL;
	if (!LQ || IsNull(LQ)) {
		return 0;
	}
	tmp = LQ->front;
	LQ->front = tmp->next;
	if (!LQ->front) {
		LQ->rear = NULL;
	}
	LQ->length--;
	return tmp;
}

//打印队列中的各元素
void PrintQue(LinkQue* LQ) {
	QuePtr tmp;
	if (!LQ) {
		return;
	}
	if (LQ->front == NULL) {
		return;
	}
	tmp = LQ->front;
	while (tmp) {
		cout << tmp->id << ",";
		tmp = tmp->next;
	}
	cout << endl;
}

//获取队列中的元素个数
int GetLength(LinkQue* LQ) {
	if (!LQ) {
		return 0;
	}
	return LQ->length;
}

//测试任务
void test1() {
	cout << "测试任务1" << endl;
}

void test2() {
	cout << "测试任务2" << endl;
}

void test3() {
	cout << "测试任务3" << endl;
}

//测试函数
int main4(void) {
	LinkQue* LQ = new LinkQue;
	QNode* task = NULL;

	initQue(LQ);

	//任务1入队
	task = thread_task();
	task->id = 1;
	task->handler = &test1;
	EnterQue(LQ, task);

	//任务2入队
	task = thread_task();
	task->id = 2;
	task->handler = &test2;
	EnterQue(LQ, task);

	//任务3入队
	task = thread_task();
	task->id = 3;
	task->handler = &test3;
	EnterQue(LQ, task);

	cout << "队列中的元素总数:" << GetLength(LQ) << endl;
	PrintQue(LQ);

	//执行任务
	while (task = PopQue(LQ)) {
		task->handler();
		cout << "删除:" << task->id <<endl;
		delete[] task;
	}

	delete LQ;

	system("pause");
	return 0;
}

循环队列
在队列的顺序存储中,采用出队方式2,删除front所指的元素,然后加1并返回被删元素,这样可以避免元素移动,但是也带来一个新的问题 “假溢出” (就是前面删除的元素一直往后移动,导致队列空间越来越小),这样清空使用循环队列就可以把前面删除掉元素的空间继续存储,元素可以不断地进出;

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

using namespace std;

#define MAX_SIZE 15

//创建一个循环队列
typedef int DataType;//队列中的元素

typedef struct {
	DataType que[MAX_SIZE];
	DataType front;//队列头指针
	DataType rear;//队列尾指针
}LinkQue;

//初始化循环队列链表
void InitQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}
	LQ->front = LQ->rear = NULL;//头尾指针同时为空
}

//判断队列是否为空
int IsEmpty(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if (LQ->front == LQ->rear) {
		return 1;
	}

	return 0;
}

//判断队列是否为满
int IsFull(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if ((LQ->rear + 1) % MAX_SIZE == LQ->front) {
		return 1;
	}

	return 0;
}

//入队
int EnterQue(LinkQue* LQ, DataType data) {

	if (!LQ) {
		return 0;
	}

	if (IsFull(LQ)) {
		return 0;
	}

	LQ->que[LQ->rear] = data;//在队尾插入元素
	LQ->rear = (LQ->rear + 1) % MAX_SIZE;//队尾指针后移一位

	return 1;
}

//出队
int DeleteQue(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	data = LQ->que[LQ->front];//出队的元素值
	LQ->front = (LQ->front + 1) % MAX_SIZE;//队首指针后移一位

	return 1;
}

//打印队列中的元素
void PrintQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	DataType tmp;
	tmp = LQ->front;
	cout << "元素有:";
	while (tmp!=LQ->rear) {
		cout << LQ->que[tmp] << ",";
		tmp = (tmp + 1) % MAX_SIZE;
	}
	cout << endl;
}

//获取队首元素
int GetHead(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	return data = LQ->que[LQ->front];
}

//获取队尾元素
int GetEnd(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	return data = LQ->que[LQ->rear];
}

//清空队列
void ClearQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	LQ->front = LQ->rear = NULL;
}

//获取队列中的元素个数
int GetLength(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	return (LQ->rear - LQ->front + MAX_SIZE) % MAX_SIZE;
}

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

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

using namespace std;

#define MAX_SIZE 15

//创建一个链表队列
typedef int DataType;//队列中的元素

typedef struct _QNode {
	int priorrty;//每个节点的优先级,按高到低
	DataType data;
	struct _QNode* next;
}QNode;

typedef QNode* QuePtr;

typedef struct {
	int length;//队列的长度
	QuePtr front;//队列头指针
	QuePtr rear;//队列尾指针
}LinkQue;

//初始化循环队列链表
void InitQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}
	LQ->length = 0;
	LQ->front = LQ->rear = NULL;//头尾指针同时为空
}

//判断队列是否为空
int IsEmpty(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if (LQ->front == NULL) {
		return 1;
	}

	return 0;
}

//判断队列是否为满
int IsFull(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	if (LQ->length==MAX_SIZE) {
		return 1;
	}

	return 0;
}

//入队
int EnterQue(LinkQue* LQ, DataType data,int priority) {

	if (!LQ||IsFull(LQ)) {
		return 0;
	}

	QNode* qnode = new QNode;
	qnode->data = data;
	qnode->priorrty = priority;
	qnode->next = NULL;

	if (IsEmpty(LQ)) {//空队列
		LQ->front = LQ->rear = qnode;
	}
	else {
		LQ->rear->next = qnode;//在队尾插入节点
		LQ->rear = qnode;//队尾指向新插入的节点
	}
	LQ->length++;

	return 1;
}

//出队
int DeleteQue(LinkQue* LQ, DataType *data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}
	//保存当前已选取的最高优先级
	QNode** prev_NO2 = NULL;
	QNode* prev_NO1 = NULL;

	//节点上一个节点的指针地址
	QNode* last = NULL;
	QNode* tmp = NULL;

	//prev_NO2指向队首front指针的地址
	prev_NO2 = &(LQ->front);
	printf("第一个节点的优先级:%d\n", (*prev_NO2)->priorrty);
	last = LQ->front;
	tmp = last->next;
	while (tmp) {
		if (tmp->priorrty > (*prev_NO2)->priorrty) {
			printf("抓到一个更大的优先级节点:%d\n", tmp->priorrty);
			prev_NO2 = &(last->next);
			prev_NO1 = last;
		}
		last = tmp;
		tmp = tmp->next;
	}

	*data = (*prev_NO2)->data;
	tmp = *prev_NO2;
	*prev_NO2 = (*prev_NO2)->next;
	delete[] tmp;
	LQ->length--;

	//接下来存在两种情况需要分别对待
	//1.删除的是首节点,而且队列长度为0
	if (LQ->length == 0) {
		LQ->rear = NULL;
	}
	//2.删除的是尾节点
	if (prev_NO1 && prev_NO1->next == NULL) {
		LQ->rear = prev_NO1;
	}

	return 1;
}

//打印队列中的元素
void PrintQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	QuePtr tmp;
	tmp = LQ->front;
	cout << "元素有:\n";
	while (tmp) {
		cout << tmp->data << "优先级为:" << tmp->priorrty << "\n";
		tmp = tmp->next;
	}
	cout << endl;
}

//获取队首元素
int GetHead(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	return data = LQ->front->data;
}

//获取队尾元素
int GetEnd(LinkQue* LQ, DataType data) {

	if (!LQ || IsEmpty(LQ) || !data) {
		return 0;
	}

	return data = LQ->rear->data;
}

//清空队列
void ClearQue(LinkQue* LQ) {

	if (!LQ) {
		return;
	}

	LQ->front = LQ->rear = NULL;
}

//获取队列中的元素个数
int GetLength(LinkQue* LQ) {

	if (!LQ) {
		return 0;
	}

	return LQ->length;
}

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

  1. 使用双向循环队列保存缓存的文件节点,这样可以实现多种淘汰策略: 比如:如果采用淘汰进入时间 比较久的策略,就可以使用队列的特性,先进先出 如果要采用按照 LRU,就遍历链表,找到节点删除
    模拟代码实现:
#pragma once
#ifndef _NGINX_QUEUE_H
#define _NGINX_QUEUE_H

typedef struct ngx_queue_s ngx_queue_t;

//创建链式双向队列的挂件
struct ngx_queue_s {
	ngx_queue_t* next;
	ngx_queue_t* prev;
};

//使用宏函数实现各个功能函数

//初始化双向队列
#define ngx_queue_init(q) \
	(q)->prev=q;          \
	(q)->next=q

//判断双向队列是否为空
#define ngx_queue_empty(h) \
	(h==(h)->prev)

//根据优先级的划分:先进先出的原则

//从前面入队
#define ngx_queue_insert_hand(h,x) \
	(x)->next=(h)->next;     \
	(x)->next->prev=x;       \
	(x)->prev=h;             \
	(h)->next=x

#define ngx_queue_insert_after  ngx_queue_insert_hand

//从后面出队
#define ngx_queue_insert_tail(h,x) \
	(x)->prev=(h)->next; \
	(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_QUEUE_H


#include <iostream>
#include <Windows.h>
#include <time.h>
#include "nginx_queue.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);

	//模拟文件模块,增加打开的文件到缓存中
	for (int i = 0; i < 10; i++) {
		cout << i << "入队" << endl;
		ngx_cached_file_t* e = new ngx_cached_file_t;
		e->fd = i;
		ngx_queue_insert_hand(&cache->expire_queue, &e->queue);
	}

	//遍历队列
	for (q = cache->expire_queue.next; q != ngx_queue_sentinel(&cache->expire_queue); q = q->next) {
		cout << "队列中的元素:" << (ngx_queue_data(q, ngx_cached_file_t, queue))->fd << endl;
	}

	//模拟缓存的文件到期,执行出列操作
	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);
		cout << "出队列的元素:" << cached_file->fd << endl;
		ngx_queue_remove(q);
		delete(cached_file);
	}

	system("pause");
	return 0;
}

使用C++模板类实现循环队列,这样做可以兼容更多的数据类型

#pragma once
#ifndef QUEUE_H
#define QUEUE_H

#include <iostream>

using namespace std;

template<typename T>
class queue{
public:
	queue(int size);
	~queue();

	bool queIsNull();//判断队列是否为空
	bool queIsFull();//判断队列是否为满
	int getFount();//获取队首元素
	int getLength();//获取队列的元素个数

	bool insert(T elem);//入队
	T come();//出队

	void clear();//清空队列
	void pinrt();//打印队列

private:
	T* data;
	int size;//队列的空间
	int fount;//第一个元素位置
	int reat;//最后一个元素位置
};

#endif // !QUEUE_H


#include "queue.h"

template<typename T>
queue<T>::queue(int size)
{
	this->size = size;
	this->data = new int[size];

	this->fount = 0;
	this->reat = 0;
}

template<typename T>
queue<T>::~queue()
{
	if (data != NULL) {
		delete[] data;
		data = NULL;
		fount = reat = 0;
	}
}

template<typename T>
bool queue<T>::queIsNull()
{
	if (reat == fount) {
		return true;
	}
	return false;
}

template<typename T>
bool queue<T>::queIsFull()
{
	if (fount == (reat+1) % size) {
		return true;
	}
	return false;
}

template<typename T>
int queue<T>::getFount()
{
	return data[fount];
}

template<typename T>
int queue<T>::getLength()
{
	return reat-fount;
}

template<typename T>
bool queue<T>::insert(T elem)
{
	if (queIsFull()) {
		return false;
	}
	data[reat++] = elem;
	//reat++;
	return true;
}

template<typename T>
T queue<T>::come()
{
	if (queIsNull()||fount==reat) {
		return -1;
	}
	T tmp;
	tmp = data[fount];
	fount++;
	return tmp;
}

template<typename T>
void queue<T>::clear()
{
	fount = 0;
	reat = 0;
}

template<typename T>
void queue<T>::pinrt()
{
	for (int i = fount; i < reat; i++) {
		cout << data[i] << ",";
	}
	cout << endl;
}

注意:本人是在VS2019的编译器下实现的,如果在其他编译器有问题可以一起探讨,谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值