栈和队列面试精讲

堆栈和队列统称线性表

  1. 简单的线性结构
  2. 数组和链表可以实现这两种数据结构

堆栈

  • 后进先出(LIFO)
  • 用栈实现DFS的非递归遍历
栈的基本操作如下
 头文件为: #include<stack>
 定义:stack<type> s  type为数据类型(如int float char等)
 基本操作
 s.push(item)
 s.pop()  //删除栈顶元素,但是不返回
 s.top()
 s.size()
 s.empty()

队列

  • 先进先出(FIFO)
  • 用队列实现BFS遍历
队列的基本操作如下
头文件 #include\<queue>
queue<type> q   type为数据类型(如int float char等)
基本操作有
q.push(item)
q.pop()  //删除队首元素,但是不返回
q.front()
q.back()  //队列不仅可以访问队首,还可以访问队尾
q.size()
q.empty()

题一 元素出入栈的合法性判断。
给定一些元素的入栈顺序和出栈顺序,问是否可能?(假设所有元素都不相同)
【分析】根据栈的特性,栈顶进,栈顶出,则栈顶元素恰好等于要出栈的元素,则必须出栈,否则继续入栈。当栈压完的时候,也会被弹完,如果还有的话,则出入栈的顺序不合法

bool isPossible(vector<int> _in, vector<int> _out) {
	//使用in表示进栈顺序,out表示出栈顺序
	stack<int> s;
	int i, j;
	for (i = 0, j = 0; i < _in.size(); i++) {
		s.push(_in[i]);
		while ((!s.empty()) && (s.top() == _out[j])) {
			s.pop();
			j++; //当前要出栈的元素
		}
	}
	if (s.empty()) {
		return true;
	}
	return false;
}

题二 用两个队列实现堆栈
【分析】根据队列和堆栈的特点,两个队列来回倒,保证一个空队列,用于存放另一个队列除了队尾元素的其他元素。

typedef class _mystack {
public:
	void push(int a);
	int pop();  //pop是不返回值的
	int top();
	bool empty();
	int size();
private:
	queue<int> q1;
	queue<int> q2;
	int len;
}mystack;

void mystack::push(int a) {

	if (!this->q1.empty()) {
		this->q1.push(a);
	}
	else {
		this->q2.push(a);
	}
}

int mystack::pop() {
	if (this->q1.empty() && !this->q2.empty()) {
		for (int i = 0; i < q2.size() - 1; i++) {
			this->q1.push(this->q2.front());
			this->q2.pop();
		}
		this->q2.pop();
	}
	else if (this->q2.empty() && !this->q1.empty()) {
		for (int i = 0; i < this->q1.size()-1; i++) {
			this->q2.push(this->q1.front());
			this->q1.pop();
		}
		this->q1.pop();
	}
	else {
		printf("current stack is empty, can not pop!!\n");
		return -1;
	}
	return 0;
}

int mystack::top() {
	if (this->q1.empty() && !this->q2.empty()) {
		return this->q2.back();
	}
	else if (this->q2.empty() && !this->q1.empty()) {
		return this->q1.back();
	}
	else {
		printf("current stack is empty, can not pop!!\n");
		return -1;
	}
}

bool mystack::empty() {
	return (this->q1.empty() || this->q2.empty());
}

int mystack::size() {
	return this->q1.empty() ? this->q2.size() : this->q1.size();
}

题三,用两个栈实现一个队列
【分析】使用两个栈s1和s2,s1专门负责入队列,s2专门负责出队列,假如s2为空,就将整个s1弹出s2中。实际代码较长,下面只给出push()和pop()两部分的代码。

stack<int> s1;  // 负责入队列
stack<int> s2;  // 负责出队列
void push(int a) {
	s1.push(a);
}
void pop() {
	if (s2.empty()) {
		while (!s1.empty()) {
			s2.push(s1.top());
			s1.pop();
		}
	}
	s2.pop();
}

题四 支持查找最小元素的堆栈
一个堆栈除了支持push, pop以外还要支持一个操作getMin得到当前堆栈里所有元素的最小值
【分析】1. 笨方法是再开辟一个栈,要getMin的时候,将元素一个一个倒到新栈中,并顺便求最小值,最后再倒回原堆栈
2. 好一点的办法,是开辟一个栈stackMin,用来维护到目前为止的最小值,插入一个新值,如果比当前最小值小或者等于,则压入stackMin中,如果比当前stackMin.top()大的话,则维持stackMin不动.
从堆栈中弹出元素,如果弹出元素比stackMin.top()大的话,则维持stackMin不动,如果等于stackMin.top()则弹出stackMin的栈顶元素。下面贴出代码,供参考。

typedef class _minstack {
public:
	void push(int a);
	void pop();
	int getMin();
private:
	stack<int> sc;
	stack<int> scMin;
}minstack;

