#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "knLinkstack.h"
#if 0
//--------------------------------------------------------------------------
/*
栈的应用1
应用1:就近匹配
几乎所有的编译器都具有检测括号是否匹配的能力
如何实现编译器中的符号成对检测?
#include <stdio.h> int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;
算法思路
从第一个字符开始扫描
当遇见普通字符时忽略,当遇见左符号时压入栈中
当遇见右符号时从栈中弹出栈顶符号,并进行匹配
匹配成功:继续读入下一个字符
匹配失败:立即停止,并报错
结束:
成功: 所有字符扫描完毕,且栈为空
失败:匹配失败或所有字符扫描完毕但栈非空
当需要检测成对出现但又互不相邻的事物时
可以使用栈“后进先出”的特性
栈非常适合于需要“就近匹配”的场合
*/
int isLeft(char c)
{
int ret = 0;
switch (c)
{
case '<':
case '(':
case '[':
case '{':
case '\'':
case '\"':
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
int isRight(char c)
{
int ret = 0;
switch (c)
{
case '>':
case ')':
case ']':
case '}':
case '\'':
case '\"':
ret = 1;
break;
default:
ret = 0;
break;
}
return ret;
}
int match(char left, char right)
{
int ret = 0;
switch (left)
{
case'<':
ret = (right == '>');
break;
case'(':
ret = (right == ')');
break;
case'[':
ret = (right == ']');
break;
case'{':
ret = (right == '}');
break;
case'\'':
ret = (right == '\'');
break;
case'\"':
ret = (right == '\"');
break;
default:
ret = 0;
break;
}
return ret;
}
int scanner(const char *code)
{
//创建一个链栈
LinkStack *stack = LinkStack_Create();
int ret = 0;
int i = 0;
while (code[i] != 0){
//当遇见普通字符时忽略,当遇见左符号时压入栈中
if (isLeft(code[i])){
LinkStack_Push(stack, (void*)(code + i));
}
//当遇见右符号时从栈中弹出栈顶符号,并进行匹配
if (isRight(code[i])){
char *c = (char*)LinkStack_Pop(stack);
if ((NULL == c) || !match(*c, code[i])){
printf("%c does not match!\n", code[i]);
ret = 0;
break;
}
}
i++;
}
//成功: 所有字符扫描完毕,且栈为空 失败:匹配失败或所有字符扫描完毕但栈非空
if ((LinkStack_Size(stack) == 0) && (code[i] == '\0')){
printf("succeed!\n");
ret = 1;
}
else{
printf("invalid code!\n");
}
LinkStack_Destroy(stack);
return ret;
}
void test()
{
const char* code = "#include <stdio.h> int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;} ";
scanner(code);
}
#endif
#if 0
//--------------------------------------------------------------------
/*
应用2:中缀 后缀
计算机的本质工作就是做数学运算,那计算机可以读入字符串
“9 + (3 - 1) * 5 + 8 / 2”并计算值吗?
后缀表达式 ==?符合计算机运算
波兰科学家在20世纪50年代提出了一种将运算符放在数字后面的后缀表达式对应的,
我们习惯的数学表达式叫做中缀表达式===》符合人类思考习惯
实例:
5 + 4=> 5 4 +
1 + 2 * 3 => 1 2 3 * +
8 + ( 3 – 1 ) * 5 => 8 3 1 – 5 * +
中缀表达式符合人类的阅读和思维习惯
后缀表达式符合计算机的“运算习惯”
如何将中缀表达式转换成后缀表达式?
中缀转后缀算法:
遍历中缀表达式中的数字和符号
对于数字:直接输出
对于符号:
左括号:进栈
运算符号:与栈顶符号进行优先级比较
若栈顶符号优先级低:此符号进栈 (默认栈顶若是左括号,左括号优先级最低)
若栈顶符号优先级不低:将栈顶符号弹出并输出,之后进栈
右括号:将栈顶符号弹出并输出,直到匹配左括号
遍历结束:将栈中的所有符号弹出并输出
*/
int isNumber(char c)
{
return ('0' <= c) && (c <= '9');
}
int isOperator(char c)
{
return (c == '+') || (c == '-') || (c == '*') || (c == '/');
}
int isLeft2(char c)
{
return (c == '(');
}
int isRight2(char c)
{
return (c == ')');
}
int priority(char c)
{
int ret = 0;
if ((c == '+') || (c == '-'))
{
ret = 1;
}
if ((c == '*') || (c == '/'))
{
ret = 2;
}
return ret;
}
void output(char c)
{
if (c != '\0')
{
printf("%c", c);
}
}
void transform(const char *exp)
{
int i = 0;
LinkStack *stack = LinkStack_Create();
while (exp[i] != '\0'){
//对于数字:直接输出
if (isNumber(exp[i])){
output(exp[i]);
}
else if (isOperator(exp[i])){ //运算符号 加减乘除: 与栈顶符号进行优先级比较
while (priority(exp[i]) <= priority((char)(int)LinkStack_Top(stack))){
output((char)(int)LinkStack_Pop(stack)); //若栈顶符号优先级低,将栈顶符号弹出并输出
}
LinkStack_Push(stack, (void*)(int)exp[i]); //此符号进栈(默认栈顶若是左括号,左括号优先级最低)
}
else if (isLeft2(exp[i])){ //遇到了 左括号 进栈
LinkStack_Push(stack, (void*)(int)exp[i]);
}
else if (isRight2(exp[i])){ //遇到了 右括号
while (!isLeft2((char)(int)LinkStack_Top(stack))){//右括号:将栈顶符号弹出并输出,直到匹配左括号
output((char)(int)LinkStack_Pop(stack));
}
LinkStack_Pop(stack); //将左括号弹出
}
else {
printf("Invalid expression\n");
break;
}
i++;
}
//遍历结束:将栈中的所有符号弹出并输出
while ((LinkStack_Size(stack) > 0) && (exp[i] == '\0')){
output((char)(int)LinkStack_Pop(stack));
}
LinkStack_Destroy(stack);
}
void test()
{
char str[64] = "8+(3-1)*5";
printf("中缀表达式符合人类的阅读和思维习惯:\n%s\n", str);
printf("后缀表达式符合计算机的“运算习惯:\n");
transform(str);
}
#endif
#include "stdlib.h"
#include "stdio.h"
#include "string.h"
#include "knLinkstack.h"
#if 1
/*
计算机是如何基于后缀表达式计算的?
8 3 1 – 5 * +
遍历后缀表达式中的数字和符号
对于数字:进栈
对于符号:
从栈中弹出右操作数
从栈中弹出左操作数
根据符号进行运算
将运算结果压入栈中
遍历结束:栈中的唯一数字为计算结果
*/
int isNumber(char c)
{
return ('0' <= c) && (c <= '9');
}
int isOperator(char c)
{
return (c == '+') || (c == '-') || (c == '*') || (c == '/');
}
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 compute(const char*exp)
{
LinkStack *stack = LinkStack_Create();
int ret = 0;
int i = 0;
while (exp[i] != '\0')
{
//对于数字,进栈
if (isNumber(exp[i])){
LinkStack_Push(stack, (void*)value(exp[i]));
}
else if (isOperator(exp[i])){
//对于符号:从栈中弹出右操作数从栈中弹出左操作数
//根据符号进行运算 将运算结果压入栈中
int right = (int)LinkStack_Pop(stack);
int left = (int)LinkStack_Pop(stack);
int result = express(left, right, exp[i]);
LinkStack_Push(stack, (void*)result);
}
else{
printf("invalid expression\n");
}
i++;
}
//遍历结束, 栈中唯一的数字为计算结果
if ((LinkStack_Size(stack) == 1) && (exp[i] == '\0')){
ret = (int)LinkStack_Pop(stack);
}
else{
printf("invalid expression\n");
}
LinkStack_Destroy(stack);
return ret;
}
void test()
{
char str[64] = "831-5*+";
printf("计算机计算后缀表达式:%s\n", str);
printf("8 + (3 - 1) * 5 = %d\n", compute(str));
}
#endif
int main()
{
test();
printf("\n");
system("pause");
return 0;
}