中级提升班-4
题目一 设计栈及其操作
实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素
的操作。
要求:1.pop、push、getMin操作的时间复杂度都是O(1);2.设计的栈类型可以
使用现成的栈结构
方法:
- 初始化两个栈分别为data和min,data按照顺序放数据,min放当前data栈内最小的元素(若新放入的比min栈的top还小,则为新放入的数字,否则仍放入min的top)
class newStack {
private:
stack<int> data;
stack<int> min;
public:
newStack() {}
void pop() {
if (!data.empty()) {
data.pop();
min.pop();
}
else {
return;
}
}
void push(int x) {
data.push(x);
if (min.empty() || min.top() > x) {
min.push(x);
}
else {
min.push(min.top());
}
}
int getMin() {
if (!min.empty()) {
return min.top();
}
}
};
题目二 栈与队列的相互实现
如何仅用栈结构实现队列结构?
- 建立push栈和pop栈,用户数据首先存在push栈中,当要进行输出数据时,若pop栈为空,则将push栈数据全部倒入pop栈,再由pop栈输出
如何仅用队列结构实现栈结构?
- 建立两个queue为A和B,用户输入数据,加入A中,若需要返回栈顶元素,将A中数据除了最后一个,均放到B中,返回A中第一个数据;当又有数据来时,加入B中;以及交替进行
面试官:问利用栈完成BFS,或者用队列完成DFS,则需要用到以上方法
class newStack {
private:
stack<int> data;
stack<int> min;
public:
newStack() {}
void pop() {
if (!data.empty()) {
data.pop();
min.pop();
}
else {
return;
}
}
void push(int x) {
data.push(x);
if (min.empty() || min.top() > x) {
min.push(x);
}
else {
min.push(min.top());
}
}
int getMin() {
if (!min.empty()) {
return min.top();
}
}
};
class TwoStacktoQueue {
public:
stack<int> pushStack;
stack<int> popStack;
void push(int x) {
pushStack.push(x);
dao();
}
int front() {
dao();
return popStack.top();
}
void pop() {
if (pushStack.empty() && popStack.empty()) {
return;
}
dao();
popStack.pop();
}
void dao() {
if (popStack.empty()) {//popStack里没东西
while (!pushStack.empty()) {
popStack.push(pushStack.top());
pushStack.pop();
}
}
}
};
class queueToStack {
public:
queue<int> qa;
queue<int> qb;
void pushStack(int x) {
if (!qa.empty()) {
qa.push(x);
}
else {
qb.push(x);
}
}
int topStack() {
if (qa.empty() && qb.empty()) {
return INT32_MIN;
}
int cur = 0;
if (!qa.empty()) {
while (!qa.empty()) {
cur = qa.front();
qb.push(cur);
qa.pop();
}
}
else {
while (!qb.empty()) {
cur = qb.front();
qa.push(cur);
qb.pop();
}
}
return cur;
}
void popStack() {
if (qa.empty() && qb.empty()) {
return;
}
if (!qa.empty()) {
while (qa.size() > 1) {
qb.push(qa.front());
qa.pop();
}
qa.pop();
}
else {
while (qb.size() > 1) {
qa.push(qb.front());
qb.pop();
}
qb.pop();
}
}
};
推广 动态规划优化
一般动态规划,均可转换成下图所示dp矩阵,例如普通位置与其左方和上方相关
若某位置只与其左边和上边相关,则可以利用一维矩阵完成优化,相当于迭代更新
若某位置只与其右边和上边相关,则可以从右边开始更新
与多个位置相关同理
若dp矩阵如下图所示,若按照x方向建立则优化效果不大,因此应当按照纵向建立矩阵
若跨行相关,则可以建立多个一维矩阵
同样,三维dp矩阵可以转换成二维,层与层之间的关系,用一层来做迭代和记录
题目三容器装水
给定一个数组arr,已知其中所有的值都是非负的,将这个数组看作一个容器,
请返回容器能装多少水
比如,arr = {3,1,2,5,2,4},根据值画出的直方图就是容器形状,该容
器可以装下5格水
再比如,arr = {4,5,1,3,2},该容器可以装下2格水
不能求波峰波谷,太多复杂情况难coding
- 从第二个开始到第n-1个,当前在i位置,求出左侧最大值和右侧最大值,两者取其小为其装水瓶颈,该位置水量为(最大值 - arr[i]),累加则得到结果
- 可以利用辅助数组,求出每个位置的左边最大值和右边最大值,时间复杂度O(N),空间复杂度O(N)
- 图片方法做到时间复杂度O(N),空间复杂度O(1):定义max左和max右,左指针和右指针,若max左>max右,说明瓶颈由右边决定,处理右指针数据,并左移,当两者相同时,可以同时移动
//方法1
int waterProblem1(vector<int> arr) {
if (arr.size() < 3) {
return 0;
}
// 辅助数组:i左边最大值
vector<int> leftArr(arr.size());
int maxleft = -1;
for (int i = 0; i < arr.size(); i++) {
maxleft = max(maxleft, arr[i]);
leftArr[i] = maxleft;
}
// 辅助数组:i右边最大值
vector<int> rightArr(arr.size());
int maxright = -1;
for (int i = arr.size() - 1; i >= 0; i--) {
maxright = max(maxright, arr[i]);
rightArr[i] = maxright;
}
int res = 0;
for (int i = 1; i < arr.size() - 1; i++) {
res += (min(leftArr[i], rightArr[i]) - arr[i]) > 0 ? min(leftArr[i], rightArr[i]) - arr[i] : 0;
}
cout << res << endl;
return res;
}
//方法2
int waterProblem2(vector<int> arr) {
if (arr.size() < 3) {
return 0;
}
int l = 1;
int r = arr.size() - 2;
int maxleft = arr[0];
int maxright = arr[arr.size() - 1];
int res = 0;
while (l <= r) {
if (maxleft > maxright) {
res += (maxright - arr[r]) > 0 ? maxright - arr[r] : 0;
maxright = max(maxright, arr[r--]);
}
else if (maxleft < maxright) {
res += (maxleft - arr[l]) > 0 ? maxleft - arr[l] : 0;
maxleft = max(maxleft, arr[l++]);
}
else {
res += (maxright - arr[r]) > 0 ? maxright - arr[r] : 0;
maxright = max(maxright, arr[r--]);
res += (maxleft - arr[r]) > 0 ? maxleft - arr[l] : 0;
maxleft = max(maxleft, arr[l++]);
}
}
cout << res << endl;
return res;
}
题目六
给定一个数组arr长度为N,你可以把任意长度大于0且小于N的前缀作为左部分,剩下的
作为右部分。但是每种划分下都有左部分的最大值和右部分的最大值,请返回最大的,
左部分最大值减去右部分最大值的绝对值
-
法一:可以建立两个辅助数组,记录左边开始计算的最大值和右边开始计算的最大值,再进行处理
-
法二,做到空间复杂度O(1)
- 找到数组中最大值,若在左边,则max左肯定为数组中最大值max,要使右边最小,则只包含n-1位置
- 若max在右部分,要使左部分最小,则只包含0位置的数
代码不写了,差不多比较简单
题目七旋转词
KMP算法(判断字符串是否为子串)
如果一个字符串为str,把字符串str前面任意的部分挪到后面形成的字符串叫
作str的旋转词。比如str=“12345”,str的旋转词有"12345"、“23451”、
“34512”、“45123"和"51234”。给定两个字符串a和b,请判断a和b是否互为旋转
词。
比如:
a=“cdab”,b=“abcd”,返回true。
a=“1ab2”,b=“ab12”,返回false。
a=“2ab1”,b=“ab12”,返回true。
方法:看b是不是a+a的子串
vector<int> getNexts(string str) {
vector<int> nexts(str.length());
if (str.length() == 1) {
nexts[0] = -1;
return nexts;
}
nexts[0] = -1;
nexts[1] = 0;
int i = 2;
int cn = 0;
while (i < str.length()) {
if (str[i - 1] == str[cn]) {
nexts[i++] = ++cn;
}
else if (cn > 0) {
cn = nexts[cn];
}
else {
nexts[i++] = 0;
}
}
return nexts;
}
int KMP(string str1, string str2) {
if (str1.length() < str2.length()) {
return -1;
}
vector<int> nexts = getNexts(str2);
int i1 = 0;
int i2 = 0;
while (i1 < str1.length() && i2 < str2.length()) {
if (str1[i1] == str2[i2]) {
i1++;
i2++;
}
else if (nexts[i2] == -1) {
i1++;
}
else {
i2 = nexts[i2];
}
}
return i2 == str2.length() ? i1 - i2 : -1;
}
bool isRotateString(string str1, string str2) {
if (str1.length() != str2.length()) {
return false;
}
string str3 = str1 + str1;
int k = KMP(str3, str2);
return k == -1 ? false : true;
}
题目八 咖啡机
贪心、动态规划
数组arr表示咖啡机的个数及每个咖啡机需要制作咖啡的时间;N表示有多少个客人,每个客人点一杯咖啡且做完就喝,喝的时间为0;a表示有一台洗杯子机器,一次洗一个,需要a时间;另外,杯子可以经过b时间后自然风干
方法:
- 建立小根堆,按照结束时间排队(开始时间+持续时间)
- 不管洗杯子过程,建立数组,表示0 ~ N-1个人每个人喝完咖啡的时间,即小根堆的top
- 接着利用动态规划或者递归,写出洗杯子的最佳时间
int process(vector<int> drink, int a, int b, int index, int washline) {// 咖啡机在washline时间点才有空
if (index == drink.size() - 1) {
return min(max(washline, drink[index]) + a, drink[index] + b);// 用机器洗:等有空洗了和有需求洗了开始洗的时间,有需求洗了直接放着的时间
}
int wash = max(washline, drink[index]) + a;
int next1 = process(drink, a, b, index + 1, wash);
int p1 = max(wash, next1);
int dry = drink[index] + b;
int next2 = process(drink, a, b, index + 1, washline);
int p2 = max(dry, next2);
return min(p1, p2);
}
class Cmp {
public:
bool operator()(const pair<int, int>& a, const pair<int, int>& b) {
return a.first + a.second > b.first + b.second;
}
};
int coffeeTime(vector<int> arr, int n, int a, int b) {
priority_queue<pair<int, int>, vector<pair<int, int>>, Cmp>que;
for (int i = 0; i < arr.size(); i++) {
que.push({ 0, arr[i] });
}
vector<int> drink(n);
for (int i = 0; i < n; i++) {
int drinkTime = que.top().first + que.top().second;
int workTime = que.top().second;
que.pop();
drink[i] = drinkTime;
que.push({ drinkTime, workTime });
}
return process(drink, a, b, 0, 0);
}