mirror of
https://github.com/TheAlgorithms/C
synced 2024-11-24 14:29:42 +03: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)
|
||||
* [1524](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1524.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)
|
||||
* [1653](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/1653.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)
|
||||
* [476](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/476.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)
|
||||
* [509](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/509.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)
|
||||
* [540](https://github.com/TheAlgorithms/C/blob/HEAD/leetcode/src/540.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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [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)
|
||||
* [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)
|
||||
|
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