void minstack::push(int a) {
	sc.push(a);
	if (scMin.empty()) {
		scMin.push(a);
	}
	else if(a <= scMin.top()){
		scMin.push(a);
	}
}
void minstack::pop() {
	if (sc.empty()) {
		printf("stack is empty\n");
		return;
	}
	if (sc.top() = scMin.top()) {
		scMin.pop();
		sc.pop;
		return;
	}
	sc.pop();
}

题五 最大直方图
给出一个直方图,求最大面积矩形
在这里插入图片描述
【分析】如上图,想求矩形面积,得知道当前高度,左边界,右边界。可以使用堆栈来确定左右边界
对于每一块板,当前栈顶板子如果高于要加入的板子,则当前板子的右边界确定,左边界就是上一块板子,如果当前栈顶板子低于要加入的板子,则要加入的板子的左边界确定。可知堆栈里的元素是递增的。值得注意的是 1)栈底元素是目前最小的元素,所以左边界是-1。2)堆栈最后剩下的元素是的递增的,我们需要再循环计算一次。现贴出实现上述思想的代码:

#include <stdio.h>
#include <stack>
#include <vector>
#include <algorithm>

using namespace std;
int getMaxRec(vector<int> &rec) {
	int res = 0;
	int len = rec.size();
	int i;
	stack<int> stc;
	for (i = 0; i < len; i++) {
		while (!stc.empty() && rec[i] < rec[stc.top()]) {
			int h = rec[stc.top()];
			//栈底元素是到目前位置最小的数,则左边界是0
			res = max(res, (h * (i - (stc.size() == 1 ? 0 : stc.top()))));
			stc.pop();
		}
		stc.push(i);
	}
	i = stc.top();
	while (!stc.empty()) {
		int h = rec[stc.top()];
		res = max(res, (h * (1 + i - (stc.size() == 1 ? 0 : stc.top()))));
		stc.pop();
	}
	return res;
}
void main() {
	vector<int> rec = { 2,1,5,6,2,3 };
	int m = getMaxRec(rec);
	printf("max rec is %d", m);
	system("pause");
	return;
}

题六 滑动窗口最大值
给定一个数组a[0…n],还有一个值k,计算数组b[i] = max(a[i – k + 1… i]) 注意认为负数下标对应值是无穷小。
分析:可以采用滑动窗口的思想,如果同时存在一个旧的数x,和一个新的数y并且x ≤ y,则x永远不会是我们要的解。我们使用一个双端队列,来模拟窗口,旧的值从队列前端删除,要加入的数,如果比队尾元素大的话,则队尾无效的数要被删除。总的来看要维护队列的单调性(递减的)。

#include <stdio.h>
#include <deque>
#include <vector>
using namespace std;

int winMax(vector<int> a, int k) {
	if (a.size() < k) {
		printf("size is error\n");
		return -1;
	}
	vector<int> b;
	deque<int> dq;  // 窗口
	for (int i = 0; i < a.size(); i++) {
		while (!dq.empty() && dq.front() <= i - k) dq.pop_front();
		while (!dq.empty() && a[i] >= a[dq.back()]) dq.pop_back();
		dq.push_back(i);  //队列中存放的是元素序号
		b.push_back(a[dq.front()]);
	}
	
	for (int i = 0; i < b.size(); i++) {
		printf("%d\t", b[i]);
	}
}
void main() {
	vector<int> a = { 5,1,4,3,2,4 };
	winMax(a, 3);
	system("pause");
}

对于上述代码的理解
旧的数比较大,因为“过期”而“不得不”出队
存放a数组的“下标”而没存放具体值

这里总结下双端队列和向量的用法

#include <deque>
#include <vector>
deque<int> dq;
dq.push_front();
dq.push_back()
dq.pop_front();
dq.pop_back();
dq.front();
dq.back();
dq.size();
dq.empty()

vector<int> v;
v.push_back() //通过push_back来插入元素。

这里先挖个坑,图的遍历等下次再写

用堆栈实现图的深度优先(非递归)
用队列实现图的广度优先

图的递归与非递归深度优先:https://blog.csdn.net/weixin_41969587/article/details/82708219

图的邻接矩阵表示方法如下

#define MAXVERTEX 100   //图的最大顶点数
#define INFINITY 32767  //用有符号的int最大值表示无穷大
typedef char vertextype;    //定义定点的存储信息为字符型
typedef int arctype;    //定义边的权值为int型
 
//图的邻接矩阵的存储结构
typedef struct
{
    vertextype vertex[MAXVERTEX];   //顶点表
    arctype arc[MAXVERTEX][MAXVERTEX];  //邻接矩阵
    int vertexnum;  //图的当前顶点数
    int arcnum; //图的当前边数
}MGraph;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值