栈、队列与优先队列

目录

1.栈

概念

例题(集合栈计算机)

2.队列

概念

例题(团队队列)

3.优先队列

概念

例题(丑数)


1.栈

概念

栈,是符合“后进先出”(Last In First Out,LIFO)规则规则的数据结构,有PUSH和POP两种操作,其中PUSH把元素压入“栈顶”,而POP从栈顶把元素“弹出

STL的stack头文件提供了栈,用"stack<int> s"方式定义,用push()和pop()实现元素的入栈和出栈操作,top()取栈顶元素(但不删除)。

例题(集合栈计算机)

问题描述

有一个专门为了集合运算而设计的“集合栈”计算机。该机器有一个初始为空的栈,并且支持以下操作。

PUSH:空集“{}”入栈。

DUP:把当前栈顶元素复制一份后再入栈。

UNION:出栈两个集合,然会把两者的并集入栈。

INTERSECT:出栈两个集合,然后把二者的交集入栈。

ADD:出栈两个集合,然后把先出栈的集合加入到后出栈的集合中,把结果入栈。

每次操作后,输出栈顶集合的大小(即元素个数)。例如,栈顶元素是A={{},{{}}},下一个元素是B={{},{{{}}}},则:

UNION操作将得到{{},{{}},{{{}}}},输出3。

INTERSECT操作将得到{{}},输出1。

