草庐IT

stack_queue | priority_queue | 仿函数

风起、风落 2023-04-15 原文

文章目录

1. stack 的使用

栈不在是一个容器,而是一个容器适配器 ,
stack的模板中第二个deque暂时不知道干什么的,后面会说


说明stack是一个容器适配器,并且为了保证严格的先进后出,所以不存在迭代器


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


int main()
{
	stack<int>v;
	v.push(1);
	v.push(2);
	v.push(3);
	v.push(4);
	while (!v.empty())//后进先出的原则
	{
		cout << v.top() << " ";//4 3 2 1
		v.pop();
	}
	return 0;
}

2. stack的模拟实现

#include<iostream>
#include<vector>
#include<list>
#include<stack>
#include<queue>
using namespace std;
namespace yzq
{
	//适配器模式
	template<class T, class Container = deque<T>>//给与缺省值
	//deque 作为一个双端队列,可以非常适用与大量 头插 头删 尾插 尾删 的情况
	class stack
	{
	public:
		void push(const T& x)//插入
		{
			_con.push_back(x);//尾插
		}
		void pop()//删除栈顶元素
		{
			_con.pop_back();  
		}
		const T& top()//栈顶
		{
			return _con.back();
		}
		size_t size()//大小
		{
			return _con.size();
		}
		bool empty()//判空
		{
			return _con.empty();
		}
	private:
		Container _con;//Container 是一个符合要求的容器

	};//适配器模式
	void test()
	{
		yzq::stack<int>v;
		v.push(1);
		v.push(2);
		v.push(3);
		v.push(4);

		while (!v.empty())
		{
			cout << v.top() << " ";
			v.pop();
		}
		cout << endl;
	}
	
	
}



这里假设我们不认识 deque,那么如果stack频繁使用pop尾删,将vector< T >设置成缺省值也是非常适合的

3. queue的使用

队列同样不在是一个容器,而是一个容器适配器


说明queue为了保证严格的先进先出,所以不存在迭代器



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



int main()
{
	queue<int>v;
	v.push(1);
	v.push(2);
	v.push(3);
	v.push(4);
	while (!v.empty())//符合先进先出的原则
	{
		cout << v.front() << " ";//1 2 3 4
		v.pop();
	}
	return 0;
}

4. queue的模拟实现

#pragma once
namespace yzq
{
	template<class T, class Container=deque<T>>//给与缺省值,默认使用list<T>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}
		void pop()
		{
			_con.pop_front();  //vector没有头删,强行使用erase 效率太低
		}
		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;//Container 是一个符合要求的容器
		
	};
}

这里假设我们不认识 deque,那么如果stack频繁使用pop头删,将 list< T >设置成缺省值也是非常适合的

5. deque ——双端队列


可以在头尾双端进行插入和删除的操作,且时间复杂度为O(1),与vetcor比较,头插效率高,不需要移动元素,
与list比较,空间利用比较高



而deque就是要综合两者的优点 (想法很美好)
deque并不是真正的连续的空间,而是由一段段连续的小空间拼接而成的
一段段的小数组,若满了不扩容,在开辟一块空间,通过中控(指针数组)的方式将一个个小数组管理起来
第一个buf数组,开的是中控指针数组中间的位置

头插:


尾插:


若中控满了,就要扩容,但是扩容代价低
若为vector,本来为100个int空间,需要200个空间,就需要扩容2倍
而 中控是一个指针数组,里面都是指针,可能只需要20个指针就搞定了,所以扩容代价相对于低一些


deque优缺点

优点:
1.相比于vector,扩容代价低
2. 头插 头删,尾插尾删效率高
3. 支持随机访问


缺点:
1.中间插入删除很难搞
若中间插入删除效率高,会影响随机访问的效率,牺牲中间插入删除的效率,随机访问效率就变高了
2.没有vector和list优点极致
deque你说它跟vector比随机访问,速度不如vector,跟list比任意位置插入删除,效率没list高 ,这种就搞的很难啦,哪一项都不突出,但是都会一点


栈和队列都是需要大量的头插头删,尾插尾删的,而deque在这个场景下效率很高,所以deque被当作栈和队列的默认适配容器

6. priority_queue ——优先级队列

1. priority_queue的使用



底层是一个堆,默认容器使用vector, 最后一个模板参数代表仿函数 默认使用 less 代表小于 (后面会讲)



