首先是写栈的习惯:一定要在创建完栈后 就立即写上销毁栈的函数 否则很容易遗忘销毁 导致内存泄露
在唐老师讲的符号配对检测的程序时 有一个小细节 老师并没有提出来 但是后来发现了 :
char* c = (char*)LinkStack_Pop(stack);
//进行检测 格外注意 一定要先c==NULL
if( (c == NULL) || !match(*c,code[i]) )
{
printf("is error");
ret = 0;
break;
}
这里进行了标红 就是先从堆栈里取出了栈顶元素 然后进行判断 这里先进性了判断c是否是NULL ,因为如果要是先调用MATCH函数 当*c 的时候 可能会由于c的指向地址是NULL
所以导致不发找到该地址 最终导致程序崩溃;
然后是计算器的运行:人类习惯的运算式是中缀表达式例如: 3+5、3+4+4、3*(3+3)*4;
而机器习惯的运算表达式是:35+,34+4+;等等
以下是唐老师给出的例子:
中缀表达式:后缀表达式:
5 + 3 =5 3 +
1 + 2 * 3
= 1 2 3 * +
9 + ( 3 – 1 ) * 5= 9 3 1 – 5 * +
而如何将人们习惯的中缀表达式转成后缀表达式呢??这个老师给出了答案:
解决方案:
遍历中缀表达式中的数字和符号对于数字:直接输出 直接输出
对于符号:
• 左括号 :进栈 进栈
• 符号 :与栈顶符号进行优先级比较 与栈顶符号进行优先级比较
• 栈顶符号优先级低:进栈 进栈
• 栈顶符号优先级不低:将栈顶符号弹出并输出 将栈顶符号弹出并输出, ,之后进栈 之后进栈
• 右括号:将栈顶符号弹出并输出 将栈顶符号弹出并输出, ,直到匹配左括号 直到匹配左括号
遍历结束:将栈中的所有符号弹出并输出 将栈中的所有符号弹出并输出
而计算机又是如何对后缀表达式进行运算的呢??这个老师也给出了答案:
解决方案
遍历后缀表达式中的数字和符号对于数字:进栈 进栈
对于符号:
• 从栈中弹出右操作数(记住先弹出的是有操作数)
• 从栈中弹出左操作数(后弹出的是左操作数)
• 根据符号进行运算
• 将运算结果压入栈中
遍历结束:栈中的唯一数字为计算结果
对于顺序链表唐老师用的是链表的尾作为栈顶,而普通的链表做栈是用的是表头作为栈顶;
我觉得对于顺序链表表尾作为栈顶很正确,毕竟顺序链表是数组,如果以表头作为栈顶,在进行插入的时候,要将所有数据都进行后移,这样过于浪费运行时间
而用普通的链表作为栈时,用表头或者标尾作为栈顶就没有什么区别,用表头作为栈顶是,不论是取元素还是删除元素时,直接将函数中需要操作的节点位置写零即可,而标尾还要检测长度;
对于第四专题四的课后习题里唐老师让将整个计算器的算法不全,可以识别两位数还有能够自己进行中缀转后缀,然后进行后缀计算;
这里就需要设置两个栈了:1:符号栈 2:数字栈
当为数字是,直接进入数字栈,而为符号时,就进入符号栈
而进入符号栈时,什么情况入栈,什么情况出栈和中缀转后缀的方法一样,当符号要进行出栈时,数字栈就要同时进行左右操作时出栈,然后进行运算,再将结果数字栈中;
这样就可以实现中缀转后缀,后缀进行计算了;
但这里有一个小细节就是当数字入栈是一定要先把字符的数字转成数字int型 然后在进行压栈,否则如果在后面的计算结果进行压栈时,如果结果是两位数,然么用字符表示就会出错,然后也没法将两位数转成数字(因为不确定生成的是几位数)所以最好的方法就是在数字压栈时就直接转成int型的数字然后压栈,就不会涉及到结果是几位数了;
//判断是否是数字
int isnumber(char c)
{
return ( c >= '0' ) && ( c <= '9' );
}
//输出字符函数
void output(char c)
{
printf("%c",c);
}
//判断是否是左括号
int isLeft(char c)
{
return (c == '(');
}
//判断是否是右括号
int isRight(char c)
{
return (c == ')');
}
//判断是否是操作符
int isOperator(char c)
{
return ( c == '+' ) || ( c == '-' ) || ( c == '*' ) || ( c == '/' );
}
//判断操作符的优先级
int priority(char c)
{
int ret = 0;
if( (c == '+') || (c == '-') )
{
ret = 1;
}
if( (c == '*') || (c == '/') )
{
ret = 2;
}
return ret;
}
//字符转成数字
int value( char c )
{
return ( c - '0' );
}
int express(int left, int right, char op)
{
int ret = 0;
switch(op)
{
case '+':
ret = left + right;
break;
case '-':
ret = left - right;
break;
case '*':
ret = left * right;
break;
case '/':
ret = left / right;
break;
default:
break;
}
return ret;
}
//转后缀与后缀计算结合体
int arithmic(const char* exp)
{
//存储数字的栈
LinkStack* num = LinkStack_Creat();
//存储运算符的栈
LinkStack* ope = LinkStack_Creat();
int i = 0;
int right = 0; //读取数字栈时 存储右值
int left = 0; //读取数字栈时 存储右值
int result = 0;
char name = '\0'; //存放符号栈出栈时的符号
int ret = 0;
while( exp[i] != '\0')
{
// 是数字
if( isnumber(exp[i]) )
{
int tem = 0;
int r = 0;
int q = 0;
while( isnumber(exp[i + r]))//这里的循环是实现多位数字的运算
{
for(q=0; q<r; q++)
{
tem*=10;
}
tem += value(exp[i+r]);
r++;
}
i+=(r-1);
LinkStack_Push(num,(void*)tem);
// LinkStack_Push(num,(void*)value(exp[i]));
}
//是左括号
else if( isLeft(exp[i]) )
{
LinkStack_Push(ope,(void*)(int)exp[i]);
}
//是右括号
else if( isRight(exp[i]))
{
while( !isLeft( (char)(int)LinkStack_Top(ope) ) )
{
right = (int)LinkStack_Pop(num);
left = (int)LinkStack_Pop(num);
name = (char)(int)LinkStack_Pop(ope);
result = express(left,right,name);
printf("result=%d\n",result);
LinkStack_Push(num,(void*)result);
// LinkStack_Push(num,(void*)result);
}
//将左括号 推出栈
LinkStack_Pop(ope);
}
//是操作符
else if( isOperator(exp[i]) )
{
while( priority(exp[i]) <= priority((char)(int)LinkStack_Top(ope)) )
{
right = (int)LinkStack_Pop(num);
// printf("right:%d\n",right);
left = (int)LinkStack_Pop(num);
// printf("left:%d\n",left);
name = (char)(int)LinkStack_Pop(ope);
result = express(left,right,name);
// printf("left:%d\n",result);
LinkStack_Push(num,(void*)result);
}
LinkStack_Push(ope,(void*)(int)exp[i]);
}
else
{
printf("is error");
break;
}
i++;
}
//当字符串扫描完毕后
if( LinkStack_Size(num)>0 && exp[i]=='\0' )
{
while( LinkStack_Size(ope) > 0 )
{
printf("size=%d\n",LinkStack_Size(num));
right = (int)LinkStack_Pop(num);
printf("right:%d\n",right);
left = (int)LinkStack_Pop(num);
printf("left:%d\n",left);
name = (char)(int)LinkStack_Pop(ope);
printf("%c\n",name);
result = express(left,right,name);
LinkStack_Push(num,(void*)(result));
// LinkStack_Push(num,(void*)result);
}
if( LinkStack_Size(num) > 1)
{
printf("is error");
// break;
}
ret = (int)LinkStack_Pop(num);
}
//一定要记得销毁栈
LinkStack_Destroy(num);
LinkStack_Destroy(ope);
return ret;
}