C++-----stack和queue

news2024/9/20 18:44:34

本期我们来学习stack和queue

目录

stack介绍

栈的使用

栈的模拟实现

queue介绍

队列的使用 

 队列的模拟实现

deque

优先级队列

 模拟实现

仿函数

全部代码


stack介绍

1. stack 是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。
2. stack 是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部( 即栈顶 ) 被压入和弹出。
3. stack 的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:
empty :判空操作
back :获取尾部元素操作
push_back :尾部插入元素操作
pop_back :尾部删除元素操作
4. 标准容器 vector deque list 均符合这些需求,默认情况下,如果没有为 stack 指定特定的底层容器,默认情况下使用deque

 我们发现,stack模板参数和我们之前的容器是不同的,我们现在只要知道,我们的栈和队列,是容器适配器,是用容器转换出来的,适配器的本质是一种复用

栈的使用

 这些接口根据我们目前的水平是可以轻松看懂的

并且我们发现,它是没有迭代器的,不支持我们随便进行遍历的,因为要保持后进先出,队列也是一样的

 

 使用起来是非常简单的

栈的模拟实现

我们前面说过适配,我们来完成一个链式栈和数组栈秒切换

namespace bai
{
	template<class T,class Container>
	class stack
	{
	public:
		void push(const T& x)
		{

		}
	private:
		Container _con;
	};
	void test() {
		stack<int, vector<int>> st1;
		stack<int, list<int>> st2;

	}
}

我们加一个模板参数就可以了

        void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		T& top()
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}

这些接口我们全部复用即可,这就是适配器,非常方便

其中top我们使用back接口

list和vector都有back和front接口,我们直接使用

容器适配器就是,数据是容器管理,我们对容器进行封装,管理,改造等等

测试一下,也没有问题

不过此时我们还有一个问题,我们每次都要传两个模板参数,非常麻烦,库里面是不需要的

 它给的是类型,给了一个deque,deque我们后面解释

我们先加一个缺省参数,给一个vector即可

此时不写默认就是vector的栈

 另外,我们的栈是不需要构造函数,拷贝构造这些,因为我们是自定义类型,回去调用自己的构造,拷贝构造等等

queue介绍

1. 队列是一种容器适配器,专门用于在 FIFO 上下文 ( 先进先出 ) 中操作,其中从容器一端插入元素,另一端提取元素。
2. 队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类, queue 提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。
3. 底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:
empty :检测队列是否为空
size :返回队列中有效元素的个数
front :返回队头元素的引用
back :返回队尾元素的引用
push_back :在队列尾部入队列
pop_front :在队列头部出队列
4. 标准容器类 deque list 满足了这些要求。默认情况下,如果没有为 queue 实例化指定容器类,则使用标准容器deque

队列的使用 

同样的,这些接口都很简单,这里就不再演示

 队列的模拟实现

队列用vector适配是不好的,但我们也可以强制适配

vector是没有提供头删的

不过我们可以使用erase来强制适配

        void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			//_con.pop_front();
			_con.erase(_con.begin());
		}
		T& front()
		{
			return _con.front();
		}
		T& back()
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}

修改一下top,为front,再加一个back接口即可

测试一下也没有问题 

但是这样强制适配是不好的

库里面是支持list,但不支持vector的 

因为顺序表的头删效率是很低的

所以queue的默认容器我们使用list 

deque

deque是一个双端队列,不过它虽然看起来是队列,但其实不是

队列的特点是先进先出,但deque不是,它是一个双向开口的,两边都可以插入删除,而且他是一个随机迭代器

它就像一个vector和list合集的六边形战士,可以像数组一样访问,还有头插头删,尾插尾删

 

我们简单用一下 

那deque这么强,为什么我们之前没怎么听说过呢?为什么不用它代替vector和list呢?

其实只看头插头删,尾插尾删这些,deque还是可以的,但论综合性,是无法代替vector和list的

这是100w数据量下,第一个deque是将数据拷贝到vector里,然后进行排序,再拷贝回来,第二个是直接对deque进行排序,差距是很明显的 

deque其实是一个很一般的设计,这也想要,那也想要,最后啥也干不了

vector直接下标随机访问,但是扩容,中间以为头部的插入删除效率不高,list则是支持任意位置插入删除,但不支持下标随机访问,是按需要求申请释放的