#include<iostream>
#include<queue>
#include<functional>
using namespace std;
int main()
{
	priority_queue<int, vector<int>>v;
	//默认为大堆,数据大的优先级高
	v.push(4);
	v.push(8);
	v.push(6);
	v.push(2);
	while (!v.empty())
	{
		cout << v.top() << " "; //8 6 4 2
		v.pop();
	}
	return 0;
}

正常来说,默认建立大堆,所以数据大的优先级高


#include<iostream>
#include<queue>
#include<functional>
int main()
{
	priority_queue<int,vector<int>,greater<int>>v;//greater作为仿函数
	//设置小堆,让小的优先级高
	v.push(4);
	v.push(8);
	v.push(6);
	v.push(2);
	while (!v.empty())
	{
		cout << v.top() << " "; //2 4 6 8
		v.pop();
	}
	return 0;
}

但若加入仿函数 greater 后,则会建立小堆,所以数据小的优先级高

2. priority_queue的模拟实现

由于是自己实现的所以要加上命名空间,避免冲突

push——插入

void adjustup(int child)//向上调整算法
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				//建大堆
				if (com(_con[parent] ,_con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
void push(const T&x)
		{
			_con.push_back(x);
			adjustup(_con.size() - 1);//向上调整算法
		}

由于是堆的插入,而堆本身也是由数组的数据构成的,所以数组加入一个数据相当于在堆最后插入一个数据,在通过向上调整算法依次交换, 不懂向上调整算法的点击这里

pop ——删除

void adjustdown(int parent)//向下调整算法
		{
			Compare com;
			int child = parent * 2+1;//假设为左孩子
			while (child<_con.size())
			{
				//建大堆
				if (child+1 < _con.size()&&com(_con[child],_con[child + 1]))
					//child+1是防止右孩子不存在导致越界
				{
					child++;
				}
				if (com(_con[parent] ,_con[child]))//将两者换一种说法,使之能=能够调用<即可
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);//首尾交换
			_con.pop_back();//尾删
			adjustdown(0);//向下调整算法
		}

由于要删除堆顶的数据,所以交换堆尾与堆顶数据,在删除堆尾数据,默认容器为vector,所以有pop_back 尾删的功能,而此时堆顶的数据不符合当前的位置,所以需要借助向下调整算法把该数据调整到合适的位置 不懂向下调整算法的点击这里

top —— 堆顶

const T& top()
		{
			return _con[0];
		}

堆是借助数组来实现的,所以堆顶的数据就是当前的第一个数据

仿函数问题

template <class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template <class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

仿函数主要是借助两个类 来重载 运算符 ( ) ,
而 Less ( x<y) 用于 建立大堆 ,greater (x>y) 用于建立小堆


Less / greater 分别都是类名 ,而模板参数 Compare 需要类型
所以 都需要加上 ,即 Less< T> / greater < T >


通过该类型在向上/向下调整算法中分别建立对象,通过对象调用对应类less/greater的重载()从而达到目的


若为默认类型Less,则调用x <y ,从而建大堆


当传入greater< T >类型后,调用对象,找到对应greater类型的重载() ,调用 x >y ,从而建小堆

完整代码实现

#include<iostream>
#include<queue>
using namespace std;
namespace yzq
{
	template <class T>
	struct Less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	template <class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};
	template<class T, class Container=vector<T>,class Compare=Less<T> >
	class priority_queue//优先级队列
	{
	public:
		void adjustup(int child)//向上调整算法
		{
			Compare com;
			int parent = (child - 1) / 2;
			while (child > 0)
			{
				//建大堆
				if (com(_con[parent] ,_con[child]))
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}
		void adjustdown(int parent)//向下调整算法
		{
			Compare com;
			int child = parent * 2+1;//假设为左孩子
			while (child<_con.size())
			{
				//建大堆
				if (child+1 < _con.size()&&com(_con[child],_con[child + 1]))
					//child+1是防止右孩子不存在导致越界
				{
					child++;
				}
				if (com(_con[parent] ,_con[child]))//将两者换一种说法,使之能=能够调用<即可
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}
		void push(const T&x)
		{
			_con.push_back(x);
			adjustup(_con.size() - 1);//向上调整算法
		}
		void pop()
		{
			swap(_con[0], _con[_con.size() - 1]);//首尾交换
			_con.pop_back();//尾删
			adjustdown(0);//向下调整算法
		}
		const T& top()
		{
			return _con[0];
		}
		size_t size()
		{
			return _con.size();
		}
		bool empty()
		{
			return _con.empty();
		}
	private:
		Container _con;//使用vector实现
	};
}

int main()
{
	//yzq::priority_queue<int>v;
	yzq::priority_queue<int,deque<int>,greater<int>>v;
	v.push(1);
	v.push(5);
	v.push(8);
	v.push(4);
	while (!v.empty())
	{
		cout << v.top() << " ";
		v.pop();
	}
	return 0;
}

有关stack_queue | priority_queue | 仿函数的更多相关文章

  1. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  2. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  3. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  4. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  5. C51单片机——实现用独立按键控制LED亮灭(调用函数篇) - 2

    说在前面这部分我本来是合为一篇来写的,因为目的是一样的,都是通过独立按键来控制LED闪灭本质上是起到开关的作用,即调用函数和中断函数。但是写一篇太累了,我还是决定分为两篇写,这篇是调用函数篇。在本篇中你主要看到这些东西!!!1.调用函数的方法(主要讲语法和格式)2.独立按键如何控制LED亮灭3.程序中的一些细节(软件消抖等)1.调用函数的方法思路还是比较清晰地,就是通过按下按键来控制LED闪灭,即每按下一次,LED取反一次。重要的是,把按键与LED联系在一起。我打算用K1来作为开关,看了一下开发板原理图,K1连接的是单片机的P31口,当按下K1时,P31是与GND相连的,也就是说,当我按下去时

  6. ruby-on-rails - 将字符串转换为 ruby​​-on-rails 中的函数 - 2

    我需要一个通过输入字符串进行计算的方法,像这样function="(a/b)*100"a=25b=50function.something>>50有什么方法吗? 最佳答案 您可以使用instance_eval:function="(a/b)*100"a=25.0b=50instance_evalfunction#=>50.0请注意,使用eval本质上是不安全的,尤其是当您使用外部输入时,因为它可能包含注入(inject)的恶意代码。另请注意,a设置为25.0而不是25,因为如果它是整数a/b将导致0(整数)。

  7. ruby - 在 ruby​​ 中使用 .try 函数和 .map 函数 - 2

    我需要从json记录中获取一些值并像下面这样提取curr_json_doc['title']['genre'].map{|s|s['name']}.join(',')但对于某些记录,curr_json_doc['title']['genre']可以为空。所以我想对map和join()使用try函数。我试过如下curr_json_doc['title']['genre'].try(:map,{|s|s['name']}).try(:join,(','))但是没用。 最佳答案 你没有正确传递block。block被传递给参数括号外的方法

  8. ruby - 是否可以从也在该模块中的类内部调用模块函数 - 2

    在这段Ruby代码中:ModuleMClassC当我尝试运行时出现“'M:Module'的未定义方法'helper'”错误c=M::C.new("world")c.work但直接从另一个类调用M::helper("world")工作正常。类不能调用在定义它们的同一模块中定义的模块函数吗?除了将类移出模块外,还有其他解决方法吗? 最佳答案 为了调用M::helper,你需要将它定义为defself.helper;结束为了进行比较,请查看以下修改后的代码段中的helper和helper2moduleMclassC

  9. ruby - 将运算符传递给函数? - 2

    也许这听起来很荒谬,但我想知道这对Ruby是否可行?基本上我有一个功能...defadda,bc=a+breturncend我希望能够将“+”或其他运算符(例如“-”)传递给函数,这样它就类似于...defsuma,b,operatorc=aoperatorbreturncend这可能吗? 最佳答案 两种可能性:以方法/算子名作为符号:defsuma,b,operatora.send(operator,b)endsum42,23,:+或者更通用的解决方案:采取一个block:defsuma,byielda,bendsum42,23,

  10. ruby - 我可以在 Ruby 1.9.x 中使用无参数函数吗? - 2

    所以我正在研究RubyKoans,而且我遇到了一个我认为是ruby1.9.x特有的问题。deftest_calling_global_methods_without_parenthesesresult=my_global_method2,3assert_equal5,resultend我明白了:james@tristan:~/code/ruby_projects/ruby_koans$rake(in/home/james/code/ruby_projects/ruby_koans)cdkoans/home/james/.rvm/rubies/ruby-1.9.2-p180/bin/ru

随机推荐