mirror of
https://github.moeyy.xyz/https://github.com/TheAlgorithms/C.git
synced 2023-10-11 15:56:24 +08:00
feat: add Shunting Yard Algorithm (#1219)
* Create shunting_yard.c * updating DIRECTORY.md * Update shunting_yard.c * Update shunting_yard.c * Update shunting_yard.c * updating DIRECTORY.md * Update shunting_yard.c * updating DIRECTORY.md --------- Co-authored-by: github-actions[bot] <github-actions@users.noreply.github.com> Co-authored-by: David Leal <halfpacho@gmail.com>
This commit is contained in:
parent
6e94adf066
commit
59dc816c9d
@ -200,6 +200,7 @@
|
|||||||
* [142](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/142.c)
|
* [142](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/142.c)
|
||||||
* [1524](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1524.c)
|
* [1524](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1524.c)
|
||||||
* [153](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/153.c)
|
* [153](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/153.c)
|
||||||
|
* [16](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/16.c)
|
||||||
* [160](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/160.c)
|
* [160](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/160.c)
|
||||||
* [1653](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1653.c)
|
* [1653](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1653.c)
|
||||||
* [1657](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1657.c)
|
* [1657](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1657.c)
|
||||||
@ -266,10 +267,12 @@
|
|||||||
* [461](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/461.c)
|
* [461](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/461.c)
|
||||||
* [476](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/476.c)
|
* [476](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/476.c)
|
||||||
* [485](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/485.c)
|
* [485](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/485.c)
|
||||||
|
* [5](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/5.c)
|
||||||
* [50](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/50.c)
|
* [50](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/50.c)
|
||||||
* [509](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/509.c)
|
* [509](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/509.c)
|
||||||
* [520](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/520.c)
|
* [520](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/520.c)
|
||||||
* [53](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/53.c)
|
* [53](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/53.c)
|
||||||
|
* [540](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/540.c)
|
||||||
* [561](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/561.c)
|
* [561](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/561.c)
|
||||||
* [567](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/567.c)
|
* [567](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/567.c)
|
||||||
* [6](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/6.c)
|
* [6](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/6.c)
|
||||||
@ -354,6 +357,7 @@
|
|||||||
* [Rot13](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rot13.c)
|
* [Rot13](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rot13.c)
|
||||||
* [Rselect](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rselect.c)
|
* [Rselect](https://github.com/TheAlgorithms/C/blob/HEAD/misc/rselect.c)
|
||||||
* [Run Length Encoding](https://github.com/TheAlgorithms/C/blob/HEAD/misc/run_length_encoding.c)
|
* [Run Length Encoding](https://github.com/TheAlgorithms/C/blob/HEAD/misc/run_length_encoding.c)
|
||||||
|
* [Shunting Yard](https://github.com/TheAlgorithms/C/blob/HEAD/misc/shunting_yard.c)
|
||||||
* [Sudoku Solver](https://github.com/TheAlgorithms/C/blob/HEAD/misc/sudoku_solver.c)
|
* [Sudoku Solver](https://github.com/TheAlgorithms/C/blob/HEAD/misc/sudoku_solver.c)
|
||||||
* [Tower Of Hanoi](https://github.com/TheAlgorithms/C/blob/HEAD/misc/tower_of_hanoi.c)
|
* [Tower Of Hanoi](https://github.com/TheAlgorithms/C/blob/HEAD/misc/tower_of_hanoi.c)
|
||||||
* [Union Find](https://github.com/TheAlgorithms/C/blob/HEAD/misc/union_find.c)
|
* [Union Find](https://github.com/TheAlgorithms/C/blob/HEAD/misc/union_find.c)
|
||||||
|
238
misc/shunting_yard.c
Normal file
238
misc/shunting_yard.c
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief [Shunting Yard Algorithm](https://en.wikipedia.org/wiki/Shunting_yard_algorithm)
|
||||||
|
* @details From Wikipedia: In computer science,
|
||||||
|
* the shunting yard algorithm is a method for parsing arithmetical or logical expressions, or a combination of both, specified in infix notation.
|
||||||
|
* It can produce either a postfix notation string, also known as Reverse Polish notation (RPN), or an abstract syntax tree (AST).
|
||||||
|
* The algorithm was invented by Edsger Dijkstra and named the "shunting yard" algorithm because its operation resembles that of a railroad shunting yard.
|
||||||
|
* @author [CascadingCascade](https://github.com/CascadingCascade)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h> /// for assertion
|
||||||
|
#include <stdio.h> /// for IO operations
|
||||||
|
#include <stdlib.h> /// for memory management
|
||||||
|
#include <string.h> /// for string operations
|
||||||
|
#include <ctype.h> /// for isdigit()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function that returns each operator's precedence
|
||||||
|
* @param operator the operator to be queried
|
||||||
|
* @returns the operator's precedence
|
||||||
|
*/
|
||||||
|
int getPrecedence(char operator) {
|
||||||
|
switch (operator) {
|
||||||
|
case '+':
|
||||||
|
case '-': {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
case '*':
|
||||||
|
case '/': {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
case '^': {
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
default:{
|
||||||
|
fprintf(stderr,"Error: Invalid operator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper function that returns each operator's associativity
|
||||||
|
* @param operator the operator to be queried
|
||||||
|
* @returns '1' if the operator is left associative
|
||||||
|
* @returns '0' if the operator is right associative
|
||||||
|
*/
|
||||||
|
int getAssociativity(char operator) {
|
||||||
|
switch (operator) {
|
||||||
|
case '^': {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
case '+':
|
||||||
|
case '-':
|
||||||
|
case '*':
|
||||||
|
case '/': {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
fprintf(stderr,"Error: Invalid operator\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief An implementation of the shunting yard that converts infix notation to reversed polish notation
|
||||||
|
* @param input pointer to input string
|
||||||
|
* @param output pointer to output location
|
||||||
|
* @returns `1` if a parentheses mismatch is detected
|
||||||
|
* @returns `0` if no mismatches are detected
|
||||||
|
*/
|
||||||
|
int shuntingYard(const char *input, char *output) {
|
||||||
|
const unsigned int inputLength = strlen(input);
|
||||||
|
char* operatorStack = (char*) malloc(sizeof(char) * inputLength);
|
||||||
|
|
||||||
|
// This pointer points at where we should insert the next element,
|
||||||
|
// Hence stackPointer - 1 is used when accessing elements
|
||||||
|
unsigned int stackPointer = 0;
|
||||||
|
|
||||||
|
// We will parse the input with strtok(),
|
||||||
|
// Since strtok() is destructive, we make a copy of the input to preserve the original string
|
||||||
|
char* str = malloc(sizeof(char) * inputLength + 1);
|
||||||
|
strcpy(str,input);
|
||||||
|
char* token = strtok(str," ");
|
||||||
|
|
||||||
|
// We will push to output with strcat() and strncat(),
|
||||||
|
// This initializes output to be a string with a length of zero
|
||||||
|
output[0] = '\0';
|
||||||
|
|
||||||
|
while (token != NULL) {
|
||||||
|
// If it's a number, push it to the output directly
|
||||||
|
if (isdigit(token[0])) {
|
||||||
|
strcat(output,token);
|
||||||
|
strcat(output," ");
|
||||||
|
|
||||||
|
token = strtok(NULL," ");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (token[0]) {
|
||||||
|
// If it's a left parenthesis, push it to the operator stack for later matching
|
||||||
|
case '(': {
|
||||||
|
operatorStack[stackPointer++] = token[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's a right parenthesis, search for a left parenthesis to match it
|
||||||
|
case ')': {
|
||||||
|
// Guard statement against accessing an empty stack
|
||||||
|
if(stackPointer < 1) {
|
||||||
|
fprintf(stderr,"Error: Mismatched parentheses\n");
|
||||||
|
free(operatorStack);
|
||||||
|
free(str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (operatorStack[stackPointer - 1] != '(') {
|
||||||
|
// strncat() with a count of 1 is used to append characters to output
|
||||||
|
const unsigned int i = (stackPointer--) - 1;
|
||||||
|
strncat(output, &operatorStack[i], 1);
|
||||||
|
strcat(output," ");
|
||||||
|
|
||||||
|
// If the operator stack is exhausted before a match can be found,
|
||||||
|
// There must be a mismatch
|
||||||
|
if(stackPointer == 0) {
|
||||||
|
fprintf(stderr,"Error: Mismatched parentheses\n");
|
||||||
|
free(operatorStack);
|
||||||
|
free(str);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Discards the parentheses now the matching is complete,
|
||||||
|
// Simply remove the left parenthesis from the stack is enough,
|
||||||
|
// Since the right parenthesis didn't enter the stack in the first place
|
||||||
|
stackPointer--;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's an operator(o1), we compare it to whatever is at the top of the operator stack(o2)
|
||||||
|
default: {
|
||||||
|
// Places the operator into the stack directly if it's empty
|
||||||
|
if(stackPointer < 1) {
|
||||||
|
operatorStack[stackPointer++] = token[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to check if there's actually a valid operator at the top of the stack
|
||||||
|
if((stackPointer - 1 > 0) && operatorStack[stackPointer - 1] != '(') {
|
||||||
|
const int precedence1 = getPrecedence(token[0]);
|
||||||
|
const int precedence2 = getPrecedence(operatorStack[stackPointer - 1]);
|
||||||
|
const int associativity = getAssociativity(token[0]);
|
||||||
|
|
||||||
|
// We pop operators from the stack, if...
|
||||||
|
while ( // ... their precedences are equal, and o1 is left associative, ...
|
||||||
|
((associativity && precedence1 == precedence2) ||
|
||||||
|
// ... or o2 simply have a higher precedence, ...
|
||||||
|
precedence2 > precedence1) &&
|
||||||
|
// ... and there are still operators available to be popped.
|
||||||
|
((stackPointer - 1 > 0) && operatorStack[stackPointer - 1] != '(')) {
|
||||||
|
|
||||||
|
strncat(output,&operatorStack[(stackPointer--) - 1],1);
|
||||||
|
strcat(output," ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We'll save o1 for later
|
||||||
|
operatorStack[stackPointer++] = token[0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
token = strtok(NULL," ");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(str);
|
||||||
|
|
||||||
|
// Now all input has been exhausted,
|
||||||
|
// Pop everything from the operator stack, then push them to the output
|
||||||
|
while (stackPointer > 0) {
|
||||||
|
// If there are still leftover left parentheses in the stack,
|
||||||
|
// There must be a mismatch
|
||||||
|
if(operatorStack[stackPointer - 1] == '(') {
|
||||||
|
fprintf(stderr,"Error: Mismatched parentheses\n");
|
||||||
|
free(operatorStack);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int i = (stackPointer--) - 1;
|
||||||
|
strncat(output, &operatorStack[i], 1);
|
||||||
|
if (i != 0) {
|
||||||
|
strcat(output," ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
free(operatorStack);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Self-test implementations
|
||||||
|
* @returns void
|
||||||
|
*/
|
||||||
|
static void test() {
|
||||||
|
char* in = malloc(sizeof(char) * 50);
|
||||||
|
char* out = malloc(sizeof(char) * 50);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
strcpy(in,"3 + 4 * ( 2 - 1 )");
|
||||||
|
printf("Infix: %s\n",in);
|
||||||
|
i = shuntingYard(in, out);
|
||||||
|
printf("RPN: %s\n",out);
|
||||||
|
printf("Return code: %d\n\n",i);
|
||||||
|
assert(strcmp(out,"3 4 2 1 - * +") == 0);
|
||||||
|
assert(i == 0);
|
||||||
|
|
||||||
|
strcpy(in,"3 + 4 * 2 / ( 1 - 5 ) ^ 2 ^ 3");
|
||||||
|
printf("Infix: %s\n",in);
|
||||||
|
i = shuntingYard(in, out);
|
||||||
|
printf("RPN: %s\n",out);
|
||||||
|
printf("Return code: %d\n\n",i);
|
||||||
|
assert(strcmp(out,"3 4 2 * 1 5 - 2 3 ^ ^ / +") == 0);
|
||||||
|
assert(i == 0);
|
||||||
|
|
||||||
|
printf("Testing successfully completed!\n");
|
||||||
|
free(in);
|
||||||
|
free(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Main function
|
||||||
|
* @returns 0 on exit
|
||||||
|
*/
|
||||||
|
int main() {
|
||||||
|
test(); // Run self-test implementations
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user