第四节课:

本文详细介绍了如何使用两个栈实现最小值栈,两个栈实现队列,以及两个队列实现栈。此外,还讨论了动态规划的空间压缩技巧,数组中水的体积计算,寻找数组中最大值的最小差值,以及判断旋转字符串的方法。最后,解决了一道关于咖啡机和洗杯机问题的优化算法,探讨了如何在有限时间内为所有人提供干净的咖啡杯。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


题目一

在这里插入图片描述
用两个栈,其中一个为数据栈,另一个为辅助栈,用来存储最小值;数据进入数据栈前,先与辅助栈的栈顶比较,将小的那个压入辅助栈中,数据进入数据栈;需要从数据栈中弹出数据时,也将辅助栈的栈顶弹出;辅助的栈顶就是当前数据栈中的最小值。
代码实现:

class spStack {
public:
	stack<int>dataStack;
	stack<int>minStack;

	void spPush(int val) {
		dataStack.push(val);
		if (minStack.empty() || val < minStack.top()) {
			minStack.push(val);
		}
		else {
			minStack.push(minStack.top());
		}
	}

	void spPop() {
		dataStack.pop();
		minStack.pop();
	}

	int spTop() {
		return dataStack.top();
	}

	int getMin() {
		return minStack.top();
	}


};

题目二

在这里插入图片描述
两个栈实现队列:一个push栈,一个pop栈;数据每次进push栈,当pop栈为空,则将push栈中的数据全部倒入pop栈中,否则不进入pop栈;每次弹出数据从pop栈中弹出。

在这里插入图片描述
两个队列实现栈:假设两个队列分别为A和B;将数据加入A中,当用户想要栈顶元素是,将A中的数据加入B中,则最后一个数据就是栈顶元素;当又有新的数据来到时,加入到B中;A、B交替进行上述进行上述操作。
在这里插入图片描述
面试官:请用队列实现图的深度优先遍历;请用栈实现图的宽度优先遍历。用上述方法!
代码实现:

//用两个栈实现队列
class stackToqueue {
public:
	stack<int>pushStack;
	stack<int>popStack;

	void queuePush(int val) {
		pushStack.push(val);
		dao();
	}

	int queueFront() {
		if (pushStack.empty() && popStack.empty()) {
			return INT_MAX;
		}
		dao();
		return popStack.top();
	}

	void queuePop() {
		if (pushStack.empty() && popStack.empty()) {
			return;
		}
		dao();
		popStack.pop();
	}

	void dao() {
		if (popStack.empty()) {
			while (!pushStack.empty()) {
				popStack.push(pushStack.top());
				pushStack.pop();
			}
		}
	}
};

//用两个队列实现栈
class queueToStack {
	queue<int>qa;
	queue<int>qb;

	void stackPush(int val) {
		if (!qa.empty()) {
			qa.push(val);
		}
		else{
			qb.push(val);
		}
	}

	int stackTop() {
		if (qa.empty() && qb.empty()) {
			return INT_MAX;//表示栈为空,返回无效值
		}
		int cur;
		if (!qa.empty()) {
			while (!qa.empty()) {
				cur = qa.front();
				qa.pop();
				qb.push(cur);
			}
		}
		else {
			while (!qb.empty()) {
				cur = qb.front();
				qb.pop();
				qa.push(cur);
			}
		}
		return cur;
	}

	void stackPop() {
		if (qa.empty() && qb.empty()) {
			return;//表示栈为空
		}
		int cur;
		if (!qa.empty()) {
			while (qa.size()>1) {
				cur = qa.front();
				qa.pop();
				qb.push(cur);
			}
			qa.pop();
		}
		else {
			while (qb.size() > 1) {
				cur = qb.front();
				qb.pop();
				qa.push(cur);
			}
			qb.pop();
		}
	}

};

题目三

动态规划的空间压缩技巧

动态规划让填一个二维表,二维表的右下角就是要的答案。可以将二维表压缩成一个一维数组。在二维表中,一个一般位置的值,和他的左边和上边(上一行),不断更新这个数组,直到最后一行,得到所需要的结果。三维体也可以压缩,原理相同。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

如果当前位置的值依赖于的位置太远,则空间压缩可能就不太经济了

在这里插入图片描述


题目四

在这里插入图片描述
考虑每一个位置自己上方能留下多少水:

在这里插入图片描述
可以用辅助数组来预处理出,每个位置左侧最大值和右侧最大值,空间复杂度O(N)。但可以优化出O(1)的空间复杂度:

在这里插入图片描述
可以从数据状况 求解的标准两个方向进行优化。
代码实现:

int getWater(vector<int>& arr) {
	if (arr.size() < 3) {
		return 0;
	}
	int leftMax = arr[0];
	int rightMax = arr[arr.size() - 1];
	int l = 1;
	int r = arr.size() - 2;
	int res = 0;
	while (l <= r) {
		if (leftMax < rightMax) {
			res += max(leftMax - arr[l], 0);
			leftMax = max(arr[l], leftMax);
			l++;

		}
		else {
			res += max(rightMax - arr[r], 0);
			rightMax = max(arr[r], rightMax);
			r--;
		}
	}
	return res;
}

题目五

在这里插入图片描述
不管怎么切全局最大值都会作为左边最大值或者右边最大值,问题就转化为了,怎么切可以使得不包含全局最大值的那一部分的最大值尽量小;又因为左侧和右侧均必须有值,所以必包含数组的第一个值或者最后一个值,因此不包含全局最大值的那一部分的最大值至少为这两个值,去其中较小者与全局最大值做差,即为所求。
在这里插入图片描述
优化来自于问的标准。
代码实现:

