20. 有效的括号
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 1:
输入:s = “()”
输出:true
示例 2:
输入:s = “()[]{}”
输出:true
示例 3:
输入:s = “(]”
输出:false
提示:
1 <= s.length <= 10^4
s
仅由括号 '()[]{}'
组成
思路:
class Solution {
private:
unordered_map<char, char> pair = {
{'}', '{'},
{')', '('},
{']', '['}
};
public:
bool isValid(string s) {
stack<char> st;
int n = s.size();
for(char & c : s){
if(pair.find(c) != pair.end()){
//是右括号
if(st.empty() || st.top() != pair[c]) return false;
st.pop();
}else if(c == '{' || c == '(' || c == '['){
st.push(c);
}
}
if(!st.empty()) return false;
return true;
}
};
71. 简化路径
给你一个字符串 path
,表示指向某一文件或目录的 Unix 风格 绝对路径 (以 '/'
开头),请你将其转化为更加简洁的规范路径。
在 Unix 风格的文件系统中,一个点(.
)表示当前目录本身;此外,两个点 (..
) 表示将目录切换到上一级(指向父目录);两者都可以是复杂相对路径的组成部分。任意多个连续的斜杠(即,'//'
)都被视为单个斜杠 '/'
。 对于此问题,任何其他格式的点(例如,'...'
)均被视为文件/目录名称。
请注意,返回的 规范路径 必须遵循下述格式:
- 始终以斜杠
'/'
开头。 - 两个目录名之间必须只有一个斜杠
'/'
。 - 最后一个目录名(如果存在)不能 以
'/'
结尾。 - 此外,路径仅包含从根目录到目标文件或目录的路径上的目录(即,不含
'.'
或'..'
)。
返回简化后得到的 规范路径 。
示例 1:
输入:path = “/home/”
输出:“/home”
解释:注意,最后一个目录名后面没有斜杠。
示例 2:
输入:path = “/…/”
输出:“/”
解释:从根目录向上一级是不可行的,因为根目录是你可以到达的最高级。
示例 3:
输入:path = “/home//foo/”
输出:“/home/foo”
解释:在规范路径中,多个连续斜杠需要用一个斜杠替换。
示例 4:
输入:path = “/a/./b/…/…/c/”
输出:“/c”
提示:
1 <= path.length <= 3000
path
由英文字母,数字,'.'
,'/'
或 '_'
组成。
path
是一个有效的 Unix 风格绝对路径。
思路:分两步,放入栈中,出栈并组成路径结果
class Solution {
public:
string simplifyPath(string path) {
int n = path.size();
int left = 0, right = 0;
stack<string> s;
//放入栈中
while(right < n){
while(right < n && path[right] != '/'){
right ++;
}
if(left != right){
string temp = path.substr(left, right - left);
if(temp != ".") {
s.push(temp);
}
}
right ++;
left = right;
}
if(s.empty()) return "/";
int count_return = 0;
string rec = "";
while(!s.empty()){
if(s.top() == ".."){
count_return ++;
s.pop();
}else if(count_return > 0){
s.pop();
count_return --;
}else{
rec = '/' + s.top() + rec;
s.pop();
}
}
if(rec == "") return "/";
return rec;
}
};
155. 最小栈
设计一个支持 push
,pop
,top
操作,并能在常数时间内检索到最小元素的栈。
实现 MinStack
类:
MinStack()
初始化堆栈对象。void push(int val)
将元素val推入堆栈。void pop()
删除堆栈顶部的元素。int top()
获取堆栈顶部的元素。int getMin()
获取堆栈中的最小元素。
示例 1:
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
提示:
-2^31 <= val <= 2^31 - 1
pop
、top
和 getMin
操作总是在 非空栈 上调用
push
, pop
, top
, and getMin
最多被调用 3 * 10^4
次
思路:用辅助栈辅助存储最小信息
辅助栈存放从小到大的排序栈(耗时耗内存)
class MinStack {
private:
stack<int> s;
stack<int> sort_s;
public:
MinStack() {
}
void push(int val) {
s.push(val);
stack<int> temp;
while(!sort_s.empty() && sort_s.top() < val){
temp.push(sort_s.top());
sort_s.pop();
}
sort_s.push(val);
while(!temp.empty()){
sort_s.push(temp.top());
temp.pop();
}
}
void pop() {
int top = s.top();
s.pop();
stack<int> temp;
while(sort_s.top() != top){
temp.push(sort_s.top());
sort_s.pop();
}
sort_s.pop();
while(!temp.empty()){
sort_s.push(temp.top());
temp.pop();
}
}
int top() {
return s.top();
}
int getMin() {
return sort_s.top();
}
};
辅助栈存放min(当前值,原全栈最小)
class MinStack {
private:
stack<int> s;
stack<int> min_s;
public:
MinStack() {
min_s.push(INT_MAX);
}
void push(int val) {
s.push(val);
min_s.push(min(val, min_s.top()));
}
void pop() {
s.pop();
min_s.pop();
}
int top() {
return s.top();
}
int getMin() {
return min_s.top();
}
};
150. 逆波兰表达式求值
给你一个字符串数组 tokens
,表示一个根据 逆波兰表示法 表示的算术表达式。
请你计算该表达式。返回一个表示表达式值的整数。
注意:
- 有效的算符为
'+'
、'-'
、'*'
和'/'
。 - 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
- 两个整数之间的除法总是 向零截断 。
- 表达式中不含除零运算。
- 输入是一个根据逆波兰表示法表示的算术表达式。
- 答案及所有中间计算结果可以用 32 位 整数表示。
示例 1:
输入:tokens = [“2”,“1”,“+”,“3”,“*”]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = [“4”,“13”,“5”,“/”,“+”]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = [“10”,“6”,“9”,“3”,“+”,“-11”,““,”/“,””,“17”,“+”,“5”,“+”]
输出:22
解释:该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
提示:
1 <= tokens.length <= 10^4
tokens[i]
是一个算符("+"
、"-"
、"*"
或 "/"
),或是在范围 [-200, 200]
内的一个整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
- 平常使用的算式则是一种中缀表达式,如
( 1 + 2 ) * ( 3 + 4 )
。 - 该算式的逆波兰表达式写法为
( ( 1 2 + ) ( 3 4 + ) * )
。
逆波兰表达式主要有以下两个优点:
- 去掉括号后表达式无歧义,上式即便写成
1 2 + 3 4 + *
也可以依据次序计算出正确结果。 - 适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
思路:栈用来存放数字,遇到运算符就跳出最顶上的两个进行运算。注意,除法,第一个弹出的是除数,第二个才是被除数。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
stack<int> s;
for(string& a : tokens){
if(a != "+" && a != "-" && a != "*" && a != "/"){
s.push(stoi(a));
}else{
int x = s.top();
s.pop();
int y = s.top();
s.pop();
s.push(compute(y, x, a));
}
}
return s.top();
}
int compute(int x, int y, string com){
if(com == "+") return x + y;
else if(com == "-") return x - y;
else if(com == "*") return x * y;
else if(com == "/") return x / y;
return 0;
}
};
224. 基本计算器
给你一个字符串表达式 s
,请你实现一个基本计算器来计算并返回它的值。
注意:不允许使用任何将字符串作为数学表达式计算的内置函数,比如 eval()
。
示例 1:
输入:s = “1 + 1”
输出:2
示例 2:
输入:s = " 2-1 + 2 "
输出:3
示例 3:
输入:s = “(1+(4+5+2)-3)+(6+8)”
输出:23
提示:
1 <= s.length <= 3 * 10^5
s
由数字、'+'
、'-'
、'('
、')'
、和 ' '
组成
s
表示一个有效的表达式
'+'
不能用作一元运算(例如, "+1"
和 "+(2 + 3)"
无效)
'-'
可以用作一元运算(即 "-1"
和 "-(2 + 3)"
是有效的)
输入中不存在两个连续的操作符
每个数字和运行的计算将适合于一个有符号的 32位 整数
思路:一开始是用递归写的,遇到左括号就进新递归,右括号返回上一个递归。
class Solution {
private:
int i = 0;
public:
int calculate(string s) {
int n = s.size();
int rec = 0;
rec = compute_left(s);
return rec;
}
int compute_left(string s){
int sum = 0;
int sign = 1;
int n = s.size();
for(; i < n; i++){
if(s[i] >= '0' && s[i] <= '9'){
//是数字
int number = s[i] - '0';
while(i+1 < n && s[i+1] >= '0' && s[i+1] <= '9'){
i++;
number = number * 10 + (s[i] - '0');
}
sum += sign * number;
}else if(s[i] == '+'){
sign = 1;
}else if(s[i] == '-'){
sign = -1;
}else if(s[i] == '('){
i++;
sum += sign * compute_left(s);
}else if(s[i] == ')'){
return sum;
}
}
return sum;
}
};
思路:将公式展开进行计算,每次到一个符号的时候,如果栈的长度不是1,那么前面一定有括号,就存在括号内的符号可能会发生变化的情况。
class Solution {
public:
int calculate(string s) {
stack<int> sta;
sta.push(1);
int sum = 0;
int one_num = 0;
int op = 1;
for(char& c : s){
if(c >= '0' && c <= '9'){
one_num = one_num * 10 + (c - '0');
continue;
}
sum += op * one_num;
one_num = 0;
if(c == '+') op = sta.top();
else if(c == '-') op = -sta.top();
else if(c == '(') sta.push(op);
else if(c == ')') sta.pop();
}
sum += op * one_num;
return sum;
}
};