leetcode刷题记录27(2023-11-15)【基本计算器 II(栈) | 数据流的中位数(堆) | 3的幂(模拟) | 矩阵中的最长递增路径(BFS、拓扑排序)】

227. 基本计算器 II

给你一个字符串表达式 s ,请你实现一个基本计算器来计算并返回它的值。

整数除法仅保留整数部分。

你可以假设给定的表达式总是有效的。所有中间结果将在 [-231, 231 - 1] 的范围内。

注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval() 。

示例 1:

输入:s = “3+2*2”
输出:7
示例 2:

输入:s = " 3/2 "
输出:1
示例 3:

输入:s = " 3+5 / 2 "
输出:5

提示:

1 < = s . l e n g t h < = 3 ∗ 1 0 5 1 <= s.length <= 3 * 10^5 1<=s.length<=3105
s 由整数和算符 (‘+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [ 0 , 2 31 − 1 ] [0, 2^{31} - 1] [0,2311]
题目数据保证答案是一个 32-bit 整数

#include <string>
#include <map>
#include <stack>
using namespace std;

class Solution
{
    int calOneOp(int firstNum, int secondNum, char op)
    {
        int res = 0;
        switch (op)
        {
        case '+':
            res = firstNum + secondNum;
            break;
        case '-':
            res = firstNum - secondNum;
            break;
        case '*':
            res = firstNum * secondNum;
            break;
        case '/':
            res = firstNum / secondNum;
            break;
        default:
            break;
        }
        return res;
    }

public:
    int calculate(string s)
    {
        map<char, int> opPriority;
        opPriority['+'] = 0;
        opPriority['-'] = 0;
        opPriority['*'] = 1;
        opPriority['/'] = 1;

        stack<int> nums;
        stack<char> ops;

        for (int i = 0; i < s.size();)
        {

            if (s[i] >= '0' && s[i] <= '9')
            {
                int tmp = 0;
                while (s[i] >= '0' && s[i] <= '9')
                {
                    tmp = s[i] - '0' + tmp * 10;
                    i++;
                }
                nums.emplace(tmp);
            }
            else if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/')
            {
                if (ops.empty() || opPriority[s[i]] > opPriority[ops.top()])
                {
                    ops.push(s[i]);
                }
                else
                {
                    while (!ops.empty() && !(opPriority[s[i]] > opPriority[ops.top()]))
                    {
                        int firstNum, secondNum;
                        if (!nums.empty())
                        {
                            secondNum = nums.top();
                            nums.pop();
                        }
                        else
                        {
                            return -1;
                        }

                        if (!nums.empty())
                        {
                            firstNum = nums.top();
                            nums.pop();
                        }
                        nums.push(calOneOp(firstNum, secondNum, ops.top()));
                        ops.pop();
                    }
                    ops.push(s[i]);
                }
                i++;
            }
            else if (s[i] == ' ')
            {
                i++;
            }
            else
            {
                return -1;
            }
        }
        while (!ops.empty())
        {
            int firstNum, secondNum;
            if (!nums.empty())
            {
                secondNum = nums.top();
                nums.pop();
            }
            else
            {
                return -1;
            }

            if (!nums.empty())
            {
                firstNum = nums.top();
                nums.pop();
            }
            nums.push(calOneOp(firstNum, secondNum, ops.top()));
            ops.pop();
        }
        return nums.top();
    }
};

int main()
{
    string compStr = "3/2";
    Solution sol;
    int res = sol.calculate(compStr);
    return 0;
}

里面有几个需要注意的点,比如会超范围:

  • 为了防止超范围,应该把tmp*10放到最后:因为根据结合性原则,表达式是从左往右计算的tmp = tmp * 10 + s[i] - ‘0’;计算过程中,s[i]是一个ASCII值(在题目数据中,是大于10的),中间结果tmp * 10+s[i]>tmp * 10+s[i] - ‘0’,但如果把tmp*10放在后面,即:tmp = s[i] - ‘0’ + tmp * 10;中间结果不会超过max(最终结果, (int)s[i])。
    在这里插入图片描述

  • 刷leco的时候,leco在运行代码前,应该是会进行静态审查的,一些不安全的代码不会运行,比如报这种错误,所以代码的异常值要进行处理,比如在使用top的时候进行判空:
    在这里插入图片描述

295. 数据流的中位数

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

例如 arr = [2,3,4] 的中位数是 3 。
例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5 。
实现 MedianFinder 类:

MedianFinder() 初始化 MedianFinder 对象。

void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
[“MedianFinder”, “addNum”, “addNum”, “findMedian”, “addNum”, “findMedian”]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1); // arr = [1]
medianFinder.addNum(2); // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3); // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0
提示:

− 1 0 5 < = n u m < = 1 0 5 -10^5 <= num <= 10^5 105<=num<=105
在调用 findMedian 之前,数据结构中至少有一个元素
最多 5 ∗ 1 0 4 5 * 10^4 5104 次调用 addNum 和 findMedian

主要思路,就是利用两个栈,将数组拆分,来查找中位数,这就将插入的复杂度降到了O(log(n))的级别。很巧妙。

在实现最大堆的时候,less模板表示大顶堆,插入的时候返回true的时候就会停止;
greater表示小顶堆,插入的时候,返回true的时候会停止;
两个模板函数的命名,是和堆这种数据结构密切相关的。

#include <queue>
#include <iostream>

using namespace std;

class MedianFinder
{
    priority_queue<int, vector<int>, less<int>> maxQue;    // 大顶堆
    priority_queue<int, vector<int>, greater<int>> minQue; // 小顶堆

public:
    MedianFinder()
    {
    }

    void addNum(int num)
    {
        if (maxQue.empty() || num <= maxQue.top())
        {
            maxQue.push(num);
            if (maxQue.size() > minQue.size() + 1)
            {
                minQue.push(maxQue.top());
                maxQue.pop();
            }
        }
        else
        {
            minQue.push(num);
            if (minQue.size() > maxQue.size())
            {
                maxQue.push(minQue.top());
                minQue.pop();
            }
        }
    }

