ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

栈(Stack)和队列(Queue)

2022-02-28 22:33:29  阅读:174  来源: 互联网

标签:队列 Queue int second train offset include Stack first


文章目录

栈和队列可以视为数组和链表的限制版本。

应用

括号匹配

问题描述:对一个字符串的左右括号进行匹配。

解题思路:遇到左括号,入栈。遇到右括号,出栈,若没元素,显然是不匹配的。字符串遍历后,如果栈里面还有元素,那么也是不匹配的。

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

void printMatchedPairs(string expr);

int main() {
	string expr = "(a*(b+c)+d)";
	printMatchedPairs(expr);
	expr = "(a*(b+c)+d";
	printMatchedPairs(expr);
}

void printMatchedPairs(string expr) {
	stack<char, vector<char>> seq;
	for (auto& s : expr) {
		if (s == '(')
			seq.push(s);
		if (s == ')') {
			if (seq.empty()) { cout << "not matching" << endl; return; }
			else seq.pop();
		}
	}
	if (seq.empty()) cout << "matching" << endl;
	else cout << "not matching" << endl;
	return;
}

汉诺塔

问题描述:假设有n个碟子和3座塔。初始时所有碟子从大到小堆在塔1上,我们要把碟子都移动到塔2上,每次移动一个,而且任何时候都不能把大碟子压在小碟子上。

解题思路:三个塔,分为起始塔,目标塔,中转塔。每一次都是:将目标塔上n-1个砖放到中转塔,将最后一块砖放到目标塔,最后将中转塔上的砖放到目标塔。然后问题就变成了如何将目标塔上的n-1个砖放到中转塔,本质还是之前的那个思路,于是一个递归的方法就显而易见了。

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

vector<stack<int, vector<int>>> tower;

void move(int, int, int, int);
int main() {	
	tower.assign(3, {});
	for (int i = 64; i >= 1; i--)
		tower[0].push(i);
	move(tower[0].size(), 0, 1, 2);
	cout << tower[1].size() << endl;
	return 0;
}

void move(int n, int x, int y, int z) {
	if (n > 0) {
		move(n - 1, x, z, y);
		tower[y].push(tower[x].top());
		tower[x].pop();
		move(n - 1, z, y, x);
	}
}

在这个问题中给我们的启发是,递归的方法适用于循环解决同一流程的问题。

在解决这一问题中,汉诺塔的移动方式则是典型的先进后出,适合用栈的数据结构来表示塔。

列车车厢重排

问题描述:
一列货运列车有n节车厢,每节车厢要停靠在不同的车站。假设n个车站从1到n编号,而且货运列车按照从n到1的顺序经过车站。车厢的编号与它们要停靠的车站编号相同。为了便于从列车上卸掉相应的车厢,必须按照从前到后、从1到n的顺序把车厢重新排列。

车厢的重排工作在一个转轨站,其中有3个缓冲轨道H1、H2和H3。开始时挂有n节车厢的火车开始在入轨道,而最后在出轨道上的顺序是从右到左,即从1~n。

问题分析:
一节节车厢进入排序流程,如果它就是我要排出去的车厢,那么就不用进入缓冲轨道,如果它不是的话那么进入缓冲轨道,且要找到比他大的,又离他最近的一个数所在的轨道。

#include <vector>
#include <stack>
using namespace std;
// 全局变量
vector<stack<int,vector<int>>> track;
int numberOfCars;		// 车厢的数量
int numberOfTracks;		// 轨道的数量
int smallestCar;		// 在缓冲轨道中编号最小的车厢
int itsTrack;			// 停靠着最小编号车厢的缓冲轨道

void outputFromHoldingTrack() {
	// 将编号最小的移出轨道
	// 从栈itsTrack中删除编号最小的车厢
	track[itsTrack].pop();
	// 删除完了之后就要在所有栈顶中找到最小的车厢和其所在轨道
	smallestCar = numberOfCars + 2;
	for(int i=1;i<=numberOfTracks;i++)
		if (!track[i].empty() && (track[i].top() < smallestCar)) {
			smallestCar = track[i].top();
			itsTrack = i;
		}
}