vector是连续的,list的一个一个的节点,所以就有人想,我们开辟一段一段的空间,然后用一个中控数组指向他们(这个中控数组不是从头开始指向的), 中控数组就是一个指针数组,这样一看感觉和vector<vector<int>>差不多,但其实差距是很大的,vector指向的vector是可以扩容的,每一段的大小都可以不一样,但是中控数组指向的空间是一样大的

指针数组满了,要进行扩容,不过扩容的代价是非常底的,它指向的空间是不用动的,只需拷贝指针,不需要拷贝原数据,我们上面说,中控数组是从中间开始指向的,所以头插时,是可以从前面的空间插入的

就像这样,而且它的插入是倒着往前走的 ,先插入的-1,再插入的-2

相比vector,它缓解了扩容的问题,头插头删问题,但是 [ ] 不够极致,[ ] 要去计算在哪个buff(指向的数组)的第几个

 它要先计算在不在第一个buff,第一个buff不是满的,不是从零开始的,第一个buff是倒着往前走的,如果在的话就找位置访问,如果不在,i -=第一个buff数组的size,第几个buff = i / buff,在这个buff的第几个 = i % buffsize

所以是不够极致的,vector是直接指针解引用访问就行了,而这里是需要计算的,这也是为什么上面效率比对时差距明显的原因了,在大量访问时,这个计算是很麻烦的

相比list,它支持下标随机访问,cpu高速缓存效率不错,头尾插入删除不错,但是中间的插入删除就不好了,中间插入的话一个是挪动数据,这个效率太低,另一个就是扩容,扩容中间这个buff,是不好的,库里面也没有这么做,这样做会影响上面的计算,会使[ ]的效率进一步下降

只有高频的头尾的插入删除,才适合deque,所以deque就用来适配栈和队列的默认容器

deque的底层是非常复杂的

底层是一段假象的连续空间,实际是分段连续的,为了维护其 整体连续 以及随机访问的假象,落 在了 deque 的迭代器身上 因此 deque 的迭代器设计就比较复杂,迭代器就封装了四个指针,如下图所示

 我们再看一张图

start就相当于begin,finish就相当于end,要进行一遍迭代器遍历,把begin给it,然后解引用,解引用返回*cur 

这是源码 ,我们再看看++,有两个操作,first和last是指向这段数组的起始和末尾,cur!=last 说明没有走完,++cur即可,如果走完了,迭代器要指向下一个buff,里面的node就要++,指向下一个buff的地址,再解引用就可以指向下一个buff,然后再让first和last指向这个buff,cur再指向这个buff,我们再看什么适合结束,当it和end在同一个buff时,cur还没走完,当cur等于end的cur时就结束

 

 

 

 deque我们稍微看看就行

优先级队列

 优先级队列和deque一样,都不能算队列,因为队列是先进先出的,这两个只是占了个名

我们看到它也是容器适配器,默认使用了vector

这些接口也都很简单

 优先级队列底层就是二叉树的堆,默认大的优先级高

默认是大堆,我们仔细看默认容器适配器传的是小于(第三个模板参数),但默认又是大堆 ,是反过来的

传一个greater才能变为小堆 ,而默认的less是大堆,是相反的,大家要牢记

 不一样的地方是构造函数给了迭代器区间构造,可以给一个区间,用这些值构造堆

这里还可以换成deque,不过是不太好的,因为deque的随机访问要计算的,不如vector

 模拟实现

下面我们模拟实现一下优先级队列

namespace bai {
	template<class T,class Container = vector<T>>
	class priority_queue
	{
	private:
		void AdjustDown(int parent)
		{
			int child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() && _con[child + 1] > _con[child])
				{
					++child;
				}
				if (_con[child] > _con[parent])
				{
					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 (_con[child] > _con[parent])
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}	
		}
	public:
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}
			//建堆
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) 
			{
				AdjustDown(i);
			}
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size() - 1);
		}
		const T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

这里逻辑其实都不难,用到了以前我们堆的知识,如果对堆还不够理解的同学可以看我之前对堆的详解

(20条消息) 堆及其多种接口与堆排序的实现_KLZUQ的博客-CSDN博客

我们测试一下,没有问题 

仿函数

仿函数,也叫做函数对象

