深入篇【C++】【容器适配器】:(stack)(queue)(priority_queue)模拟实现(详细剖析底层实现原理)

news2024/9/23 21:31:43

深入篇【C++】【容器适配器】: (stack)&& (queue)&& (priority_queue)模拟实现(详细剖析底层实现原理)

  • Ⅰ.容器适配器
  • Ⅱ.认识deque
  • Ⅲ.stack模拟实现
  • Ⅳ.queue模拟实现
  • Ⅴ.priority_queue模拟实现
      • 1.priority_queue()
      • 2.push()
      • 3.pop()
      • 4.仿函数
      • 5.完整代码

Ⅰ.容器适配器

适配器是一种设计模式,该模式就是将一个类的接口转化成客户想要的另外一个接口。
简单来说就是对其他容器进行封装,利用这些容器的接口来实现想要的功能,然后再封装成一个接口供别人使用。容器适配器的本质就是复用。
比如stack和queue底层就是对其他容器进行了包装。STL中stack和queue底层默认使用的容器是deque。deque是什么,下面会介绍,这里我们先了解deque是一种类似vector和list的数据结构。
在这里插入图片描述

Ⅱ.认识deque

这里对deque不做深入的探究,只是介绍deque的功能和为什么可以作为栈和队列底层的默认容器。
我们首先要理解vector和list的各自特点:

vector:
1.利用下标随机访问。
2.存在扩容,大量移动数据问题。
3.存在中间头部删除插入数据,效率低下问题。
list:
1.任意位置插入删除都很方便。
2.不存在扩容释放问题。
3.不支持下标访问。
4.底层不是连续的空间,空间使用效率低。

deque是一个双向队列,头部和尾部都可以进行操作。在这里插入图片描述

我们只要理解它和vector和list之间的区别就可以理解为什么要是有它作为底层的默认容器了。

1.deque相比vector:
优点:可以极大的缓解扩容问题和头插和头删问题。
缺点:下标访问不够极致,效率相比降低。

2.deque相比list
优点:可以支持下标访问。因为空间变得连续,cpu高速缓存效率变高。
缺点:头插头删尾删尾插效率都可以,只是中间插入和删除效率不行。

3.为什么要使用deque:
①deque很适合高频的头插头删,尾插尾删。而stack和queue就是只能对一端进行操作,所以很适合适配stack和queue的默认容器
②相比vector,不需要扩容,不存在头插头删挪动数据问题。
③相比list,不需要不断的空间释放空间,cpu缓存高。
但是要注意deque不适合高频的中间插入和删除,也不适合高频的随机访问。
如果要高频的中间插入删除,则使用list,如果需要高频的随机访问和排序最好使用vector。

deque可以看成是vector和list的结合体,具备vector和list不具备的优点。deque看似很牛,其实在现实中并不怎么使用。适合作为stack和queue底层的容器使用。

Ⅲ.stack模拟实现

stack的底层容器可以是任何标准的容器类模板或者一些其他特性的容器类,这些容器应该支持一下这些操作:{先进后出}

empty():判断是否为空。
push_back():尾插一个元素。
pop_back():尾删一个元素。
back():获取尾部元素。
因为stack不需要遍历,所以就没有通过迭代器。

通过数据结构中的stack我们知道,stack用数组和链表都可以实现的,那么在这里是如何实现的呢?
通过使用模板,来实例化出不同类的stack,所以我们需要两个模板参数,一个是用来控制stack的数据类型,一个用来控制stack的实现类型。

template<class T,class container>
T会根据使用的数据实例化不同的数据类型。
container会根据使用的容器来实例化出对应的容器类型。
namespace tao
{
     //template<class T, class container=vector<T>>
     //template<class T, class container=list<T>>
    template<class T, class container=deque<T>>
    //container容量适配器可以是vector可以是list可以是deque,这里给模板参数使用缺省值,默认使用deque作为底层容器。
    class stack
    {
    public:
        void push(const T& val)//入栈
        {
            _con.push_back(val);
        }
        void pop()//出栈
        {
            _con.pop_back();
        }
        T& top()//获取栈顶元素
        {
            return _con.back();
        }
        size_t size()//获取栈的大小
        {
            return _con.size();
        }
        bool empty()//判断栈是否为空
        {
            return _con.empty();
        }
    private:
        //对容器进行封装,来实现自己想要的接口
        container _con;
        //通过模板参数,定义一个容器,这个容器要支持上面所说的操作。
       
    };
}
stack接口功能
push数据入栈
pop数据出栈
top获取栈顶元素
empty判断栈是否为空
size获取栈的大小