bool putInHoldingTrack(int c) {
	// 将车厢c移到一个缓冲轨道。返回false,当且仅当没有可用的缓冲轨道
	// 为车厢c寻找合适的缓冲轨道
	// 初始化
	int bestTrack = 0;
	int bestTop = numberOfCars + 1;
	// 扫描缓冲轨道
	for (int i = 1; i <= numberOfTracks; i++)
		if (!track[i].empty()) {
			int topCar = track[i].top();
			// 遵循原则:找到里这个数最近的,但又比它大一些的轨道
			if (c < topCar && topCar < bestTop) {
				bestTop = topCar;
				bestTrack = i;
			}
		}
		else // 如果缓冲轨道为空且没有更好的选择
			if (bestTrack == 0) bestTrack = i;
	if (bestTrack == 0) return false;
	track[bestTrack].push(c);
	if (c < smallestCar) {
		smallestCar = c;
		itsTrack = bestTrack;
	}
	return true;
}
bool railroad(int inputOrder[], int theNumberOfCars, int theNumberOfTrackers) {
	// 从初始顺序开始重排车厢
	// 如果重排成功,返回true,否则返回false

	numberOfCars = theNumberOfCars;
	numberOfTracks = theNumberOfTrackers;

	// 创建用于缓冲轨道的栈 这里是轨道数+1 目的就是考虑到了不存在的情况,如果不存在,那么我的bestTrack就是0
	track.assign(numberOfTracks + 1, {});

	int nextCarToOutput = 1; // 希望移出去的车厢号
	smallestCar = numberOfCars + 1; // 缓冲轨道中无车厢

	// 重排车厢
	for (int i = 1; i <= numberOfCars; i++)
		if (inputOrder[i] == nextCarToOutput) {
			// 如果将要排位的车厢刚好就是我要移出去的,那么直接移出去
			// 将车厢inputOrder[i]直接移出轨道
			nextCarToOutput++;
			// 移出去之后看看当前轨道里有没有也可以移出去的
			while (smallestCar == nextCarToOutput) {
				outputFromHoldingTrack();
				nextCarToOutput++;
			}
		}
		else
			if (!putInHoldingTrack(inputOrder[i]))
				return false;
	return true;

该题基于栈的数据结构,抓住离开候选轨道、进入候选轨道的条件完成了此次排序。

离线等价类问题

问题描述:输入元素、关系,将这n个元素划分为等价类。

求解策略:

  1. 第一阶段,输入数据,建立n个表以表示关系对;
  2. 第二阶段,寻找等价类,是深度优先的策略。

离线等价类本质就是每个等价类形成了一棵树,通过深度优先遍历出来。

在线等价类问题就是知道元素,知道两两之间是否等价,将这n个元素划分为等价类。

在线等价类就是通过关系一步步把这棵树搭建起来。

目的都是为了划分等价类,不过两者对于等价关系的应用不一样。

布线问题

问题介绍及思路

关键还是把握住出栈和入栈的条件。每一个线都把这个盒分了个区,所以说当这个线想要出栈时,区域内的线应该都出去了。有点类似于括号匹配问题。

配对问题通常考虑栈。

迷宫老鼠

寻找一条从入口到出口的路径。

首先把迷宫入口作为当前位置。如果当前位置是迷宫出口,那么已经找到了一条路径,寻找工作结束。如果当前位置不是迷宫出口,则在当前位置上放置障碍物,以阻止寻找过程又绕回到这个位置。

然后检查相邻位置是否有空闲。如果有,就移动到一个空闲的相邻位置,然后从这个位置开始寻找通往出口的路径。

为了方便移动,在进入新的相邻位置之前,把当前位置保存在一个栈中。

如果所有空闲的相邻位置都已经被探索果,但未能找到路径,则表明迷宫不存在此路径。

#include <iostream>
#include <stack>
#include <vector>
#include <utility>

using namespace std;
using container = stack<pair<int, int>, vector<pair<int, int>>>;
using node = pair<int, int>;
vector<vector<int>> maze = {
	{1,1,1,1,1,1,1,1,1,1,1,1},
	{1,0,1,1,1,1,1,0,0,0,0,1},
	{1,0,0,0,0,0,1,0,1,0,0,1},
	{1,0,0,0,1,0,1,0,0,0,0,1},
	{1,0,1,0,1,0,1,0,1,1,0,1},
	{1,0,1,0,1,0,1,0,1,0,0,1},
	{1,0,1,1,1,0,1,0,1,0,1,1},
	{1,0,1,0,0,0,1,0,1,0,1,1},
	{1,0,1,0,1,1,1,0,1,0,0,1},
	{1,1,0,0,0,0,0,0,1,0,0,1},
	{1,0,0,0,0,1,1,1,1,0,0,1},
	{1,1,1,1,1,1,1,1,1,1,1,1}
};



container find_path() {
	container path;
	// 设置起点和终点
	node start = { 1,1 };
	node exit = { 10,10 };
	// 初始化偏移量
	pair<int, int> offset[4];
	offset[0].first = 0; offset[0].second = 1; // right
	offset[1].first = 1; offset[1].second = 0; // down
	offset[2].first = 0; offset[2].second = -1; // left
	offset[3].first = -1; offset[3].second = 0; // up
	
	node current_location = start;
	maze[start.first][start.second] = 1;
	int option = 0;
	int lastOption = 3;

	while (current_location != exit) {
		int r, c;
		// 找无障碍的路
		while (option <= lastOption) {
			r = current_location.first + offset[option].first;
			c = current_location.second + offset[option].second;
			if (maze[r][c] == 0) break;
			option++;
		}
		// 找到了,进行更新
		if (option <= lastOption) {
			path.push(current_location);
			current_location.first = r;
			current_location.second = c;
			maze[r][c] = 1;
			option = 0;
		}
		// 没找到,进行回溯或者返回未完成的路径
		else {
			if (path.empty()) return path;
			node next = path.top();
			path.pop();
			if (next.first == current_location.first) {
				option = 2 + next.second - current_location.second;
			}
			else option = 3 + next.first - current_location.first;
			current_location = next;
		}
	}
	return path;
}

int main() {
	container path = find_path();
	while (!path.empty()) {
		cout << path.top().first << path.top().second << endl;
		path.pop();
	}
}

从该代码中,可以学到的方法有:01地图表示、用预设的偏移量来表示移动。

综上所述,栈通常用于配对问题(括号匹配、布线问题)或者针对实际应用中事物的移动情况(汉诺塔、列车重排、离线等价类问题)或者搜索问题,尤其是深度优先(迷宫老鼠)。

队列

应用

列车车厢重排

#include <iostream>
#include <queue>
#include <list>
#include <vector>
#include <deque>
#include <utility>
using namespace std;
using tracks = vector<queue<int,deque<int>>>;

tracks trs = { {},{},{} };
int next_train = 1;
pair<int, int> min_train = { 0,11 };

list<int> clear_the_rest(list<int> target);
list<int> get_trains(list<int> trains);

int main() {
	list<int> trains = { 5,8,1,7,4,2,9,6,3 };
	list<int> target = get_trains(trains);
	for (auto& t : target) cout << t << endl;
	return 0;
}

list<int> get_trains(list<int> trains) {
	list<int> target = {};
	int len = trains.size();
	for (int i = 0; i < len; i++) {
		// 弹出一节车厢
		int train = trains.back();
		trains.pop_back();
		// 判断是否可以直接驶出轨道
		if (train == next_train) {
			target.push_front(next_train);
			next_train++;
			// 驶出之后,再看看当前轨道中有没有可以续上的车厢
			target = clear_the_rest(target);
		}
		// 如果不可以,则将该车厢安排进轨道
		else {
		// 候选驶入轨道及其末尾车厢号,因为空视为车厢号为0,所以这里需要设置为-1
			pair<int, int> wait_tr = { 0,-1 };
			for (int i = 0; i < 3; i++) {
				int max;
				if (trs[i].empty()) max = 0;
				else max = trs[i].back();
				// 找到仅次于驶入车厢的最大车厢号
				if (train > max && max > wait_tr.second) {
					wait_tr.first = i;
					wait_tr.second = max;
				}	
			}
			// 实时更新最小车厢号及其位置
			trs[wait_tr.first].push(train);
			if (min_train.second > train) {
				min_train.first = wait_tr.first;
				min_train.second = train;
			}
		}
	}
	return target;
}

list<int> clear_the_rest(list<int> target) {
	while (min_train.second == next_train) {
		target.push_front(next_train);
		next_train++;
		trs[min_train.first].pop();
		
		min_train = { 0,11 };
		
		for (int i = 0; i < 3; i++) {
			if (trs[i].empty()) continue;
			if (trs[i].front() < min_train.second) {
				min_train.first = i;
				min_train.second = trs[i].front();
			}
		}
	}
	return target;
}

电路布线(Lee算法)

Lee算法是一种从网格中寻找最短路径的经典算法。

比如说,我们要寻找a和b之间的最短路径。a和b之间的最短路径需要在两个过程中确定。一个是距离标记的过程,另一个是路径标记的过程。

在距离标记过程中,从位置a开始,把从a可到达的相邻方格都标记为1,然后把标记为1的方格可到达的方格标记为2,以此类推。直到到达b或者没有可到达的相邻的方格为止。

一旦到达b,b的编号便是b与a之间的距离。之后,路径标记过程开始,从方格b开始,首先移动到比b小1的相邻位置。重复这个过程,直到到达a为止。

#include <queue>
#include <utility>
using namespace std;
using node = pair<int, int>;

bool findPath() {
	node entrance = { 1,1 };
	node exit = { 10,10 };
	
	const int size = 10;
	int grid[size + 2][size + 2];

	if (entrance == exit) {
		const int pathLength = 0;
		return true;
	}
	// 初始化偏移量
	pair<int, int> offset[4];
	offset[0].first = 0; offset[0].second = 1; // right
	offset[1].first = 1; offset[1].second = 0; // down
	offset[2].first = 0; offset[2].second = -1; // left
	offset[3].first = -1; offset[3].second = 0; // up

	// 初始化网格四周的障碍物
	for (int i = 0; i <= size + 1; i++) {
		grid[0][i] = grid[size + 1][i] = 1;
		grid[i][0] = grid[i][size + 1] = 1;
	}

	node here = entrance;
	// 0表示无障碍且未标记,1表示障碍;于是起点设置为2,只要将距离-2就可得到真实距离。
	grid[entrance.first][entrance.second] = 2; 
	int numOfNbrs = 4; // 一个方格的相邻位置数;
	node nbr;
	queue<node, deque<node>> q;
	// 距离标记
	do {
		for (int i = 0; i < numOfNbrs; i++) {
			nbr.first = here.first + offset[i].first;
			nbr.second = here.second + offset[i].second;
			if (grid[nbr.first][nbr.second] == 0) {
				grid[nbr.first][nbr.second] = grid[here.first][here.second] + 1;
				if (nbr == exit) break;
				q.push(nbr);
			}
		}
		if (nbr == exit) break;
		if (q.empty()) return false;
		here = q.front();
		q.pop();
	} while (true);
	// 路径构造
	const int pathLength = grid[exit.first][exit.second] - 2;
	node *path = new node[pathLength];
	here = exit;
	for (int j = pathLength - 1; j >= 0; j--) {
		path[j] = here;
		for (int i = 0; i < numOfNbrs; i++) {
			nbr.first = here.first + offset[i].first;
			nbr.second = here.second + offset[i].second;
			if (grid[nbr.first][nbr.second] == j + 2) break;
		}
		here = nbr;
	}
	return true;

}

标签:队列,Queue,int,second,train,offset,include,Stack,first
来源: https://blog.csdn.net/weixin_46365033/article/details/122943304

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有