如果不看其他的,只看cout输出这里,我们会认为lessfunc是一个函数

实际上这个类重载了括号,本质等价于lessfunc.operator()

本质是把这个类的对象像函数一样使用,作用是替代C语言里的函数指针

 我们将它变为类模板,使用范围就变的很广阔了 

那仿函数到底是干什么的呢?

像我们这样的比较,是写死的 ,我们可以用函数指针来让它变得灵活,但C++不使用函数指针,因为函数指针的可读性很差,所以就开发出了仿函数

 

我们把这些先全改为<的比较,然后使用com来进行控制 

这样我们的比较就变得灵活了,传入less或者greater,就可以切换大于和小于的比较,从而调整我们是要建大堆,还是建小堆 ,我们可以根据需求写出自己的less和greater,对各种类型都可以进行比较

简单来说,仿函数就是通过模板参数,调用比较类型,调用operator(),operator()是我们自己实现的,或者我们用库里面的less或者greater也可以

 我们把之前写过的Date类拿过来,都进行排序

有些场景下我们需要自己写仿函数

 

这个情况非常诡异,每一次运行结果都不一样 

因为我们的类型是一个指针,new出来的地址大小是不确定的,带有随机性,所以会有这种结果

 我们写一个这样的仿函数,就可以解决问题

我们可以通过仿函数来控制比较规则,而不是被优先级队列给写死了

全部代码

//stack.h
using namespace std;
#include<iostream>
#include<list>
#include<vector>

