day11 栈与队列part02

关键词:逆波兰表达式,C/C++ String,

150 逆波兰表达式 求值

题目
在这里插入图片描述

思路:

本题首先要搞清楚逆波兰表达式的计算规则。其次在C++实现中,要学会string和int如何做转换。

1 C++ string转换为int类型 可以使用 stoi 函数

std::stoi 是 C++11 引入的一个标准库函数,用于将字符串转换为整数。它是一个更安全、更通用的替代方案,相比旧式的 atoi 函数,提供了更好的错误处理机制。

std::stoi 函数

std::stoi 的原型如下:

int stoi( const string& str, size_t* pos = nullptr, int base = 10 );
  • str:要转换的字符串。
  • pos:可选参数,用于指出解析停止的位置。如果提供了这个参数,函数会在解析停止的位置设置 pos 的值。
  • base:可选参数,默认值为 10,表示数字的基数。可以是 2 到 36 之间的任意整数,也可以是 0,此时函数会根据前缀(如 0x0b)来自动确定基数。

示例

#include <iostream>
#include <string>
#include <stdexcept>

int main() {
    std::string str = "12345";
    try {
        int num = std::stoi(str);
        std::cout << "Converted number: " << num << std::endl;
    } catch (const std::invalid_argument& e) {
        std::cerr << "Invalid argument: " << e.what() << std::endl;
    } catch (const std::out_of_range& e) {
        std::cerr << "Out of range: " << e.what() << std::endl;
    }

    return 0;
}

atoi 函数

atoi 是一个较早的标准 C 库函数,用于将字符串转换为整数。它的原型如下:

int atoi(const char* nptr);
  • nptr:指向一个 C 风格字符串的指针。

. 错误处理

  • std::stoi:提供了异常处理机制,当转换失败时(例如,字符串不是有效的整数表示),会抛出 std::invalid_argumentstd::out_of_range 异常。
  • atoi:没有内建的错误处理机制。如果字符串不能被正确转换为整数,atoi 会返回一个不确定的值,这可能导致程序中的错误。

2 C语言风格字符串 和 C++字符串的区别

C语言风格字符串是一个以空字符’\0’为结尾的字符数组,通常为 const char* 类型。
string是C++中标准库中的一个类,提供如 append、insert、erase 等,避免了传统 C 风格字符串函数(如 strcpy、strcat)可能引起的缓冲区溢出等问题。
c_str() 函数是C++中string类的一个成员函数,用于将string转换为 C语言类型的字符串。

C风格函数C++ string类的成员函数

150题目答案

class Solution {
public:
    bool isNumber(string &token) {
        return !(token == "+" || token == "-" || token == "*" || token == "/");
    }

    int evalRPN(vector<string>& tokens) {
        // 逆波兰表达式是一种后缀表达式
        // 将token在栈中存放,每次访问栈顶元素是:
        //  1 若遇到符号则运算,取接下来栈顶的两个数字
        //  2 若遇到数字则取出

        stack<int> tmp;
        string current;
        for(int i = 0; i < tokens.size(); i++) {

            if(isNumber(tokens[i])) {
                tmp.push(stoi(tokens[i]));
            } else {
                int num2 = tmp.top();
                tmp.pop();
                int num1 = tmp.top();
                tmp.pop();
                // switch语句中用来判断的必须是整数或者枚举类型
                string &token = tokens[i];
                switch(token[0]) {
                    case '+':
                        tmp.push(num1 + num2);
                        break;
                    case '-':
                        tmp.push(num1 - num2);
                        break;
                    case '*':
                        tmp.push(num1 * num2);
                        break;
                    case '/':
                        tmp.push(num1 / num2);
                        break;
                }
            }
            
        }

        return tmp.top();

    }
};

239 滑动窗口最大值 【单调栈】【队列】【dequeue】

题目
在这里插入图片描述

思路

本题使用单调栈算法
1 自己写的解法代码1,也是用的单调栈的思想,但实现的时候代码有些冗余

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        // 单调栈算法
        deque<int> dq;
        vector<int> result;
        for(int i = 0; i < k; i++) {
            if(!dq.empty()) {
                if(nums[i] > dq.front()) {
                    while(!dq.empty()) {
                        dq.pop_front();
                    }
                    dq.push_back(nums[i]);
                } else {
                    while(dq.back() < nums[i]) {
                        dq.pop_back();
                    }
                    dq.push_back(nums[i]);
                }
            } else {
                dq.push_back(nums[i]);
            }
        }
        result.push_back(dq.front());
        int j = 0;
        for(int i = k; i < nums.size(); i++) {
            if(nums[j++] == dq.front()) {
                dq.pop_front();
            }
            
            if(nums[i] > dq.front()) {
                while(!dq.empty()) {
                    dq.pop_front();
                }
                dq.push_back(nums[i]);
            } else {
                while(dq.back() < nums[i]) {
                    dq.pop_back();
                }
                dq.push_back(nums[i]);
            }

            result.push_back(dq.front());

        }
        return result;
    }
};

