fbpx
维基百科

调度场算法

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

簡例

 
算法示意图,使用了3个空间。输入用符号代替,如果输入是一个数字则直接进输出队列,即图中 b),d),f),h)。如果输入是运算符,则压入操作符堆栈,即图中 c),e),但是,如果输入运算符的优先级低于或等于运算符栈顶的操作符优先级,则栈内元素进入输出队列,输入操作符压入运算符堆栈,即图中 g)。 最后,运算符堆栈内元素入输出队列,算法结束。
输入:3+4
  1. 将3入输出队列(每当输入一个数字时,直接进入输出队列)
  2. 将+号压入运算堆栈
  3. 将4入输出队列
  4. 输入结束,将操作符堆栈中剩余操作符入输出队列
  5. 在本情况下只有+号
  6. 输出 3 4 +

通过这个例子可以看出两条规则:

  • 当读入一个数字时直接入输出队列
  • 当输入结束后,运算符队列中所有操作符入输出队列

详细的算法

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

更详细的例子

  • 中綴表示法 及 結果: 
    逆波兰表示法:3 4 2 * 1 5 - 2 3 ^ ^ / +
输入: 3 + 4 * 2 / ( 1 − 5 ) ^ 2 ^ 3
输入 动作 输出 (逆波兰表示法) 运算符堆栈 提示
3 将符号加入输出队列 3
+ 将符号压入操作符堆栈 3 +
4 将符号加入输出队列 3 4 +
* 将符号压入操作符堆栈 3 4 * + *号的优先级高于+号
2 将符号加入输出队列 3 4 2 * +
/ 将堆栈中元素弹出,加入输出队列 3 4 2 * + /号和*号优先级相同
将符号压入操作符堆栈 3 4 2 * / + /号的优先级高于+号
( 将符号压入操作符堆栈 3 4 2 * ( / +
1 将符号加入输出队列 3 4 2 * 1 ( / +
将符号压入操作符堆栈 3 4 2 * 1 − ( / +
5 将符号加入输出队列 3 4 2 * 1 5 − ( / +
) 将堆栈中元素弹出,加入输出队列 3 4 2 * 1 5 − ( / + 循环直到找到(号
将堆栈元素弹出 3 4 2 * 1 5 − / + 括号匹配结束
^ 将符号压入操作符堆栈 3 4 2 * 1 5 − ^ / + ^号的优先级高于/号
2 将符号加入输出队列 3 4 2 * 1 5 − 2 ^ / +
^ 将符号压入操作符堆栈 3 4 2 * 1 5 − 2 ^ ^ / + ^号为从右至左求值
3 将符号加入输出队列 3 4 2 * 1 5 − 2 3 ^ ^ / +
END 将栈中所有数据加入输出队列 3 4 2 * 1 5 − 2 3 ^ ^ / +

C++程序实现

#include <cstring> #include <cstdio> #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 <= 'Z') #define is_ident(c) ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')) // 操作符 // 优先级 符号 运算顺序 // 1 ! 从右至左 // 2 * / % 从左至右 // 3 + - 从左至右 // 4 = 从右至左 int op_preced(const char c) {  switch (c)   {  case '!':  return 4;  case '*': case '/': case '%':  return 3;  case '+': case '-':  return 2;  case '=':  return 1;  }  //若输入不是运算符  return 0; } unsigned int op_arg_count(const char c) {  switch (c)  {  //运算符  case '*': case '/': case '%': case '+': case '-': case '=':  return 2;  //阶乘  case '!':  return 1;  //不是运算符  default:  return c - 'A';  }  return 0; } bool shunting_yard(const char* input, char* output) {  const char* strpos = input, * strend = input + strlen(input);  char c, stack[32], sc, * outpos = output;  unsigned int sl = 0;  while (strpos < strend)  {  c = *strpos;  if (c != ' ')  {   // 扫描到左括号直接入栈  if (c == '(')  {  stack[sl] = c;  ++sl;  }  // 如果输入为数字,则直接入输出队列  else if (is_ident(c))  {  *outpos = c;  ++outpos;  }  // 如果输入为函数记号,则压入堆栈  else if (is_function(c))  {  stack[sl] = c;  ++sl;  }  // 如果输入为函数分割符(如:逗号)  else if (c == ',')  {  bool pe = false;  while (sl > 0)  {  sc = stack[sl - 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 = stack[sl - 1];  // sc为其栈顶元素  // 如果c是左结合性的且它的优先级小于等于栈顶运算符sc的优先级  // 或者c是右结合性且它的优先级小于栈顶运算符sc的优先级  // 将栈顶元素sc出栈,否则sc进栈  if (is_operator(sc) && ((op_left_assoc(c) && (op_preced(c) <= op_preced(sc))) ||   (!op_left_assoc(c) && (op_preced(c) < op_preced(sc)))))   {  *outpos = sc;  ++outpos;  sl--;  }  else  {  break;  }  }  //c的优先级大于或大于等于结合性的要求,则将其入栈  stack[sl] = c;  ++sl;  }  // 扫描到右括号  else if (c == ')')  {  bool pe = false;  // 从栈顶向下扫描左括号,将扫描到左括号之前的栈顶运算符出栈并放入输出队列  while (sl > 0)  {  sc = stack[sl - 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 = stack[sl - 1];  if (is_function(sc))  {  *outpos = sc;  ++outpos;  sl--;  }  }  }  //未知运算符c  else printf("Unknown token %c\n", c);  }  ++strpos;  }  // 当所有元素已经读完  // 栈中还有剩余运算符  while (sl > 0)  {  sc = stack[sl - 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, res[4];  unsigned int sl = 0, sc, stack[32], rn = 0;  // While there are input tokens left  while (strpos < strend)   {  // Read the next token from input.  c = *strpos;  // If the token is a value or identifier  if (is_ident(c))   {  // Push it onto the stack.  stack[sl] = c;  ++sl;  }  // Otherwise, the token is an operator (operator here includes both operators, and functions).  else if (is_operator(c) || is_function(c))  {  sprintf(res, "_%02d", rn);  printf("%s = ", res);  ++rn;  // It is known a priori that the operator takes n arguments.  unsigned int nargs = op_arg_count(c);  // If there are fewer than n values on the stack  if (sl < nargs ) return false; // (Error) The user has not input sufficient values in the expression.  // Else, Pop the top n values from the stack.  // Evaluate the operator, with the values as arguments.  if (is_function(c))   {  printf("%c(", c);  while (nargs > 0)   {  sc = stack[sl - 1];  sl--;  if (nargs > 1) printf("%s, ", &sc);  else printf("%s)\n", &sc);  --nargs;  }  }  else   {  if (nargs == 1)   {  sc = stack[sl - 1];  sl--;  printf("%c %s;\n", c, &sc);  }  else  {  sc = stack[sl - 1];  sl--;  printf("%s %c ", &sc, c);  sc = stack[sl - 1];  sl--;  printf("%s;\n", &sc);  }  }  // Push the returned results, if any, back onto the stack.  stack[sl] = *(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 = stack[sl - 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 output[128];  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; } 

参见

调度场算法, 此條目没有列出任何参考或来源, 2014年2月8日, 維基百科所有的內容都應該可供查證, 请协助補充可靠来源以改善这篇条目, 无法查证的內容可能會因為異議提出而移除, shunting, yard, algorithm, 是一个用于将中缀表达式转换为后缀表达式的经典算法, 由艾兹格, 迪杰斯特拉引入, 因其操作类似于火车编组场而得名, 目录, 簡例, 详细的算法, 更详细的例子, 程序实现, 参见簡例, 编辑, 算法示意图, 使用了3个空间, 输入用符号代替, 如果输入是一个数字则直接进输出队列, 即. 此條目没有列出任何参考或来源 2014年2月8日 維基百科所有的內容都應該可供查證 请协助補充可靠来源以改善这篇条目 无法查证的內容可能會因為異議提出而移除 调度场算法 Shunting Yard Algorithm 是一个用于将中缀表达式转换为后缀表达式的经典算法 由艾兹格 迪杰斯特拉引入 因其操作类似于火车编组场而得名 目录 1 簡例 2 详细的算法 3 更详细的例子 4 C 程序实现 5 参见簡例 编辑 算法示意图 使用了3个空间 输入用符号代替 如果输入是一个数字则直接进输出队列 即图中 b d f h 如果输入是运算符 则压入操作符堆栈 即图中 c e 但是 如果输入运算符的优先级低于或等于运算符栈顶的操作符优先级 则栈内元素进入输出队列 输入操作符压入运算符堆栈 即图中 g 最后 运算符堆栈内元素入输出队列 算法结束 输入 3 4将3入输出队列 每当输入一个数字时 直接进入输出队列 将 号压入运算堆栈 将4入输出队列 输入结束 将操作符堆栈中剩余操作符入输出队列 在本情况下只有 号 输出 3 4 通过这个例子可以看出两条规则 当读入一个数字时直接入输出队列 当输入结束后 运算符队列中所有操作符入输出队列详细的算法 编辑当还有记号可以读取时 读取一个记号 如果这个记号表示一个数字 那么将其添加到输出队列中 如果这个记号表示一个函数 那么将其压入栈当中 如果这个记号表示一个函数参数的分隔符 例如 一个半角逗号 从栈当中不断地弹出操作符并且放入输出队列中去 直到栈顶部的元素为一个左括号为止 如果一直没有遇到左括号 那么要么是分隔符放错了位置 要么是括号不匹配 如果这个记号表示一个操作符 记做o1 那么 只要存在另一个记为o2的操作符位于栈的顶端 并且如果o1是左结合性的并且它的运算符优先级要小于或者等于o2的优先级 或者 如果o1是右结合性的并且它的运算符优先级比o2的要低 那么 dd 将o2从栈的顶端弹出并且放入输出队列中 循环直至以上条件不满足为止 dd 然后 将o1压入栈的顶端 dd 如果这个记号是一个左括号 那么就将其压入栈当中 如果这个记号是一个右括号 那么 从栈当中不断地弹出操作符并且放入输出队列中 直到栈顶部的元素为左括号为止 将左括号从栈的顶端弹出 但并不放入输出队列中去 如果此时位于栈顶端的记号表示一个函数 那么将其弹出并放入输出队列中去 如果在找到一个左括号之前栈就已经弹出了所有元素 那么就表示在表达式中存在不匹配的括号 dd 当再没有记号可以读取时 如果此时在栈当中还有操作符 如果此时位于栈顶端的操作符是一个括号 那么就表示在表达式中存在不匹配的括号 将操作符逐个弹出并放入输出队列中 dd 退出算法 更详细的例子 编辑中綴表示法 及 結果 3 4 2 1 5 2 3 3 0001220703125 displaystyle 3 4 times 2 div left 1 5 right 2 3 3 0001220703125 逆波兰表示法 3 4 2 1 5 2 3 输入 3 4 2 1 5 2 3 输入 动作 输出 逆波兰表示法 运算符堆栈 提示3 将符号加入输出队列 3 将符号压入操作符堆栈 3 4 将符号加入输出队列 3 4 将符号压入操作符堆栈 3 4 号的优先级高于 号2 将符号加入输出队列 3 4 2 将堆栈中元素弹出 加入输出队列 3 4 2 号和 号优先级相同将符号压入操作符堆栈 3 4 2 号的优先级高于 号 将符号压入操作符堆栈 3 4 2 1 将符号加入输出队列 3 4 2 1 将符号压入操作符堆栈 3 4 2 1 5 将符号加入输出队列 3 4 2 1 5 将堆栈中元素弹出 加入输出队列 3 4 2 1 5 循环直到找到 号将堆栈元素弹出 3 4 2 1 5 括号匹配结束 将符号压入操作符堆栈 3 4 2 1 5 号的优先级高于 号2 将符号加入输出队列 3 4 2 1 5 2 将符号压入操作符堆栈 3 4 2 1 5 2 号为从右至左求值3 将符号加入输出队列 3 4 2 1 5 2 3 END 将栈中所有数据加入输出队列 3 4 2 1 5 2 3 C 程序实现 编辑 include lt cstring gt include lt cstdio gt 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 gt A amp amp c lt Z define is ident c c gt 0 amp amp c lt 9 c gt a amp amp c lt z 操作符 优先级 符号 运算顺序 1 从右至左 2 从左至右 3 从左至右 4 从右至左 int op preced const char c switch c case return 4 case case case return 3 case case return 2 case return 1 若输入不是运算符 return 0 unsigned int op arg count const char c switch c 运算符 case case case case case case return 2 阶乘 case return 1 不是运算符 default return c A return 0 bool shunting yard const char input char output const char strpos input strend input strlen input char c stack 32 sc outpos output unsigned int sl 0 while strpos lt strend c strpos if c 扫描到左括号直接入栈 if c stack sl c sl 如果输入为数字 则直接入输出队列 else if is ident c outpos c outpos 如果输入为函数记号 则压入堆栈 else if is function c stack sl c sl 如果输入为函数分割符 如 逗号 else if c bool pe false while sl gt 0 sc stack sl 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 gt 0 sc stack sl 1 sc为其栈顶元素 如果c是左结合性的且它的优先级小于等于栈顶运算符sc的优先级 或者c是右结合性且它的优先级小于栈顶运算符sc的优先级 将栈顶元素sc出栈 否则sc进栈 if is operator sc amp amp op left assoc c amp amp op preced c lt op preced sc op left assoc c amp amp op preced c lt op preced sc outpos sc outpos sl else break c的优先级大于或大于等于结合性的要求 则将其入栈 stack sl c sl 扫描到右括号 else if c bool pe false 从栈顶向下扫描左括号 将扫描到左括号之前的栈顶运算符出栈并放入输出队列 while sl gt 0 sc stack sl 1 if sc pe true break else outpos sc outpos sl 如果没有扫描到左括号 则有可能是符号放错或者不匹配 if pe printf Error parentheses mismatched n return false 左括号出栈且不放入输出队列 sl 扫描完左括号后 如果栈顶元素是函数运算符 则将其出栈并放入输出队列 if sl gt 0 sc stack sl 1 if is function sc outpos sc outpos sl 未知运算符c else printf Unknown token c n c strpos 当所有元素已经读完 栈中还有剩余运算符 while sl gt 0 sc stack sl 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 res 4 unsigned int sl 0 sc stack 32 rn 0 While there are input tokens left while strpos lt strend Read the next token from input c strpos If the token is a value or identifier if is ident c Push it onto the stack stack sl c sl Otherwise the token is an operator operator here includes both operators and functions else if is operator c is function c sprintf res 02d rn printf s res rn It is known a priori that the operator takes n arguments unsigned int nargs op arg count c If there are fewer than n values on the stack if sl lt nargs return false Error The user has not input sufficient values in the expression Else Pop the top n values from the stack Evaluate the operator with the values as arguments if is function c printf c c while nargs gt 0 sc stack sl 1 sl if nargs gt 1 printf s amp sc else printf s n amp sc nargs else if nargs 1 sc stack sl 1 sl printf c s n c amp sc else sc stack sl 1 sl printf s c amp sc c sc stack sl 1 sl printf s n amp sc Push the returned results if any back onto the stack stack sl 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 stack sl 1 sl printf s is a result n amp 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 output 128 printf input s n input if shunting yard input output printf output s n output if execution order output printf n Invalid input n return 0 参见 编辑中缀表达式 后缀表达式 取自 https zh wikipedia org w index php title 调度场算法 amp oldid 67063185, 维基百科,wiki,书籍,书籍,图书馆,

文章

,阅读,下载,免费,免费下载,mp3,视频,mp4,3gp, jpg,jpeg,gif,png,图片,音乐,歌曲,电影,书籍,游戏,游戏。