namespace bai
{
	//容器适配器
	template<class T,class Container = vector<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_back();
		}
		T& top()
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
	void test() {
		stack<int> st1;
		st1.push(1);
		st1.push(2);
		st1.push(3);
		st1.push(4);
		while (! st1.empty()) {
			cout << st1.top() << " ";
			st1.pop();
		}
		cout << endl;
		stack<int, list<int>> st2;
		st2.push(10);
		st2.push(20);
		st2.push(30);
		st2.push(40);
		while (!st2.empty()) {
			cout << st2.top() << " ";
			st2.pop();
		}
		cout << endl;
	}
}
//queue.h
#include<iostream>
#include<list>
#include<vector>
using namespace std;
namespace bai
{
	//容器适配器
	template<class T, class Container = list<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			//_con.pop_front();
			_con.erase(_con.begin());
		}
		T& front()
		{
			return _con.front();
		}
		T& back()
		{
			return _con.back();
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;
	};
	void test_queue() {
		//std::queue<int,vector<int>> q;
		queue<int, list<int>> q2;
		q2.push(10);
		q2.push(20);
		q2.push(30);
		q2.push(40);
		while (!q2.empty()) {
			cout << q2.front() << " ";
			q2.pop();
		}
		cout << endl;
	}
}
//priority_queue.h
using namespace std;
#include<vector>
#include<iostream>
namespace bai {
	template<class T,class Container = vector<T>,class Comapre = less<T>>
	class priority_queue
	{
	private:
		void AdjustDown(int parent)
		{
			Comapre com;
			size_t child = parent * 2 + 1;
			while (child < _con.size())
			{
				if (child + 1 < _con.size() &&  com(_con[child] , _con[child + 1]))
				{
					++child;
				}
				if (com(_con[parent], _con[child]))
				{
					swap(_con[child], _con[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else 
				{
					break;
				}
			}
		}
		void AdjustUp(int child)
		{
			Comapre com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				if (com(_con[parent] , _con[child]))
				{
					swap(_con[child], _con[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}	
		}
	public:
		priority_queue()
		{}
		template<class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				_con.push_back(*first);
				++first;
			}
			//建堆
			for (int i = (_con.size() - 1 - 1) / 2; i >= 0; i--) 
			{
				AdjustDown(i);
			}
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);
			_con.pop_back();
			AdjustDown(0);
		}
		void push(const T& x)
		{
			_con.push_back(x);
			AdjustUp(_con.size() - 1);
		}
		const T& top()
		{
			return _con[0];
		}
		bool empty()
		{
			return _con.empty();
		}
		size_t size()
		{
			return _con.size();
		}
	private:
		Container _con;
	};

	class Date
	{
	public:
		Date(int year = 1900, int month = 1, int day = 1)
			: _year(year)
			, _month(month)
			, _day(day)
		{}

		bool operator<(const Date& d)const
		{
			return (_year < d._year) ||
				(_year == d._year && _month < d._month) ||
				(_year == d._year && _month == d._month && _day < d._day);
		}

		bool operator>(const Date& d)const
		{
			return (_year > d._year) ||
				(_year == d._year && _month > d._month) ||
				(_year == d._year && _month == d._month && _day > d._day);
		}

		friend ostream& operator<<(ostream& _cout, const Date& d);
	private:
		int _year;
		int _month;
		int _day;
	};

	ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
	struct LessPDate
	{
		bool operator()(const Date* p1, const Date* p2)
		{
			return *p1 < *p2;
		}
	};
	void test() {
		priority_queue<Date*, vector<Date*>,LessPDate> pq;
		pq.push(new Date(2021, 7, 28));
		pq.push(new Date(2021, 6, 28));
		pq.push(new Date(2021, 8, 28));
		while (!pq.empty()) {
			cout << *pq.top() << " ";
			pq.pop();
		}
		cout << endl;
	}
}

以上即为本期全部内容,希望大家可以有所收获

如有错误,还请指正

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

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

相关文章

亚马逊测评自养号:如何正确选择学习对象与获取可靠技术知识?

亚马逊是一家知名的跨境电商平台&#xff0c;吸引了越来越多的人涉足这个领域。随着商家数量的增加&#xff0c;亚马逊的竞争力也在不断提高。在亚马逊平台上&#xff0c;产品评价对于卖家账号的评估以及产品曝光量、销量等方面具有直接影响。因此&#xff0c;对于任何一个希望…

单路 PWM 控制的高调光比 LED 降压恒流控制器

概述 OC5401 是一款单路 PWM 控制的高调光比降压恒流驱动控制器&#xff0c;PWM 调光比最高可达 10000&#xff1a;1。 OC5401 支持 16-60V 输入电压范围。 OC5401 采用电流滞环控制方式&#xff0c;无需环路补偿。 OC5401 可通过外接电阻设置 LED 输出电流&#xff0c;最…

数据库中的事务处理

MySQL的事务处理&#xff1a;只支持 lnnoDB 和BDB数据表类型 1.事务就是将一组SQL语句放在同一批次内去执行 2.如果一个SQL语句出错&#xff0c;则该批次内的所有SQL都将被取消执行 MySQL的事务实现方法一&#xff1a; select autocommit 查询当前事务提交模式 set a…

【第二套】Java面试题

第二套&#xff1a; 一、JavaScript前端开发 1、下列的代码输出什么&#xff1f; var y 1; if(function f(){}){y typeof f; } console.log(y);正确的答案应该是 1undefined。 JavaScript中if语句求值其实使用eval函数&#xff0c;eval(function f(){}) 返回 function f()…

Web前端 常用布局

Flex布局&#xff08;弹性布局&#xff09; 参考&#xff1a;滑动验证页面 space-between与space-around的区别

Python编程很简单,四步菜鸟到高手(文末送书5本)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

别在找git报错的解决方案啦,多达20条git错误解决方案助你学习工作

1. 找不到Git命令 $ sudo apt-get update $ sudo apt-get install git2. 无法克隆远程仓库 $ git clone https://github.com/username/repo.git3. 无法拉取或推送到远程仓库 $ git pull origin master $ git add . $ git commit -m "Resolve conflicts" $ git pus…

java.lang.ClassNotFoundException: sun.misc.BASE64Decoder

有一个新的应用服务&#xff0c;idea启动应用应用服务时&#xff0c;突然报错java.lang.ClassNotFoundException: sun.misc.BASE64Decoder &#xff0c;然后在网上搜索&#xff0c;说是建议使用apache包&#xff0c;该类新的JRE已经废弃&#xff0c;并从rt.jar包中移除。但是该…

Visual Components数字化工厂仿真软件 衡祖仿真

Visual Components 3D 制造仿真软件可以方便快捷地设计和验证新的生产解决方案&#xff0c;可以向用户提供快速、便捷、真实的智能制造仿真解决方案。 一、数字化工厂产线仿真 VisualComponents仿真软件将离散物流事件仿真、人机协作、机器人工艺仿真和虚拟调试集于一个平台&am…

【Matter】基于Ubuntu 22.04 编译chip-tool工具

前言 编译过程有点曲折&#xff0c;做下记录&#xff0c;过程中&#xff0c;有参考别人写的博客&#xff0c;也看github 官方介绍&#xff0c;终于跑通了~ 环境说明&#xff1a; 首先需要稳定的梯子&#xff0c;可以访问“外网”ubuntu 环境&#xff0c;最终成功实验在Ubunt…

#P1005. [NOIP2010普及组] 导弹拦截

题目描述 经过 1111年的韬光养晦&#xff0c;某国研发出了一种新的导弹拦截系统&#xff0c;凡是与它的距离不超过其工作半径的导弹都能够被它成功拦截。当工作半径为 00时&#xff0c;则能够拦截与它位置恰好相同的导弹。但该导弹拦截系统也存在这样的缺陷&#xff1a;每套系…

L2Cache 核心原理解析

源码地址&#xff1a;L2Cache L2Cache是什么 L2Cache 是一个基于内存、 Redis 、 Spring Cache 实现的满足高并发场景下的分布式二级缓存框架。 L2Cache如何使用 如何使用 L2cache L2Cache架构图 核心逻辑 1、发起 get(key) 请求2、从localCache中get缓存&#xff0c;若存…

C语言获取13位时间戳以及格式化时间戳

定义time_util.h头文件 #ifndef TIME_UTIL_H #define TIME_UTIL_H #include <stdio.h> #include <time.h> #ifdef _WIN32 #include <windows.h> #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64 #else #include <sys/time.h> #endifvoid ge…

DoIP学习笔记系列:(一)DoIP协议概述

文章目录 1. 为什么会有DoIP协议的需求产生?2. DoIP协议入门2.1 传输层协议和网络层服务2.2 物理层和数据链路层2.3 协议介绍2.3.1 报文封装结构2.3.2 端口号2.3.3 DoIP报文格式2.3.3.1 DoIP首部,协议版本号2.3.3.2 DoIP首部,协议版本号取反2.3.3.3 DoIP首部,负载类型2.3.3…

简要介绍 | 自回归生成:探索序列的未来之旅

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对Autoregressive Generation进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 自回归生成&#xff1a;探索序列的未来之旅 Approach - Autoregressive Conditional Generation using Transformer…

Array(20) 和 Array.apply(null, {length: 20})

1.Array(20) 其结果是&#xff1a; 创建了一个长度为20&#xff0c;但元素均为 empty 的数组。 2.Array.apply(null, { length: 20 }) 其结果是&#xff1a; 创建了一个长度为20&#xff0c;但元素均为 undefined 的数组。 3.异同 3.1相同 console.log(arr1[0] arr2[0]) /…

工具推荐:Linux Busybox

文章首发地址 BusyBox是一个开源的、轻量级的、可嵌入式的、多个Unix工具的集合。BusyBox提供了各种Unix工具的实现&#xff0c;包括文件处理工具、网络工具、shell工具、系统管理工具、进程管理工具等等。它被设计为一个小巧、高效、可靠、易于维护的工具&#xff0c;适用于嵌…

iOS--KVO和KVC

KVC 简单介绍 KVC的全称是KeyValueCoding&#xff0c;俗称“键值编码”&#xff0c;可以通过一个key来访问某个属性&#xff1b; KVC提供了一种间接访问其属性方法或成员变量的机制&#xff0c;可以通过字符串来访问对应的属性方法或成员变量&#xff1b; 它是一个非正式的…

数据结构笔记

数据结构笔记 1. 绪论 随着计算机深入到各个领域&#xff0c;它的作用已不再局限于科学计算&#xff0c;而更多的用于控制&#xff0c;管理及数据处理等非数值计算的处理工作。而它加工出理的对象也由纯粹的数值发展到字符&#xff0c;表格和图像等各种具有一定结构的数据。…

overleaf上踩的坑~

IEEE模板伪代码 问题&#xff1a; 格式显示不对&#xff0c;且 \\ 换行后不会自动标序号 解决办法&#xff1a; 在宏包中只保留 \usepackage[algo2e,linesnumbered,lined,boxed,commentsnumbered,ruled]{algorithm2e} 删除其他伪代码宏包&#xff1a; %\usepackage{algorith…