int f(vector<int>& arr) {
	if (arr.size() < 2) {
		return -1;//无效值
	}
	int maxVal = arr[0];
	for (int i = 1; i < arr.size(); i++) {
		maxVal = max(maxVal, arr[i]);
	}

	return maxVal - min(arr[0], arr[arr.size() - 1]);
}

题目六

在这里插入图片描述
使用KMP算法(判断一个字符串是否为另一个字符串的子串)。首先,判断两个字符串的长度是否一样;然后将其中一个字符串自己和自己拼接,判断另一个字符串是否为拼接之后字符串的子串。拼接后的字符串中任意长度为5子串必是旋转词,另一个字符串如果是其旋转词必在其中。
在这里插入图片描述

vector<int> getNexts(string& s2) {
	vector<int>nexts(s2.length());
	nexts[0] = -1;
	if (s2.length() == 1) {
		return nexts;
	}
	nexts[1] = 0;
	int i = 2;
	int cn = 0;
	while (i < s2.length()) {
		if (s2[cn] == s2[i - 1]) {
			nexts[i] = cn + 1;
			i++;
			cn++;
		}
		else if (cn > 0) {
			cn = nexts[cn];
		}
		else {
			nexts[i] = 0;
			i++;
		}
	}
	return nexts;
}

bool f(string s1, string s2) {
	if (s1.length() != s2.length()) {
		return false;
	}
	if (s1.length() == 0) {
		return true;
	}
	s1 += s1;
	vector<int>nexts = getNexts(s2);
	int i1 = 0;
	int i2 = 0;

	while (i1 < s1.length() && i2 < s2.length()) {
		if (s1[i1] == s2[i2]) {
			i1++;
			i2++;
		}
		else if (i2 == 0) {
			i1++;
		}
		else {
			i2 = nexts[i2];
		}
	}

	return i2 == s2.length() ? true : false;
}


题目七

给定一个数组arr,数组中的元素代表一台咖啡机的服务时长;有N个人要喝咖啡,每人喝一杯,认为喝咖啡的时长为0;只有一台洗咖啡杯的的机器,洗一个杯子的时长为a;杯子如果不洗,需要挥发干净的时长为b。问:从第一个人泡咖啡开始,到所有人的杯子都干净的最少时间?
在这里插入图片描述
先求泡咖啡的最小时长,用小根堆:小根堆中存储的元素是(x,y)表示服务时长为y的咖啡机在x时刻可用,小根堆的比较方式是x+y,表示现在谁泡完一杯咖啡的结束时间少,谁放在上面。利用这种方式选择咖啡机,可用求得每个人获得咖啡的时间,也是需要洗杯子的时间
在这里插入图片描述
后序的问题转化为,给定一个需要洗杯子的时间数组,选择怎么洗杯子用的时间最短–>暴力递归–>动态规划
代码实现:

//返回要洗完drinks[index..]最早的时间点
//washline:洗杯机空闲的时间点
int process(vector<int>& drinks, int a, int b, int index, int washline) {
	if (index == drinks.size() - 1) {//basecase
		return min(drinks[drinks.size() - 1] + b, max(drinks[drinks.size() - 1], washline) + a);
	}
	//用洗杯机
	int wash = max(drinks[index], washline) + a;
	int nextTime01 = process(drinks, a, b, index + 1, wash);
	int p1 = max(wash, nextTime01);

	//不用洗杯机
	int dry = drinks[index] + b;
	int nextTime02 = process(drinks, a, b, index + 1, washline);
	int p2 = max(dry, nextTime02);
	
	return min(p1, p2);
}

//改动态规划
int process02(vector<int>& drinks, int N, int a, int b) {
	vector<vector<int>>dp(N, vector<int>(drinks[N - 1] + N * a));//准确估算washline的范围不太容易,往大了估算
	for (int j = 0; j < dp[0].size(); j++) {
		dp[N - 1][j] = min(drinks[N - 1] + b, max(drinks[N - 1], j) + a);
	}
	for (int i = N - 2; i >= 0; i--) {
		int washline = drinks[i] + (i + 1) * a;//对于当前的i,j不可能超过这个值,压缩时间
		for (int j = 0; j < washline; j++) {
			int wash = max(drinks[i], j) + a;
			int p1 = max(wash, dp[i + 1][wash]);
			int dry = drinks[i] + b;
			int p2 = max(dry, dp[i + 1][j]);
			dp[i][j] = min(p1, p2);
		}
	}
	return dp[0][0];
}

class Cmp {
public:
	bool operator()(const pair<int, int>& a, const pair<int, int>& b) {
		return a.first + a.second > b.first + b.second;
	}
};
//arr:一台咖啡机的服务时长
//N:N个人要喝咖啡,每人喝一杯,认为喝咖啡的时长为0
//a:洗杯机洗一个杯子的时长
//b:如果杯子不洗,挥发干净的时长
int minTime(vector<int>& arr, int N, int a, int b) {
	priority_queue<pair<int, int>, vector<pair<int, int>>, Cmp>heap;
	for (int i = 0; i < arr.size(); i++) {
		heap.push({ 0,arr[i] });
	}
	vector<int>drinks(N);//每个人喝完咖啡的时间

	for (int i = 0; i < N; i++) {
		drinks[i] = heap.top().first + heap.top().second;
		heap.push({ drinks[i], heap.top().second });
		heap.pop();
	}

	return process(drinks, a, b, 0, 0);
	//return process02(drinks, N, a, b);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值