20. 有效的括号
一、做题感受&第一想法
用栈保存暂未匹配的左括号。
①每当扫描到一个右括号,弹出栈顶元素,看是否是与该右括号匹配的左括号,不是则匹配失败,返回false。如果栈已空,匹配失败,返回false。
②整个字符串扫描完毕,如果栈不空,则说明有未匹配的左括号,匹配失败,返回false。如果栈空,则所有括号完成匹配,返回true。
#define MAX_SIZE 10000
bool isValid(char* s) {
char stack[MAX_SIZE];
int top = 0;
for(int i = 0; s[i] != '\0'; i++){
if(s[i]=='(' || s[i] == '[' || s[i] == '{' ){
stack[top] = s[i];
top++;
}
else if(s[i] == ')'){
if(top != 0){ //不要忘记判断top是否等于0,否则top--会error。
top--;
}
else{
return false;
}
if(stack[top] != '('){
return false;
}
}
else if(s[i] == ']'){
if(top != 0){
top--;
}
else{
return false;
}
if(stack[top] != '['){
return false;
}
}
else if(s[i] == '}'){
if(top != 0){
top--;
}
else{
return false;
}
if(stack[top] != '{'){
return false;
}
}
}
if(top == 0){
return true;
}
else{
return false;
}
}
二、学习文章后收获
1.括号不匹配的三种情况
要充分考虑这三种情况是否在编程中体现:
①括号类型不匹配
②左端多了左括号
③右端多了右括号
1047. 删除字符串中的所有相邻重复项
一、做题感受&第一想法
一开始没有想到栈,但是因为这是栈与队列的题目,所以还是想到了。这说明对栈的应用场景还是不熟悉!
#define MAX_SIZE 200000
char* removeDuplicates(char* s) {
char* stack = (char*)malloc(sizeof(char)*MAX_SIZE);
int top = 0, i = 0;
for(i = 0; s[i] != '\0'; i++){
if(top != 0 && s[i] == stack[top - 1]){ //如果和栈顶元素相同,则删除栈顶元素
top--;
}
else{
stack[top] = s[i];
top++;
}
}
stack[top] = '\0';
printf("%d",i);
return stack;
}
二、学习文章后收获
1.栈的经典应用: 删除字符串中的所有相邻重复项
要知道栈为什么适合做这种类似于爱消除的操作,因为栈帮助我们记录了 遍历数组当前元素时候,前一个元素是什么。
150. 逆波兰表达式求值
一、做题感受&第一想法
一开始把逆波兰式求值和前缀求值搞混了,上来就定义了两个栈,然后写着写着发现并不需要(笑丝)。
#define MAX_SIZE 10000
int operation(char op,int left,int right){
printf("operation:%d %c %d\n",left,op,right);
switch(op){
case '+': return left+right;
case '-': return left-right;
case '*': return left*right;
case '/': return left/right;
}
return -1;
}
int backIntoNum(char* s){
int num = 0, i = 0;
if(s[0] == '-'){ //先判断是否为负数
i++;
}
for(; s[i] != '\0';i++){
num *= 10;
num += s[i] - '0';
}
return (s[0]=='-'? (-1)*num : num); //如果是负数,则需要乘-1
}
int evalRPN(char** tokens, int tokensSize) {
int numStack[MAX_SIZE];
int numTop = 0;
int left = 0, right = 0, result = 0;
for(int i = 0; i < tokensSize ;i++){
if((tokens[i][0] == '+' || tokens[i][0] == '-' || tokens[i][0] == '*' || tokens[i][0] == '/') && tokens[i][1] == '\0'){
numTop--;
right = numStack[numTop]; //右操作数出栈
numTop--;
left = numStack[numTop]; //左操作数出栈
result = operation(tokens[i][0],left,right); //得到运算结果
numStack[numTop] = result; //运算结果入栈
numTop++;
}
else{ //如果是数字
numStack[numTop] = backIntoNum(tokens[i]); //字符串转换成数字
numTop++; //数字入栈
}
}
return numStack[numTop-1]; //返回栈顶元素(是栈里唯一一个元素)
}
二、学习文章后收获
1.逆波兰式是二叉树的后序序列
三、过程中遇到的问题
1.switch语句的语法淡忘了
switch(){
case xxx:
语句1;
语句2;
...
break;
case yyy:
语句1;
...
break;
default:
语句1;
...
break;
}
2.一开始对char**的理解不清楚。
①token[i]
对应的是一个char*
指针,即后面存的是一个字符串。token[i][j]
是字符串里的各个元素。
②如果字符串token[i]
是运算符:(tokens[i][0] == '+' || tokens[i][0] == '-' || tokens[i][0] == '*' || tokens[i][0] == '/') && tokens[i][1] == '\0'
不能忘记判断token[i][1]
,因为如果是负数那么第一个字符也是运算符(减号)。
③如果字符串token[i]
是数字:要把字符串转成数字(注意先判断是否是负数)
int backIntoNum(char* s){
int num = 0, i = 0;
if(s[0] == '-'){ //先判断是否为负数
i++;
}
for(; s[i] != '\0';i++){
num *= 10;
num += s[i] - '0';
}
return (s[0]=='-'? (-1)*num : num); //如果是负数,则需要乘-1
}
3.数字字符的ASCII码
数字字符的ASCII码不是从0开始的!
比如,数字0的ASCII码是48(后面的数字依次加一)。
所以由数字字符转成数字真值,可以用s[i] - '0'
的方式,减掉0的ASCII码。
4.分清楚:“逆波兰式求值”、“中缀表达式转逆波兰式”、“中缀表达式求值”
(1)逆波兰式求值:
①一个栈,数字栈。
②每扫描到数字,入栈;每扫描到运算符,把栈顶两个元素和运算符做运算,再将结果入栈。
③最后,栈中只剩一个数字,即为结果。
(2)中缀转逆波兰:(考虑括号)
①一个栈,运算符栈。
②扫描中缀表达式。每扫描到数字,输出。
③每扫描到运算符,先将运算符栈中优先级“高于和等于”它的运算符依次弹出栈(到优先级小于它、或左括号、或栈空,为止),再把当前运算符压入栈。④遇到“(”,则压入栈。遇到“)”,将栈中运算符依次弹出,直到弹出“(”为止。
(3)中缀求值:(上述二者的结合)
①两个栈。一个数字栈numStack,一个运算符栈opStack。
②扫描中缀表达式。每扫描到数字,压入数字栈。
③每扫描到运算符,先将运算符栈中优先级“高于和等于”它的运算符依次弹出栈(到优先级小于它、或左括号、或栈空,为止),每弹出一个运算符,要从数字栈弹出两个运算数,把运算结果压入数字栈。最后把当前运算符压入运算符栈。
④遇到“(”,则压入栈。遇到“)”,将栈中运算符依次弹出并做运算,直到弹出“(”为止。
⑤扫描完毕后,运算符栈如果不为空,则依次弹出运算符并做运算。
⑥最终,运算符栈为空。数字栈只剩一个数字,即为结果。