    double findMedian()
    {
        if (maxQue.empty() && minQue.empty())
            return -1;
        return minQue.size() == maxQue.size() ? (double)(minQue.top() + maxQue.top()) / 2 : maxQue.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

int main()
{
    MedianFinder *obj = new MedianFinder();
    obj->addNum(3);
    double medain = obj->findMedian();
    cout << medain << endl;
    obj->addNum(5);
    medain = obj->findMedian();
    cout << medain << endl;
    obj->addNum(3);
    medain = obj->findMedian();
    cout << medain << endl;
    obj->addNum(2);
    medain = obj->findMedian();
    cout << medain << endl;
}

326. 3 的幂

给定一个整数,写一个函数来判断它是否是 3 的幂次方。如果是,返回 true ;否则,返回 false 。

整数 n 是 3 的幂次方需满足:存在整数 x 使得 n == 3x

示例 1:

输入:n = 27
输出:true

示例 2:

输入:n = 0
输出:false

示例 3:

输入:n = 9
输出:true

示例 4:

输入:n = 45
输出:false

提示:

− 2 31 < = n < = 2 31 − 1 -2^{31} <= n <= 2^{31} - 1 231<=n<=2311

进阶:你能不使用循环或者递归来完成本题吗?

#include <iostream>
using namespace std;

class Solution
{
public:
    bool isPowerOfThree(int n)
    {
        if (n < 0)
            return false;
        unsigned int power_3 = 1;
        unsigned int unsigned_n = n;
        while (power_3 < unsigned_n)
        {
            power_3 *= 3;
        }
        if (power_3 == unsigned_n)
        {
            return true;
        }
        return false;
    }
};

int main()
{
    Solution sol;
    bool res = sol.isPowerOfThree(27);
    cout << res;
}

题解1给出了一种时间复杂度为O(1)的做法:

在题目给定的 32 位有符号整数的范围内,最大的 3 的幂为 3 19 = 11622614673 3^{19}=11622614673 319=11622614673。我们只需要判断 n 是否是 3 19 3^{19} 319 的约数即可。

class Solution {
public:
    bool isPowerOfThree(int n) {
        return n > 0 && 1162261467 % n == 0;
    }
};

329. 矩阵中的最长递增路径

给定一个 m x n 整数矩阵 matrix ,找出其中 最长递增路径 的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你 不能 在 对角线 方向上移动或移动到 边界外(即不允许环绕)。

示例 1:

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/9a53090a832a49989a728b2531d23d42.png#pic_center, x_16=200x)
输入:matrix = [[9,9,4],[6,6,8],[2,1,1]]
输出:4
解释:最长递增路径为 [1, 2, 6, 9]。
示例 2:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/92373cb0026e4275af34201ddde85efe.png#pic_center, x_16=200x)
输入:matrix = [[3,4,5],[3,2,6],[2,2,1]]
输出:4
解释:最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。
示例 3:

输入:matrix = [[1]]
输出:1

提示:

m = = m a t r i x . l e n g t h m == matrix.length m==matrix.length
n = = m a t r i x [ i ] . l e n g t h n == matrix[i].length n==matrix[i].length
1 < = m , n < = 200 1 <= m, n <= 200 1<=m,n<=200
0 < = m a t r i x [ i ] [ j ] < = 2 31 − 1 0 <= matrix[i][j] <= 2^{31} - 1 0<=matrix[i][j]<=2311

没有直接解题,参考了题解的代码2,采用了BFS,同时利用一个memo矩阵,来对已经搜索过的空间进行记录,防止重复计算:

class Solution
{
    int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

public:
    int longestIncreasingPath(vector<vector<int>> &matrix)
    {
        if (matrix.size() == 0 || matrix[0].size() == 0)
            return -1;
        int res = 0;
        vector<vector<int>> memo(matrix.size(), vector<int>(matrix[0].size()));
        for (int i = 0; i < matrix.size(); i++)
        {
            for (int j = 0; j < matrix[0].size(); j++)
            {
                res = max(res, dfs(matrix, i, j, memo));
            }
        }
        return res;
    }

    int dfs(vector<vector<int>> &matrix, int row, int column, vector<vector<int>> &memo)
    {
        if (memo[row][column] != 0)
        {
            return memo[row][column];
        }
        memo[row][column]++;
        for (int i = 0; i < 4; i++)
        {
            int newRow = row + direction[i][0];
            int newColumn = column + direction[i][1];
            if (newRow >= 0 && newRow < matrix.size() && newColumn >= 0 && newColumn < matrix[0].size() && matrix[row][column] > matrix[newRow][newColumn])
            {
                memo[row][column] = max(memo[row][column], dfs(matrix, newRow, newColumn, memo) + 1);
            }
        }
        return memo[row][column];
    }
};

把矩阵看成一个有向图,从出度为0的节点开始,进行拓扑排序,最终拓扑排序的轮数就是最长路径:

class Solution
{
    int direction[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};

public:
    int longestIncreasingPath(vector<vector<int>> &matrix)
    {
        vector<vector<int>> outDegree = vector<vector<int>>(matrix.size(), vector<int>(matrix[0].size(), 0));
        for (int i = 0; i < matrix.size(); i++)
        {
            for (int j = 0; j < matrix[0].size(); j++)
            {
                for (int k = 0; k < 4; k++)
                {
                    int newX = i + direction[k][0];
                    int newY = j + direction[k][1];
                    if (newX >= 0 && newX < matrix.size() && newY >= 0 && newY < matrix[0].size() && matrix[i][j] < matrix[newX][newY])
                    {
                        outDegree[i][j]++;
                    }
                }
            }
        }

        queue<pair<int, int>> que;
        for (int i = 0; i < outDegree.size(); i++)
        {
            for (int j = 0; j < outDegree[0].size(); j++)
            {
                if (outDegree[i][j] == 0)
                    que.push({i, j});
            }
        }

        int res = 0;
        while (!que.empty())
        {
            res++;
            int size = que.size();
            for (int i = 0; i < size; i++)
            {
                int X = que.front().first;
                int Y = que.front().second;
                que.pop();
                for (int j = 0; j < 4; j++)
                {
                    int newX = X + direction[j][0];
                    int newY = Y + direction[j][1];
                    if (newX >= 0 && newX < matrix.size() && newY >= 0 && newY < matrix[0].size() && matrix[X][Y] > matrix[newX][newY])
                    {
                        outDegree[newX][newY]--;
                        if (outDegree[newX][newY] == 0)
                        {
                            que.push({newX, newY});
                        }
                    }
                }
            }
        }
        return res;
    }
};

  1. https://leetcode.cn/problems/power-of-three/solutions/1011809/3de-mi-by-leetcode-solution-hnap/ ↩︎

  2. https://leetcode.cn/problems/longest-increasing-path-in-a-matrix/solutions/346614/ju-zhen-zhong-de-zui-chang-di-zeng-lu-jing-by-le-2/ ↩︎

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cherries Man

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值