ADD操作将得到{{},{{{】}},{{},{{}}}},输出3.

输入不超过2000个操作,并且保证操作均能顺利进行(不需要对空栈执行出栈操作)。

示例代码

#include<iostream>
#include<cctype>
#include<vector>
#include<string>
#include<map>
#include<algorithm>
#include<stack>
#include<set>
#define ALL(x) x.begin(),x.end()//表示“所有的内容”
#define INS(x) inserter(x,x.begin())//表示“插入的迭代器”
using namespace std;
typedef set<int> Set;
map<Set, int> IDcache;//把集合映射成ID
vector<Set> Setcache;//根据ID取集合
//查找给定集合x的ID。如果找不到,分配一个新ID
int ID(Set x) {
	if (IDcache.count(x)) {//如果能找到
		return IDcache[x];//输出旧ID
	}
	Setcache.push_back(x);//如果找不到,压入栈
	return IDcache[x] = Setcache.size() - 1;//输出新ID
}
int main() {
	stack<int> s;//题目中的栈
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {//n个操作
		string op;
		cin >> op;
		if (op[0] == 'P') {//“{}入栈”
			s.push(ID(Set()));
		}
		else if (op[0] == 'D') {//栈顶元素入栈
			s.push(s.top());
		}
		else {
			Set x1 = Setcache[s.top()];//元素A
			s.pop();
			Set x2 = Setcache[s.top()];//元素B
			s.pop();
			Set x;
			if (op[0] == 'U') {//并
				set_union(ALL(x1), ALL(x2), INS(x));
			}
			if (op[0] == 'I') {//交
				set_intersection(ALL(x1), ALL(x2), INS(x));
			}
			if (op[0] == 'A') {//加
				x = x2;
				x.insert(ID(x1));
			}
			s.push(ID(x));
		}
		cout << Setcache[s.top()].size() << endl;
	}
	return 0;
}

分析

本题的集合并不是简单的整数集合或者字符串集合,而是集合的集合。为了方便起见,此处为每个不同的集合分配一个唯一的ID,则每个集合都可以表示成所含元素的ID集合,这样就可以用STL的set<int>来表示了,而整个栈则是一个stack<int>。 

2.队列

概念

队列是符合“先进先出”(First In Fisrt Out,FIFO)原则的“公平队列”。

STL队列定义在头文件<queue>中,可以用“queue<int> s”方式定义,用push()和pop()进行元素的入列和出队操作,front()取队首元素(但不删除)。

例题(团队队列)

问题描述

有t个团队的人正在排一个长队。每次新来一个人时,如果他有队友在排队,那么这个新人会插到最后一个队友的身后。如果没有任何一个队友排队,则他会排到长队的队尾。

输入每个团队中所有队员的编号,要求支持如下三种指令(前两种指令可以穿插进行)。

ENQUEUE x:编号为x的人进入长队。

DEQUEUE:长队的队首出队。

STOP:停止模拟。

对于每个DEQUEUE指令,输出出队的人的编号。

示例代码

#include<cstdio>
#include<queue>
#include<map>
using namespace std;
const int maxt = 1000 + 10;
int main(){
	int t, kase = 0;
	while (scanf("%d", &t) == 1 && t) {
		printf("Scenerio #%d\n", ++kase);
		//记录所有人的团队编号
		map<int, int>team;
		for (int i = 0; i < t; i++) {//t个团队
			int n, x;
			scanf("%d", &n);
			while (n--) {
				scanf("%d", &x);
				team[x] = i;
			}
		}
		//模拟
		queue<int> q, q2[maxt];//q是团队的队列,而q2[i]是团队i成员的队列
		for (;;) {
			int x;
			char cmd[10];
			scanf("%d", cmd);//命令
			if (cmd[0] == 'S') {//停止模拟
				break;
			}
			else if (cmd[0] == 'D') {//长队的队首出列
				int t = q.front();//取队列的首号
				printf("%d\n", q2[t].front());
				q2[t].pop();//出队
				if (q2[t].empty()) {//团体t全体出队列
					q.pop();
				}
			}
			else if (cmd[0] == 'E') {//编号为x的人进入队列
				scanf("%d", &x);
				int t = team[x];//找到队伍编号
				if (q2[t].empty()) {//团队t进入队列
					q.push(t);
				}
			}
		}
		printf("\n");
	}
	return 0;
}

分析

每个团队有一个队列,而团队整体又形成一个队列。

3.优先队列

概念

优先队列是一种抽象数据类型(Abstract Data Type,ADT),行为有些像队列,但先出队列的元素不是先进入队列的元素,而是队列中优先级最高的元素。

STL的优先队列也定义在头文件<queue>里,用"priority_queue<int> pq"来声明。这个pq是一个“越小的整数优先级越低的优先队列”。由于出队元素并不是最先进队的元素,出队的方法由queue的front()变成了top()。

自定义类型也可以组成优先队列,但必须为每个元素定义一个优先级。这个优先级并不需要一个确定的数字,只需要能比较大小即可。

STL的queue头文件提供了优先队列,用"priority_queue<int>s"方式定义,用push()和pop()进行元素的入队和出队操作,top()取队首元素(但不删除)。

例题(丑数)

问题描述

丑数是指不能被2,3,5以外的其他素数整除的数。把丑数从小到大排列起来,结果如下:

1,2,3,4,5,6,8,9,10,12,14, ....

求第1500个丑数。

示例代码

#include<vector>
#include<queue>
#include<set>
#include<iostream>
using namespace std;
typedef long long LL;
const int coeff[3] = { 2,3,5 };
int main(){
	priority_queue<LL, vector<LL>, greater<LL> > pq;
	set < LL> s;
	pq.push(1);
	s.insert(1);
	for (int i = 1;; i++) {
		LL x = pq.top();
		pq.pop();
		if (i == 1500) {
			cout << "The 1500'th ugly number is" << x << ".\n";
			break;
		}
		for (int j = 0; j < 3; j++) {
			LL x2 = x*coeff[j];
			if (!s.count(x2)) {//如果不存在x2
				s.insert(x2);
				pq.push(x2);
			}
		}
	}
	return 0;
}

分析

从小到大生成各个丑数,最小的丑数是1,而对于任意丑数x,2x,3x,5x也都是丑数。这样可以用一个优先队列保存所有已经生成的丑数,每次取出最小的丑数,生成3个新的丑数。唯一需要注意的是,同一个丑数有多种生成方式,所以需要判断一个丑数是否已经生成过。 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值