题目一
用两个栈,其中一个为数据栈,另一个为辅助栈,用来存储最小值;数据进入数据栈前,先与辅助栈的栈顶比较,将小的那个压入辅助栈中,数据进入数据栈;需要从数据栈中弹出数据时,也将辅助栈的栈顶弹出;辅助的栈顶就是当前数据栈中的最小值。
代码实现:
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);
}