0
点赞
收藏
分享

微信扫一扫

(第六天)初识Spring框架-SSM框架的学习与应用(Spring + Spring MVC + MyBatis)-Java EE企业级应用开发学习记录

在这里插入图片描述


目录

一、queue的基本概念

  • queue是一种容器适配器,也是一种先进先出(First in First Out,简称FIFO)的数据结构, 其中从容器一端插入元素,另一端提取元素
  • 容器适配器通常会限制对底层容器的访问方式,因此不能遍历,但队列中只有

在这里插入图片描述

二、 queue的常见操作

2.1 构造函数

  • 默认构造
// T可以是任意类型
queue<T> q;
  • 拷贝构造
//q已知
queue<T> qq(q);

2.2 empty

2.3 size

2.4 front

2.5 back

2.6 push

2.7 pop

2.8 赋值操作

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	queue<int> q;
	// 插入
	q.push(10);
	q.push(20);
	q.push(30);
	q.push(40);


	queue<int> qq;
	//赋值运算符
	qq = q;

	cout << "元素个数:" << qq.size() << endl;
	cout << "队头元素" << qq.front() << endl;
	cout << "队尾元素" << qq.back() << endl;

	// 遍历
	while (!qq.empty())
	{
		cout << qq.front() << ' ';
		qq.pop();
	}
	cout << endl;
	return 0;
}

【输出结果】

在这里插入图片描述

三、有关队列的力扣经典题

3.1 二叉树的层序遍历

链接:点击跳转

【题目描述】

在这里插入图片描述

【思路】

在这里插入图片描述

【代码实现】

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) 
    {
        queue<TreeNode*>q;
        vector<vector<int>> vv;
        int levelSize;

        // 如果根节点不为空,则入队列
        if (root)
        {
            q.push(root);
            // 并且根节点的root一定为1
            levelSize = 1;
        }

        while (!q.empty())
        {
            // 一层一层出
            vector<int> v;
            for (int i = 0;i < levelSize;i++)
            {
                // 记录当前节点
                TreeNode* front = q.front();
                // 删除节点并存入
                q.pop();
                v.push_back(front->val);
                // 并带入它的子节点
                if (front->left) q.push(front->left);
                if (front->right) q.push(front->right);
            }
            vv.push_back(v);
            // 一层出完更新一层的个数
            levelSize = q.size();
        }
        return vv;
    }
};

3.2 用队列实现栈

链接:点击跳转

【题目描述】

在这里插入图片描述

【思路】

队列的特点是先进先出,而栈是先进后出,首先定义两个队列

在这里插入图片描述

那如何模拟一个栈呢?首先往空的队列入数据

在这里插入图片描述

对于栈来说,先出的是4。因此我们可以把1 2 3移到另一个空队列中

在这里插入图片描述

【代码实现】

class MyStack {
public:
    MyStack() {}
    
    void push(int x) 
    {
    	// 往不是空的队列插入数据
        if (in.empty())
        {
            out.push(x);
        }
        else
            in.push(x);
    }
    
    int pop() 
    {
    	// 保持一个队列为空
    	// 将一个为空的队列的前n-1个移到空队列
    	// 剩下的那个这是栈顶元素
        if (in.empty())
        {
            while (out.size() > 1)
            {
                int front = out.front();
                out.pop();
                in.push(front);
            }
            int ans = out.front();
            out.pop();
            return ans;
        }
        else // out为空
        {
            while (in.size() > 1)
            {
                int front = in.front();
                in.pop();
                out.push(front);
            }
            int ans = in.front();
            in.pop();
            return ans;
        }
    }
    
    int top() 
    {
        if (in.empty())
        {
           return out.back();
        }
        else 
        {
            return in.back();
        }
    }
    
    bool empty()
    {
        return in.empty() && out.empty();
    }
private:
    queue<int> in;
    queue<int> out;
};

四、模拟实现queue

4.1 简介

在这里插入图片描述

queue同样也是一种容器适配器,容器适配器可以被视为一种包装器,它们通过修改底层容器的接口或行为来实现新的功能。通过使用这些容器适配器,开发者可以方便地在不同场景下使用已有容器的功能,并且无需关心底层容器的具体实现。

其实就是STL中封装好的队列,在使用的时候我们不仅可以指定内部的数据类型,还可以指定内部的容器。不指定容器其实也是可以的,模板参数有一个缺省值,默认是deque

4.2 代码实现

#pragma once

namespace wj
{
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& x)        
		{
			_con.push_back(x);       
		}

		void pop()                   
		{
			_con.pop_front();    
		}

		const T& front()            
		{
			return _con.front();
		}

		const T& back()              
		{
			return _con.back();
		}

		size_t size()                
		{
			return _con.size();      
		}

		bool empty()                 
		{
			return _con.empty();     
		}

	private:
		Container _con;
	};

注意:队列就不能用vector适配了,因为vector没有提供pop_front接口,但是也可以强制适配。只需要改动pop接口

void pop()                   
{
	_con.erase(_con.begin());      
}

但注意,库里是没有强制适配的

在这里插入图片描述

为了贴近库,最好不要进行强制适配~

那么为什么库里不支持vector的头删呢?就是因为效率低,头删需要移动数据。

五、deque(了解)

5.1 deque的介绍

deque(双端队列):是一种可以在头尾两端进行插入和删除的"连续"空间的数据结构,且时间复杂度为O(1),它似乎是vectorlist的合体版。与vector比较,deque头插效率高,不需要搬移元素;与list比较,空间利用率比较高(是一块连续的空间)。

在这里插入图片描述

5.2 deque的底层原理

但如果深挖底层的话,deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,其底层结构如下图所示:

在这里插入图片描述

为了兼顾双端插入以及随机访问,deque的底层是使用一个中控数组(可以认为是指针数组)来管理一个个连续的空间,且第一个空间被开辟出来后是存放在中控数组的中央位置,之后不断插入数据,若一块连续空间已满只需要再开一块连续空间即可。也就是在中控数组中再增加一个指针。若是进行头插,则需要开辟一段新空间,将新的值存于连续空间的尾部。

5.3 deque的缺陷

  • vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是比vector高的。
  • list比较,deque底层是连续空间,空间利用率比较高,
  • 但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到
    某段小空间的边界,导致效率低下。大多数情况下优先考虑vectorlist。还有中部的插入删除操作相当复杂,若是直接在中部插入就要挪动当前空间的数据,更甚者还要牵扯到接下来的连续空间
  • deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stackqueue的底层默认容器

5.4 为什么选择deque作为stack和queue的底层默认容器

在这里插入图片描述
在这里插入图片描述

  • stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()pop_back()操作的线性结构,都可以作为stack的底层容器,比如vectorlist都可以;
  • queue是先进先出的特殊线性数据结构,只要具有push_backpop_front操作的线性结构,都可以作为queue的底层容器,比如list

但是STL中对stackqueue默认选择deque作为其底层容器,主要是因为:

  1. stackqueue不需要遍历(因此stackqueue没有迭代器),只需要在固定的一端或者两端进行操作。
  2. stack中元素增长时,dequevector的效率高(扩容时不需要搬移大量数据);queue中的元素增长
    时,deque不仅效率高,而且内存使用率高。结合了deque的优点,而完美的避开了其缺陷。
举报

相关推荐

0 条评论