调度场算法

调度场算法（Shunting Yard Algorithm）是一个用于将中缀表达式转换为后缀表达式的经典算法，由艾兹格·迪杰斯特拉引入，因其操作类似于火车编组场而得名。

**简例**
算法示意图，使用了3个空间。输入用符号代替，如果输入是一个数字则直接进输出队列，即图中 b),d),f),h)。如果输入是运算符，则压入操作符堆栈，即图中 c),e)，但是，如果输入运算符的优先级低于或等于运算符栈顶的操作符优先级，则栈内元素进入输出队列，输入操作符压入运算符堆栈，即图中 g)。 最后，运算符堆栈内元素入输出队列，算法结束。
输入：3+4
#将3入输出队列（每当输入一个数字时，直接进入输出队列）
#将+号压入运算堆栈
#将4入输出队列
#输入结束，将操作符堆栈中剩余操作符入输出队列
#在本情况下只有+号
#输出 3+4
通过这个例子可以看出两条规则：- 当读入一个数字时直接入输出队列- 当输入结束后，运算符队列中所有操作符入输出队列

**详细的算法**
 当还有记号可以读取时：- 读取一个记号。- 如果这个记号表示一个数字，那幺将其添加到输出队列中。- 如果这个记号表示一个函数，那幺将其压入栈当中。- 如果这个记号表示一个函数参数的分隔符（例如，一个半角逗号 , ）：
:* 从栈当中不断地弹出操作符并且放入输出队列中去，直到栈顶部的元素为一个左括号为止。如果一直没有遇到左括号，那幺要幺是分隔符放错了位置，要幺是括号不匹配。- 如果这个记号表示一个操作符，记做o1，那幺：
:* 只要存在另一个记为o2的操作符位于栈的顶端，并且
:::: 如果o1是左结合性的并且它的运算符优先级要小于或者等于o2的优先级，或者
:::: 如果o1是右结合性的并且它的运算符优先级比o2的要低，那幺
::: 将o2从栈的顶端弹出并且放入输出队列中（循环直至以上条件不满足为止）；
:* 然后，将o1压入栈的顶端。- 如果这个记号是一个左括号，那幺就将其压入栈当中。- 如果这个记号是一个右括号，那幺：
:* 从栈当中不断地弹出操作符并且放入输出队列中，直到栈顶部的元素为左括号为止。
:* 将左括号从栈的顶端弹出，但并不放入输出队列中去。
:* 如果此时位于栈顶端的记号表示一个函数，那幺将其弹出并放入输出队列中去。
:* 如果在找到一个左括号之前栈就已经弹出了所有元素，那幺就表示在表达式中存在不匹配的括号。- 当再没有记号可以读取时：- 如果此时在栈当中还有操作符：
:* 如果此时位于栈顶端的操作符是一个括号，那幺就表示在表达式中存在不匹配的括号。
:* 将操作符逐个弹出并放入输出队列中。- 退出算法。

**更详细的例子**
中缀表示法 及 结果：3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3
*:逆波兰表示法：3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3

**C++程序实现**
#include 
#include 
#define op_left_assoc(c) (c == '+' 
c == '-' 
c == '/' 
c == '*' 
c == '%')
#define is_operator(c)   (c == '+' 
c == '-' 
c == '/' 
c == '*' 
c == '!' 
c == '%' 
c == '=')
#define is_function(c)   (c >= 'A' && c = '0' && c = 'a' && c  0)
{
sc = stacksl - 1;
//扫描到左括号
//跳出输出循环，此时左括号作为函数边界判定，所以不出栈
if (sc == '(')
{
pe = true;
break;
}
else {
// 栈顶元素不是左括号
// 将栈顶元素依次出栈并放入输出队列
*outpos = sc;
++outpos;
sl--;
}
}
// 如果没有遇到左括号，则有可能是符号放错或者不匹配
if (!pe)
{
printf("Error: separator or parentheses mismatched\n");
return false;
}
}
// 如果输入符号为运算符，然后:
else if (is_operator(c))
{
while (sl > 0)
{
sc = stacksl - 1;
// sc为其栈顶元素
// 如果c是左结合性的且它的优先级小于等于栈顶运算符sc的优先级
// 或者c是右结合性且它的优先级小于栈顶运算符sc的优先级
// 将栈顶元素sc出栈，否则sc进栈
if (is_operator(sc) && ((op_left_assoc(c) && (op_preced(c)  0)
{
sc = stacksl - 1;
if (sc == '(')
{
pe = true;
break;
}
else
{
*outpos = sc;
++outpos;
sl--;
}
}
// 如果没有扫描到左括号，则有可能是符号放错或者不匹配
if (!pe)
{
printf("Error: parentheses mismatched\n");
return false;
}
// 左括号出栈且不放入输出队列
sl--;
// 扫描完左括号后 
// 如果栈顶元素是函数运算符
// 则将其出栈并放入输出队列
if (sl > 0)
{
sc = stacksl - 1;
if (is_function(sc))
{
*outpos = sc;
++outpos;
sl--;
}
}
}
//未知运算符c
else printf("Unknown token %c\n", c);
}
++strpos;
}
// 当所有元素已经读完
// 栈中还有剩余运算符
while (sl > 0)
{
sc = stacksl - 1;
//如果剩余括号，则符号放错或者不匹配
if (sc == '(' 
sc == ')') 
{
printf("Error: parentheses mismatched\n");
return false;
}
//出栈并放入输出队列
*outpos = sc;
++outpos;
--sl;
}
*outpos = 0;//指针置零
return true;
}
bool execution_order(const char* input) 
{
printf("order: (arguments in reverse order)\n");
const char* strpos = input, * strend = input + strlen(input);
char c, res4;
unsigned int sl = 0, sc, stack32, rn = 0;
// While there are input tokens left
while (strpos  0) 
{
sc = stacksl - 1;
sl--;
if (nargs > 1) printf("%s, ", &sc);
else printf("%s)\n", &sc);
--nargs;
}
}
else 
{
if (nargs == 1) 
{
sc = stacksl - 1;
sl--;
printf("%c %s;\n", c, &sc);
}
else
{
sc = stacksl - 1;
sl--;
printf("%s %c ", &sc, c);
sc = stacksl - 1;
sl--;
printf("%s;\n", &sc);
}
}
// Push the returned results, if any, back onto the stack.
stacksl = *(unsigned int*)res;
++sl;
}
++strpos;
}
// If there is only one value in the stack
// That value is the result of the calculation.
if (sl == 1) 
{
sc = stacksl - 1;
sl--;
printf("%s is a result\n", &sc);
return true;
}
// If there are more values in the stack
// (Error) The user input has too many values.
return false;
}
int main()
{
// functions: A() B(a) C(a, b), D(a, b, c) ...
// identifiers: 0 1 2 3 ... and a b c d e ...
// operators: = - + / * % !
const char* input = "a = D(f - b * c + d, !e, g)";
char output128;
printf("input: %s\n", input);
if (shunting_yard(input, output)) 
{
printf("output: %s\n", output);
if (!execution_order(output))
printf("\nInvalid input\n");
}
return 0;
}

**参见**
 中缀表达式- 后缀表达式