左神算法与数据结构——中级提升班-4

中级提升班-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时间后自然风干

在这里插入图片描述

方法:

  1. 建立小根堆,按照结束时间排队(开始时间+持续时间)

在这里插入图片描述

  1. 不管洗杯子过程,建立数组,表示0 ~ N-1个人每个人喝完咖啡的时间,即小根堆的top

在这里插入图片描述

  1. 接着利用动态规划或者递归,写出洗杯子的最佳时间
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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值