关键词:逆波兰表达式,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,此时函数会根据前缀(如
0x
或0b
)来自动确定基数。
示例
#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_argument
或std::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;