首页 > 其他分享 >数据结构之队列

数据结构之队列

时间:2024-10-26 21:45:38浏览次数:9  
标签:head 队列 结点 next int _- 数据结构 size

一、队列的定义

队列是一种操作受限的线性表,队列只允许在表的一端进行插入,在表的另一端进行删除。可进行插入的一段称为队尾,可进行删除的一端称为队头。

队列的主要特点就是先进先出。依照存储结构可分为:顺序队和链式队。

二、顺序队列

一开始front(队头)和rear(队尾)都在数组的0号位置,入队时rear往后挪动一位

因为是用数组实现的环形队列,front不能单纯的++,否则越界,出队时,将front后挪动一位

入队时需要先考虑队中是否已满,出队时要考虑队中是否为空

代码实现

成员变量
T* pQue_; //维护指针
int cap_; //空间容量
int front_; //队头
int rear_; //队尾
int size_; //队列元素个数

这里的T是模板

构造函数与析构函数
MyQueue(int size = 20)
	: pQue_(new T[size])
	, front_(0)
	, rear_(0)
	, cap_(size)
	, size_(0)
{}
~MyQueue() {
	delete[] pQue_;
	pQue_ = nullptr;
}
扩容
void expand(int size) {
	int* p = new T[size];
	//这里不能用memcpy(),需要遍历队列,将元素重新赋值到新内存中
	int i = 0;//遍历新数组
	int j = front_;//遍历老数组
	for (; j != rear_; i++, j = (j + 1) % cap_) {
		p[i] = pQue_[j];
	}
	delete pQue_;
	pQue_ = p;
	cap_ = size;
	front_ = 0;
	rear_ = i;
}

这里不能直接用memcpy,因为队头可能不在数组索引为0的位置,需要从front遍历到rear,同样不能单纯的递增,要考虑到数组会越界

入队
void push(T val) {
	if (isFull()) {
		//队满扩容
		expand(cap_ + 10);
	}
	pQue_[rear_] = val;
	rear_ = (rear_ + 1) % cap_;
	size_++;
}
//判断队列是否已经满了
bool isFull() {
	//保证队列中一直空着一个元素来区分队列是否满了
	if ((rear_ + 1) % cap_ == front_) {
		return true;
	}
	return false;
}

先判断队列是否满了,满了的话扩容十个元素的容量

未满直接在队头的索引放元素即可,别忘了将容纳元素数量++

出队
void pop() {
	//队列为空抛异常
	if (empty()) {
		throw "queue is empty";
	}
	front_ = (front_ + 1) % cap_;
	size_--;
}
bool empty() const {
	if (front_ == rear_) {
		return true;
	}
	return false;
}

队列为空抛异常,否则将队头往后挪一位就可以

链式队列

采用双向循环链表实现

结点结构体
struct Node
{
	Node(int data = 0)
		: data_(data)
		, next_(nullptr)
		, pre_(nullptr)
	{}
	int data_;
	Node* next_;
	Node* pre_;
};

成员变量

Node* head_;
int size_;
构造函数与析构函数
LinkQueue():size_(0) {
	head_ = new Node;
	head_->next_ = head_;
	head_->pre_ = head_;
}
~LinkQueue() {
	Node* p = head_->next_;
	while (p != head_) {
		head_->next_ = p->next_;
		p->next_->pre_ = head_;
		delete p;
		p = head_->next_;
	}
	delete head_;
}

构造函数需要将头结点的next指向头,pre指向头(双向循环列表)

析构函数删除结点是每次删除链表的第一个结点,将头结点的next指向被删结点的下一结点,被删结点的下一结点的pre指向被删结点,循环结束条件为 != 头结点,循环结束后只剩头结点了,直接删除

入队
void push(int val) {
	Node* tem = new Node(val);
	tem->next_ = head_;
	tem->pre_ = head_->pre_;
	head_->pre_->next_ = tem;
	head_->pre_ = tem;
	size_++;
}

尾插在链表中,改变指向顺序要记得先变新结点,再变老结点

出队
void pop() {
	Node* p = head_->next_;
	head_->next_ = p->next_;
	p->next_->pre_ = head_;
	delete p;
	size_--;
}

出队和析构原理是一样的,删除第一个结点

获取队头&队尾元素
//获取队头元素
int front() const {
	if (head_->next_ == head_) {
		throw "queue is empty!";
	}
	return head_->next_->data_;
}
//获取队尾元素
int rear() const {
	if (head_->next_ = head_) {
		throw "queue is empty!";
	}
	return head_->pre_->data_;
}

判断队列为空的依据都是head->next = head,双向循环链表因为可以从头结点直接访问到尾结点