测试:

  void test1()
    {
        stack<int, vector<int>> s;
        //用数组vector构成的栈
        s.push(1);
        s.push(2);
        s.push(3);
        s.push(4);
        while (!s.empty())
        {
            cout << s.top() << " ";
            s.pop();
        }
        cout << endl;

        //用链表list构成的栈
        stack<int, list<int>> l;
        l.push(1);
        l.push(2);
        l.push(3);
        l.push(4);
        l.push(5);
        while (!l.empty())
        {
            cout << l.top() << " ";
            l.pop();
        }
        cout << endl;

        stack<int> s;//不指定容器类型,则默认使用缺省参数deque
        //默认用deque构成的栈
        s.push(1);
        s.push(2);
        s.push(3);
        s.push(4);
        while (!s.empty())
        {
            cout << s.top() << " ";
            s.pop();
        }
        cout << endl;

     

Ⅳ.queue模拟实现

queue的底层容器可以是任何标准的容器类模板或者一些其他特性的容器类,这些容器应该支持一下这些操作:{先进先出}

empty():判断是否为空
push_back():尾插一个元素,入队列。
pop_front():头删一个元素,出队列。
front():获取队头数据

namespace tao
{
	template<class T,class container=deque<T>>
	//默认使用deque作为底层容器
	class queue
	{
	public:
		void push(const T& val)//入队列
		{
			_con.push_back(val);
		}
		void pop()//出队列
		{
			_con.pop_front();
		}
		T& top()//获取队头数据
		{
			return _con.front();
		}
		size_t size()//获取队列大小
		{
			return _con.size();
		}
		bool empty()//判断队列是否为空
		{
			return _con.empty();
		}
	private:
		container _con;//底层封装_con这个容器,利用_con的各个接口来实现自己想要的接口。	
	};
}
queue接口功能
push数据入队列
pop数据出队列
top获取队头数据
empty判断队列是否为空
size获取队列的大小

测试:

void test3()
	{
		queue<int, list<int>> q;
		//底层使用list构建的队列
		q.push(1);
		q.push(2);
		q.push(3);
		q.push(4);
		while (!q.empty())
		{
			cout << q.top() << " ";
			q.pop();
		}
		cout << endl;
		queue<int> q1;
		//默认使用deque作为底层容器
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		while (!q1.empty())
		{
			cout << q1.top() << " ";
			q1.pop();
		}
		cout << endl;
	}

Ⅴ.priority_queue模拟实现

priority_queue称为优先级队列,与stack和queue类似也是容器适配器,底层是用vector容器封装实现的,不过与stack和queue不同的是,priority_queue不是简单的对容器进行封装,还使用了算法来配合容器接口来实现,其底层本质是一个二叉树的堆,使用vector来构建,再加上堆的算法,将这个线性表构建成堆的结构。所以如果需要堆的地方,就可以考虑使用priority_queue。

还有注意默认情况下,建立的堆结构是大堆。这里说明下,优先级队列的创建大堆和小堆的方式是是通过使用不同的伪函数来实现的。什么是伪函数下面会进行介绍。

我们想一想堆有什么接口呢?

priority_queue接口功能
priority_queue构造对象
push数据入堆,尾插一个元素
pop数据出堆,头部删除一个元素
top获取堆顶数据
empty判断堆是否为空
size获取堆的大小

1.priority_queue()

将一个数组构建成堆,那么如何调整呢?这个过程就是建堆过程。利用向下调整算法进行建堆。
而向下调整算法使用的前提是左右子树都是堆结构才可使用。所以我们实现的方法是:

【从最后一个叶子结点的父亲开始向下调整】
①向下调整,顾名思义应当传的是父亲结点。
②首先我们要记录孩子结点的位置。
③默认child是左右孩子中较大的那一个,要与右孩子比较一下看是否右孩子大。
④用较大的孩子与父亲比较,如果孩子大于父亲,则要进行交换(建大堆),否则就停止调整。
⑤交换完后,还要持续的往下调整,利用迭代思想,将父亲位置挪动儿子位置,那么儿子就变成父亲了。再重新记录新孩子的位置。这样持续的往下找,直到孩子的下标超过了数组范围。

void Adjustdown(int parent)
		{
			//首先找到左右孩子中较大的那一个
			int child = parent * 2 + 1;
		     //默认左孩子是小的
			if (child + 1 < _con.size() && _con[child] <con[child + 1])
			{
				++child;
			}

			while (child<_con.size())
			{
				//比较父节点和孩子结点
				if ( _con[parent] < _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}

优先级队列实例化出的对象本身就是一个堆,所以我们需要在它的构造函数里就将建堆工作做好。不像前面的stack和queue我们不需要写构造函数的,因为自定义成员变量,初始化时,会调用默认构造函数或者它自己的构造函数。

而这里构造函数需要构造出一个堆,需要我们自己手动操作了。
在这里插入图片描述
我们注意到优先级队列的构造函数可以使用迭代器来初始化。
所以我们也就可以定义一个迭代器模板参数。

	//构造函数--用迭代区间来构造函数
		//这个迭代器可以写成模板
		template<class InputIterator>
		priority_queue(InputIterator begin,InputIterator last)
		{
			//第一首先将数据插入进去
			while (begin != last)
			{
				_con.push_back(*begin);
				++begin;
			}
			//第二需要建堆,默认建的是大堆--利用向下调整算法建堆
			//从最后一个叶子结点的父亲开始向下调整,然后依次往前走,直到走到堆顶。
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				Adjustdown(i);
			}

2.push()

当我们先要插入一个元素进入数组里,直接尾插利用所给的容器接口尾插到数组里。
但尾插完,要注意这个堆结构是否被破坏,如果被破坏了,需要进行调整。那么如何调整呢?我们实现的方法是:

【向上调整算法】
①顺着插入位置的双亲往上调整即可,不需要管另一侧的树,因为插入一个元素后,只会影响左子树,或者右子树的结构。
②向上调整,顾名思义,传的应该是孩子结点位置。
③首先要记录父亲位置。
④找左右孩子中比较大的孩子与父亲比较。
⑤如果孩子大于父亲则进行交换(大堆),否则停止调整。
⑥交换完后,还要持续向上调整,将孩子位置挪动父亲位置上,这样孩子就变成父亲了,再重新记录新的父亲位置。直到父亲的位置到达堆顶位置。

void Adjustup(int child)//向上调整只要一直顺着双亲向上调整即可
		{
			//首先确定父节点位置
			int parent = (child - 1) / 2;

			while (child>0)
			{
				//然后比较父节点与这个结点的大小--默认大堆
				if (_con[parent] < _con[child]))
				{
					//不需要管另一个子结点(如何有的话,因为child之前都是堆,满足堆的性质,比如大堆,那么子结点一定小于父节点的)
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
void push(const T& val)
		{
		     //首先将元素尾插到数组里
			_con.push_back(val);
			//使用向上调整算法,调整堆
			Adjustup(_con.size()-1);
		}

3.pop()

当我们需要删除一个元素时,该如何删除呢?堆的删除只有头删这个接口,那堆顶的删除该如何删除呢?还是需要使用到向下调整算法

①首先交换堆顶数据和堆尾数据
②删除堆尾数据。
③利用向下调整算法从堆顶进行调整。

void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			//里面封装着容器,直接用容器删除最后一个数据
			_con.pop_back();
             
			Adjustdown(0);
		}

其他的比如获取堆顶元素,堆的大小,堆是否为空就简单多了。

        T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}

4.仿函数

什么是仿函数呢?仿函数就是一个类的对象可以像函数一个使用。如何达到这样的效果呢?就是在类里重载()运算符,这样对象调用这个运算符重载函数时,看起来就像在调用函数。比如下面两个仿函数。

template<class T>
	class Less
	{
	public:
		
		bool operator()( T& x, T& y)//重载()运算符  其实本质也是一种泛型, 对控制比较的泛型
		{
			return x < y;
		}
	};
	template<class T>
	class Greater
	{
	public:
		
		bool operator()( T& x,  T& y)//重载()  也是一种泛型, 对控制比较的泛型
		{
			return x > y;
		}
	};

实例化出对象后,对象调用这个仿函数,看起来就像直接在调用less这个函数。

//仿函数--->类对象可以像函数一样使用本质就是类里重载了运算符()
	tao::Less<int> less;
	int x = 1;
	int y = 2;
	cout << less(x, y) << endl;;

那仿函数有什么用呢.
那仿函数有什么用呢优先级队列默认建的是大堆,那么大堆是如何建立的呢?我们是通过在构造建堆时,比较父节点和较大子节点的大小,进而控制大堆的。这样的写法是写死了,固定了,不能再修改了,如果我们像建立小堆,难道还要再拷贝一份,手动去改下符号吗?这未免太挫了吧。所以有的人就想出使用仿函数控制比较,进而控制大堆还是小堆,再增加一个模板参数用来传递仿函数,仿函数可以控制比较方式。这样就可以灵活的传递仿函数来控制创建大堆还是小堆。

在这里插入图片描述
所以我们可以用代码中用于比较的部分用仿函数来代替这样就可以灵活变化了。

5.完整代码

namespace tao
{
	template<class T, class container=vector<T>,class compare=Less<T>>//通过第三个仿函数来控制比较,进而控制大堆还是小堆
	//默认的那种方式是写死了,而这种方法通过仿函数
	//通过模板参数来控制比较方式,比较类型又通过重载()
	class priority_queue
	{
	private:
		compare com;//比较泛型---灵活比较
		void Adjustdown(int parent)
		{
			//首先找到左右孩子中较大的那一个
			int child = parent * 2 + 1;
		     //默认左孩子是小的
			if (child + 1 < _con.size() && com(_con[child] ,_con[child + 1]))
			{
				++child;
			}

			while (child<_con.size())
			{
				//比较父节点和孩子结点
				if ( com(_con[parent] , _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void Adjustup(int child)//向上调整只要一直顺着双亲向上调整即可
		{
			//首先确定父节点位置
			int parent = (child - 1) / 2;

			while (child>0)
			{
				//然后比较父节点与这个结点的大小--默认大堆
				if (com(_con[parent] , _con[child]))
				{
					//不需要管另一个子结点(如何有的话,因为child之前都是堆,满足堆的性质,比如大堆,那么子结点一定小于父节点的)
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
		}
		}
	public:
		//构造函数--用迭代区间来构造函数
		//这个迭代器可以写成模板
		template<class InputIterator>
		priority_queue(InputIterator begin,InputIterator last)
		{
			//第一首先将数据插入进去
			while (begin != last)
			{
				_con.push_back(*begin);
				++begin;
			}
			//第二需要建堆,默认建的是大堆--利用向下调整算法建堆
			//从最后一个叶子结点的父亲开始向下调整
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--)
			{
				Adjustdown(i);
			}
		}
		priority_queue()
		{}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			//里面封装着容器,直接用容器删除最后一个数据
			_con.pop_back();

			Adjustdown(0);
		}
		void push(const T& val)
		{
			_con.push_back(val);
			//使用向上调整算法,调整堆
			Adjustup(_con.size()-1);
		}
		T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		container _con;
		//容器适配器,封装了一个容器,但优先级队列不只是简单的容器封装还对容器里的数据进行了加工。插入尽量的数据还要利用算法进行调整
	};
	//仿函数/函数对象?
	template<class T>
	class Less
	{
	public:
		
		bool operator()( T& x, T& y)//重载()运算符  其实本质也是一种泛型, 对控制比较的泛型
		{
			return x < y;
		}
	};
	template<class T>
	class Greater
	{
	public:
		
		bool operator()( T& x,  T& y)//重载()  也是一种泛型, 对控制比较的泛型
		{
			return x > y;
		}
	};

测试:

void test1()
{
	//第三个模板参数可以不给,因为默认是大堆
	tao::priority_queue<int,vector<int>> pq;
	//但写了也没事,如果想建立小堆,需要显式写出。
	//priority_queue<int,vector<int>,less<int>> pq 默认是大堆
	//priority_queue<int, vector<int>, greater<int>> pq;这样才是小堆

	pq.push(1);
	pq.push(2);
	pq.push(3);
	pq.push(4);
	pq.push(5);
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
	
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/798694.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深度学习入门(一):神经网络基础

一、深度学习概念 1、定义 通过训练多层网络结构对位置数据进行分类或回归&#xff0c;深度学习解决特征工程问题。 2、深度学习应用 图像处理语言识别自然语言处理 在移动端不太好&#xff0c;计算量太大了&#xff0c;速度可能会慢 eg.医学应用、自动上色 3、例子 使用…

ARM将常数加载到寄存器方法之LDR伪指令

一、是什么&#xff1f; LDR Rd,const伪指令可在单个指令中构造任何32位数字常数,使用伪指令可以生成超过MOV和MVN指令 允许范围的常数. 实现原理: (1)如果可以用MOV或MVN指令构造该常数,则汇编程序会生成适当的指令 (2)如果不能用MOV或MVN指令构造该常数,则汇编程序会执行下列…

C++中一些常见的运算符重载代码模板

可重载和不可重载的运算符 C中可重载的运算符如下&#xff1a; 而不可重载的运算符如下&#xff1a; 运算符重载的代码模板 下面是代码中形参的统一解释&#xff1a; lhs: 左操作数&#xff0c;通常为运算符左侧的对象或值。 rhs: 右操作数&#xff0c;通常为运算符右侧的对象…

性能测试、负载测试、压力测试-之间的差异

性能测试、负载测试、压力测试-之间的差异 目录 1、什么是性能测试2、什么是负载测试3、什么是压力测试4、性能测试 vs 负载测试 vs 压力测试5、为什么要进行性能测试6、为什么要进行负载测试7、为什么要进行压力测试8、什么时候使用性能测试9、什么时候使用负载测试10、什么时…

【数据结构】实验七:字符串

实验七 字符串实验报告 一、实验目的与要求 1&#xff09;巩固对串的理解&#xff1b; 2&#xff09;掌握串的基本操作实现&#xff1b; 3&#xff09;掌握 BF 和 KMP 算法思想。 二、实验内容 1. 给定一个字符串ababcabcdabcde和一个子串abcd,查找字串是否在主串中出现。…

Ansible安装部署与应用

文章目录 一、ansible简介二、ansible 环境安装部署三、ansible 命令行模块3.1 command 模块3.2 shell 模块3.3 cron 模块3.4 user 模块3.5 group 模块3.6 copy 模块3.7 file 模块3.8 hostname 模块3.9 ping 模块3.10 yum 模块3.11 service/systemd 模块3.12 script 模块3.13 m…

可穿戴设备的发展

目录 1.什么是可穿戴设备 2.可穿戴设备的发展过程 3.可穿戴设备带来的福利 4.可穿戴设备未来的发展趋势 1.什么是可穿戴设备 可穿戴设备是指可以佩戴在身体上并与用户进行交互的电子设备。它们通常以小型、便携和智能化的形式呈现&#xff0c;与用户的服装、配饰或身体部位相…

nplayer读取电脑共享文件

Step 1&#xff1a; 在电脑中开启共享。 ▼想要访问电脑中的文件&#xff0c;电脑上需要有一个含密码的账户。 所以没有密码的首先得去控制面板为当前用户添加一个密码。 ▼下面添加共享文件夹。比如我们想共享music文件夹&#xff0c;则右键该文件夹>属性>共享>高级…

ChatGPT安卓版正式发布,附安装包,但有款手机无法使用

ChatGPT安卓版如约而至&#xff0c;OpenAI正式宣布该应用已在谷歌应用商店上架&#xff0c;用户可以免费下载&#xff0c;对话不限次数。 但是安卓版ChatGPT目前仅在美国、印度、孟加拉国和巴西提供下载&#xff0c;下周将会推广至更多国家。 网页端下载链接&#xff1a; http…

POM+数据驱动+pytest

POM数据驱动pytest 数据驱动&#xff1a;将测试过程中所有测试数据&#xff0c;进行提取、保存以及管理&#xff0c;提升框架的可维护性&#xff0c;一般情况下会使用到的数据类型&#xff1a;Excel、yaml、json、py......&#xff0c;如&#xff1a;登录场景&#xff0c;需要…

《长沙市“1+2+N”先进制造业集群提质升级研究》 课题调研组莅临麒麟信安展开专题调研

为提质升级长沙“12N”先进制造业产业集群&#xff0c;构建与现代产业体系相适应的产业生态&#xff0c;7月11日下午&#xff0c;由长沙市制造业发展促进中心党委书记沈群等市制造业发展促进中心相关领导以及中南大学王昶、卢锋华、朱思文等教授和多位博士、硕士组成的《长沙市…

视频剪辑矩阵分发系统Unable to load FFProbe报错技术处理?

问题一 报错处理 对于视频剪辑矩阵分发系统中出现的“Unable to load FFProbe”报错问题&#xff0c;可以采取以下技术处理措施进行解决。 1.检查系统中是否正确安装了FFProbe工具&#xff0c;并确保其路径正确配置。 2.检查系统环境变量是否正确设置&#xff0c;包括FFPr…

Rete算法相关内容与实例,搞懂Rete算法这一篇文章就够了,有我自己的理解,也有经典的例子详解,非常详细

Rete算法是一个规则匹配算法,是一个高效的规则匹配算法。Drools从3.X版本到7.X版本一直都是基于Rete算法实现规则匹配的,为了进一步了解Drools,所以就研究一下Rete算法。 Rete算法是由美国工程师Charles L. Forgy在1979年提出的。 该算法的设计的目的就是用于高效地处理规…

【设计模式——学习笔记】23种设计模式——装饰器模式Decorator(原理讲解+应用场景介绍+案例介绍+Java代码实现)

生活案例 咖啡厅 咖啡定制案例 在咖啡厅中&#xff0c;有多种不同类型的咖啡&#xff0c;客户在预定了咖啡之后&#xff0c;还可以选择添加不同的调料来调整咖啡的口味&#xff0c;当客户点了咖啡添加了不同的调料&#xff0c;咖啡的价格需要做出相应的改变。 要求&#xff…

SAP安装笔记

1、准备安装介质&#xff0c;SWPM10SP25&#xff0c;51050829_NW750_JavaExport、SAP_HANA_CLIENT、kernel放到/sapcd/NetWeaver目录下 ​​​​​​​ 进入SWPM10SP25执行./sapinst安装 2、待出现 “Open your browser and paste the following URL address to access the G…

如何从0开始搭建公司自动化测试框架?25k入职新公司的我是这样做的..

搭建的自动化测试框架要包括API测试&#xff0c;UI测试&#xff0c;APP测试三类。以上三类其实可以简化为两类&#xff0c;那就是&#xff1a; 1&#xff09;接口自动化测试框架搭建 2&#xff09;UI自动化测试框架搭建。 没问题&#xff0c;安排&#xff0c;且是手把手教你如何…

高校宿舍报修管理系统的设计与实现(论文+源码)_kaic

摘 要 随着科技的发展&#xff0c;信息化的管理手段早以在人们生活的各个方面取代了传统的管理手段&#xff0c;以先进管理理念为基础的现代化信息管理系统已经成为了许多机构的必备工具。在如今大学的校园里&#xff0c;有着许许多多的信息化管理系统&#xff0c;如图书管理系…

尚医通06:数据字典+EasyExcel+mongodb

内容介绍 1、数据字典列表前端 2、EasyExcel介绍、实例 3、数据字典导出接口、前端 4、数据字典导入接口、前端 5、数据字典添加redis缓存 6、MongoDB简介 7、MongoDB安装 8、MongoDB基本概念 数据字典列表前端 1、测试问题 &#xff08;1&#xff09;报错日志 &am…

LabVIEW开发小型减阻试验平台

LabVIEW开发小型减阻试验平台 湍流摩擦在粘性流体的阻力中起着重要作用&#xff0c;减少湍流摩擦是流体力学领域的热门话题之一。在油气管道的长距离流体输送中&#xff0c;泵站提供的几乎所有动力都用于克服流体的胫骨摩擦。在流体输送领域&#xff0c;船舶的蒙皮摩擦阻力占总…

函数详细解析

目录 形参有默认值的函数 基本概述 指令角度 设定形参默认值 内联函数 基本概述 代码段 函数重载 答疑解惑 形参有默认值的函数 基本概述 从右向左设定形参默认值 形参默认值可以在定义时设定&#xff0c;也可以在声明时设定 每个形参的默认值只能被设定一次 指令角度…