2 简介思路代码实现,新建一个单调栈的类MyQue,根据单调栈的原则对其进行push和pop的实现

class Solution {
public:
    // 自定义单调栈
    class MyQue{
    public:
        deque<int> dq;

        void push(int a) {
            // push的时候要注意,由于使用的是双向队列,在向队列添加元素时
            // 从队列末端开始判断,凡是比当前压入元素小的,全部pop_back出去
            // 这样才能满足当前 单调栈 是从大到小排列的
            while(!dq.empty() && dq.back() < a) {                
                dq.pop_back();
            } 
            dq.push_back(a);
           
        };

        void pop(int curFront) {
            if(curFront == dq.front()) {
                dq.pop_front();
            }

        };

        int front() {
            if(!dq.empty()) {
                return dq.front();
            } else {
                return -1;
            }
        };
    };


    vector<int> maxSlidingWindow(vector<int>& nums, int k) {

        vector<int> result;
        MyQue mq;
        for(int i = 0; i < nums.size(); i++) {
            mq.push(nums[i]);
            if(i >= k) {
                mq.pop(nums[i-k]);
            }

            if(i >= (k-1)) {
                result.push_back(mq.front());
            }

        }

        return result;
    }
};

347 前K个高频元素 【优先对列】

题目链接

在这里插入图片描述

思路:

我的实现思路由于C++中map和multimap都是默认按照升序排序的。先用一个map统计出数组中每个数字出现的频率,再将key和value反转过来存到multimap中,逆序遍历前k个pair即可。

我的实现代码 【时间复杂度 O(nlogn)】

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 先用一个map将vector遍历一遍,得到每个元素出现的频率
        // 在用一个map把key和value调转
        map<int, int> m;
        multimap<int, int> reverse_m;
        
        vector<int> result;
        for(int i = 0; i < nums.size(); i++) {
            if(m.find(nums[i]) != m.end()) {
                m[nums[i]]++;
            } else {
                m[nums[i]] = 1;
            }
        }
        for(auto it:m) {
            cout << "insert " << it.second << " and " << it.first << endl;
            reverse_m.insert({it.second, it.first});
        }

        // 注意:multimap是默认按照比较器less 从小到大排序的
        // 也可以将其指定为使用比较器greater 从大到小排序
        // 使用反向迭代器 rbegin和rend可以从末尾遍历multimap

        int count = 0;
        for(auto it = reverse_m.rbegin(); it != reverse_m.rend(); it++) {
            result.push_back(it->second);
            count++;
            if(count == k) break;
        }

        return result;

    }
};

leetcode官解 【优先队列】 时间复杂度 O(nlogk)

class Solution {
public:
    // 自定义一个用于比较pair<int,int>中的frequency的greater比较器,用于构造小根堆
    class greaterByFrequen {
    public:
        bool operator()(const pair<int,int> a, const pair<int,int> b) {
            return a.second > b.second;
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 本题使用优先队列实现,时间复杂度可达 O(nlogk)
        map<int,int> frequency;
        // 声明小根堆
        priority_queue<pair<int,int>, vector<pair<int,int>>, greaterByFrequen> minHeap;

        for(auto &it:nums) {
            frequency[it]++;
        }

        for(auto &it: frequency) {
            minHeap.push(it);
            if(minHeap.size() > k) {
                minHeap.pop();
            }
        }

        vector<int> result;
        for(int i = 0; i < k; i++) {
            result.push_back(minHeap.top().first);
            minHeap.pop();
        }

        return result;

    }
};


补充

优先队列priority_queue是一种完全二叉树。在C++中是一种容器适配器
其中 pair<int,int> 是队列中元素的数据类型,
vector<pair<int,int>> 是队列的容器,
greaterByFrequen 是比较器。
优先队列默认使用less比较器,实现的是大根堆,
当使用greater比较器,则实现的是小根堆。
原因:当底部插入元素向上swim时,若父节点 less 于子节点,则二者交换,否则不交换。说明最后在堆顶的是最大的元素,因此是大根堆。

priority_queue<pair<int,int>, vector<pair<int,int>>, greaterByFrequen> minHeap;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值