有个题:
链接里有个大佬写了个逆波兰表达式求解。
逆波兰表达式也叫后缀表达式,依靠一个栈将字符串全部按照逆波兰表达式方式压入,在之后弹出的处理中,能够依照顺序进行计算。相应还有前缀表达式(就是波兰表达式)和中缀表达式,想法都有区别。
C语言的神器---指针。下面是稍加修改后的C语言代码:
c#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef enum Type
{
NUMBER,
OPERATOR
} Type;
typedef struct Node
{
Type type;
float val;
} Node;
typedef struct Stack
{
int top;
Node *table[1000];
} Stack;
void stack_init(Stack *stack)
{
stack->top = 0;
}
void stack_push(Stack *stack, Node *pNode)
{
stack->table[stack->top++] = pNode;
}
Node *stack_pop(Stack *stack)
{
return stack->table[--stack->top];
}
Node *stack_top(Stack *stack)
{
return stack->table[stack->top - 1];
}
int stack_empty(Stack *stack)
{
return stack->top == 0;
}
Node *split(const char *s, int *length)
{
*length = 0;
int len = strlen(s);//直到碰到第一个字符串结束符'\0'为止 返回长度
Node *arr = (Node *)malloc(sizeof(Node) * len);
char digit[16];
for (int i = 0; i < len; i++)
{
if (s[i] == ' ')
continue;
if (s[i] == '+' || s[i] == '-' || s[i] == '*' || s[i] == '/' || s[i] == '(' || s[i] == ')' )
{
arr[*length].type = OPERATOR;
arr[(*length)++].val = s[i];
}
else
{
int j = 0;
while ((s[i] >= '0' && s[i] <= '9')||(s[i]=='.'))
digit[j++] = s[i++];
i--;
digit[j] = '\0';
arr[*length].type = NUMBER;
arr[(*length)++].val = strtod(digit, NULL);
}
}
return arr;
}
Node **toReversePolish(Node *arr, int *arrLength)
{
int len = *arrLength;
Stack stack;
stack_init(&stack);
Node **revPol = (Node **)malloc(sizeof(Node *) * len);
int idx = 0;
for (int i = 0; i < len; i++)
{
if (arr[i].type == NUMBER)
revPol[idx++] = &arr[i];
else if ((int)(arr[i].val) == ')')
{
*arrLength -= 2;
while (!stack_empty(&stack) && (int)(stack_top(&stack)->val) != '(')
revPol[idx++] = stack_pop(&stack);
stack_pop(&stack);
}
else if (stack.top == 0 || (int)(stack_top(&stack)->val) == '(' || (int)(arr[i].val) == '(')
stack_push(&stack, &arr[i]);
else
{
Node *op = stack_top(&stack);
if ((int)(op->val) == '+' || (int)(op->val) == '-')
{
if ((int)(arr[i].val) == '+' || (int)(arr[i].val) == '-')
{
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
else
stack_push(&stack, &arr[i]);
}
else
{
if ((int)(arr[i].val) == '+' || (int)(arr[i].val) == '-')
{
while (!stack_empty(&stack) && (int)(stack_top(&stack)->val) != '(')
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
else
{
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
}
}
}
while (!stack_empty(&stack))
revPol[idx++] = stack_pop(&stack);
return revPol;
}
void printNodes(Node **nodes, int len)
{
for (int i = 0; i < len; i++)
{
if (nodes[i]->type == NUMBER)
printf("%d, ", nodes[i]->val);
else
printf("%c, ", nodes[i]->val);
}
putchar('\n');
}
float calc(Node *a, Node *b, Node *op)
{
switch ((int)(op->val))
{
case '+':
return a->val + b->val;
case '-':
return a->val - b->val;
case '*':
return a->val * b->val;
case '/':
return a->val / b->val;
}
return 0;
}
float calculate(char *s)
{
int arrLength;
Node *arr = split(s, &arrLength);
Node **revPol = toReversePolish(arr, &arrLength);
Stack stack;
stack_init(&stack);
for (int i = 0; i < arrLength; i++)
{
if (revPol[i]->type == NUMBER)
{
stack_push(&stack, revPol[i]);
}
else
{
Node *b = stack_pop(&stack);
Node *a = stack_pop(&stack);
a->val = calc(a, b, revPol[i]);
stack_push(&stack, a);
}
}
float result = stack.table[0]->val;
free(arr);
free(revPol);
return result;
}
void main()
{
printf("%.3f",calculate("12.7+((4+5/5)+1)*2+(1-4*4)"));
}
(1)栈里top装顶点元素index+一个数组。这里的数组是一个长度为1000的结构体指针。
ctypedef struct Stack
{
int top;
Node *table[1000];
} Stack;
(2)结构体。抽象叫node,node类型要么是数字类型,要么是操作符号类型。node数值是val。
如果是123,那么Type就是NUMBER,val就是123。
如果是‘+’,那么Type就是OPERATOR,val就是‘+’。
ctypedef struct Node
{
Type type;
float val;
} Node;
(3)利用split将原始字符串按照结构体Node分割,全部给到Node *arr。注意,指针arr一直指向这段内存的首地址。
cNode *arr = (Node *)malloc(sizeof(Node) * len);
(4)函数toReversePolish用第三步得到的指针为参数,将整个 Node化的字符串(Node arr[])按照逆波兰方式重新排序。、
学习指向指针的指针:https://www.runoob.com/cprogramming/c-pointer-to-pointer.html
cNode **toReversePolish(Node *arr, int *arrLength)
从前到后遍历arr,当前元素是arr[i]时候,有如下处理。
c if (arr[i].type == NUMBER)
revPol[idx++] = &arr[i];//如果是数字就直接放入Node revPol[] 意会,其实是指向指针的指针,最后重新排序的元素内容其实是Node arr[]的一堆指针地址。
else if ((int)(arr[i].val) == ')') //逆波兰的括号处理
{
*arrLength -= 2;//回退
while (!stack_empty(&stack) && (int)(stack_top(&stack)->val) != '(')
revPol[idx++] = stack_pop(&stack);//出栈
stack_pop(&stack);//出栈
}
else if (stack.top == 0 || (int)(stack_top(&stack)->val) == '(' || (int)(arr[i].val) == '(')////逆波兰的括号处理
stack_push(&stack, &arr[i]);
else //逆波兰的运算符号
{
Node *op = stack_top(&stack);
if ((int)(op->val) == '+' || (int)(op->val) == '-')
{
if ((int)(arr[i].val) == '+' || (int)(arr[i].val) == '-')
{
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
else
stack_push(&stack, &arr[i]);
}
else
{
if ((int)(arr[i].val) == '+' || (int)(arr[i].val) == '-')
{
while (!stack_empty(&stack) && (int)(stack_top(&stack)->val) != '(')
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
else
{
revPol[idx++] = stack_pop(&stack);
stack_push(&stack, &arr[i]);
}
}
}
c// 1+2+3 -> 12+3+
// 1-2-3 -> 12-3-
// 1+2-3 -> 12+3-
// 1-2+3 -> 12-3+
// 1*2*3 -> 12*3*
// 1+2*3 -> 123*+
// 1*2+3 -> 12*3+
// 1/2/3 -> 12/3/
// 1*2/3 -> 12*3/
// 1+2/3 -> 123/+
// 1+2*3*4 -> 123*4*+
// 1+2+3*4*5 -> 12+34*5*+
// 1+2+3*4+5 -> 12+34*+5+
// 1+2*3*4+5 -> 123*4*+5+
// 低遇低,弹出一个,压入一个
// 低遇高,压入一个
// 高遇低,全部弹出,压入一个
// 高遇高,弹出一个,压入一个
// 遇左括,直接压入
// 遇右扩,全部弹出直到上一个左括
// (1-2)-3 -> 12-3-
// 1-(2-3) -> 123--
// (1-2)*3 -> 12-3*
// 1-(2*3) -> 123*-
// (1/2)/3 -> 12/3/
// 1*(2/3) -> 123/*
// (1+2)*((3-7)/(2+(6-7)))*(3+6) -> 12+37-*267-+/36+* OR 12+37-267-+/*36+*
作者:yu-tang-7
链接:https://leetcode-cn.com/problems/basic-calculator/solution/ni-bo-lan-biao-da-shi-jie-fa-zhi-jie-zhi-chi-by-yu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
(5)遍历逆波兰表达式,利用一个栈对逆波兰表达式按照顺序计算。
cfor (int i = 0; i < arrLength; i++)
{
if (revPol[i]->type == NUMBER)
{
stack_push(&stack, revPol[i]); //push
}
else
{
Node *b = stack_pop(&stack);//pop出1个操作数
Node *a = stack_pop(&stack);//pop出1个操作数
a->val = calc(a, b, revPol[i]);//计算pop出来的2个操作数
stack_push(&stack, a);//把计算结果push进去
}
}
本文作者:Dong
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC。本作品采用《知识共享署名-非商业性使用 4.0 国际许可协议》进行许可。您可以在非商业用途下自由转载和修改,但必须注明出处并提供原作者链接。 许可协议。转载请注明出处!