访问队头元素和队尾元素时间复杂度都是O(1)

两个栈实现一个队列

实现原理:s1用来入队的,s2用来出队的,出队时先把s1出栈的元素入栈到s2,再由s2出栈,这样就实现了先进先出,后进后出的进出顺序了

代码:

class TwoStack2Queue 
{
public:
	//入队
	void push(int x) {
		s1.push(x);
	}
	//出队
	int pop() {
		if (s2.empty()) {
			while (!s1.empty()) {
				s2.push(s1.top());
				s1.pop();
			}
		}
		int val = s2.top();
		s2.pop();
		return val;
	}
	//获取队头元素
	int peek() {
		if (s2.empty()) {
			while (!s1.empty()) {
				s2.push(s1.top());
				s1.pop();
			}
		}
		return s2.top();
	}
	//判断队列是否为空
	bool empty() {
		return s1.empty() && s2.empty();
	}
private:
	stack<int>s1;
	stack<int>s2;
};

如果两个栈都为空,队列才为空

标签:head,队列,结点,next,int,_-,数据结构,size
From: https://blog.csdn.net/d6d4664948/article/details/143259104

相关文章

  • 关于 顺序表、单链表、双链表、栈、队列的小总结
    1.结构的定义方式-顺序表:以结构体指针方式定义-链表:以结构体自引用方式定义-栈:个人推荐使用结构体指针方式定义(类似顺序表)-队列:以结构体指针+结构体自引用方式实现2.对顺序表、单链表、双链表的小小对比顺序表:尾插、尾删操作更方便(对头操作的话需......
  • 【数据结构】树-二叉树-堆(上)
    ......
  • 数据结构与算法——顺序栈的实现
    数据结构栈——一列数据,表尾入栈,表尾出栈,类似于子弹弹匣,压入子弹和拿出子弹都是从最上方进出。结构体structStack{ int*arr; intcapacity;//数组容量 inttop;//存储栈顶元素的下标};初始化栈intInitStack(structStack*stack){ stack->arr=......
  • 初阶数据结构之顺序表的实现
    1线性表什么是线性表呢?线性表是n个具有相同特性的数据元素的有限序列。常见的线性表:顺序表,链表,栈,队列,字符串。线性表在逻辑上是线性结构,在物理结构上不一定是线性的。线性表在物理存储时,通常是以数组或链式结构形式存储。线性表大致分为两种:顺序表和链表。基于这两种......
  • 数据结构:学生成绩管理系统(链表方法)〈可直接使用〉
    本代码比较完善,有登录模块和菜单模块,拥有很高的可操控性和可观性。代码所包含的功能有很多:成绩输入与删除,按位置查询学生,按姓名查找学生,求表的长度,求最高分学生信息,显示全部学生信息,保存与读取文件......本代码的文件读取和文件保存是一大亮点,可以灵活的存取,启动一次可以切换......
  • 延迟队列的安装步骤
    RabbitMQ中的延迟队列(DelayedQueue)是一种特殊的队列,用于在消息被发送后延迟一段时间再投递给消费者。它在许多场景中非常有用,例如需要定时执行的任务、限流、重试机制等。使用场景定时任务:例如发送提醒邮件或通知,确保在特定时间后再执行。限流:控制请求速率,防止瞬时高并......
  • Redis基础知识(学习笔记1--五种基础数据结构)
    Redis,Remote Dictionary Server,远程字典服务。Redis的性能极高:其读的速度可以达到11W/s,写的速度可以到达8W/s。高性能的原因(1)操作在内存中发生;(2)C语言开发;(3)源码简单精细(集性能与优雅于一身),早期版本源码只有2W行左右,从3.0版本(开始支持cluster),代码变成了5W左右。Redis有5种......
  • 【数据结构初阶】单链表接口实现超详解
    1.顺序表问题与思考上一篇博客中我们介绍了顺序表,那么顺序表有什么缺点呢?中间/头部的插入删除,时间复杂度为O(N)增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数......
  • 国产东方通消息队列TongLINKQ8.1服务端安装步骤
    一、服务端安装groupaddtlq# 新建组useradd-m-gtlqtlq# 新建tlq用户并指定组tlqcd/home/tlq/# 切换到安装目录并上传安装包tar-xzvfInstall_TLQ_Standard_Linux2.6.32_x86_64_8.1.16.0.tar.gz#解压安装文件cd/home/tlq/TLQ8/设置环境变量c......
  • 数据结构前置知识
    想要成为一名真正的高手,会写会读代码真的只是起步而已,能明白程序的底层运行逻辑,才是神中神。数据结构正是让我们进入代码逻辑世界的钥匙......