在理解如何用C语言处理前中后缀表达式之前,我们需要先了解以下几点内容。
一,前中后缀表达式的理解
日常生活中我们基本使用中缀表达式。我们把形如3+2×4÷(5-3), 2×(3+5)+7÷1-4,8+4-6^2式子称为中缀表达式。
前缀表达式又称作波兰式。我们把形如+12(中缀表达式为1+2)的式称作前缀表达式,因此在上文中缀表达中提到的三个式子的波兰式分别对应:+3÷×24-53,-+×2+35÷714,+8*4^62。
后缀表达式又称作逆波兰式。我们把形如12+(中缀表达式为1+2)的式子称作后缀表达式,因此在上文中缀表达式中提到的三个式子的逆波兰式分别对应:32453-/×+,235+×71/4-+,8462^-+。
二,中缀表达式转前缀表达式
前缀表达式与后缀表达式不仅在名字上成类似镜面对称,在中缀表达式转成后缀表达式与前缀表达式上也成类似镜面对称。中缀转后缀表达式的算法稍后写出,我们首先探讨中缀表达式转化成前缀表达式:1,将中缀表达式做对称,例如:3+2×4÷(5-3) ——> )3-5(÷4×2+3。 2,左右括号各自做对称,即:)3-5(÷4×2+3 ——> (3-5)÷4×2+3。3,使用中缀转后缀表达式算法,即:(3-5)÷4×2+3 ——> 35-42×÷3+。4,反转生成的后缀表达式后的结果即为我们所需的前缀表达式。即:35-42×÷3+ ——> +3÷×24-53。
三,中缀表达式转后缀表达式
此过程分为以下几个具体步骤:1,在中缀表达式字符串末尾添加右括号 )。2,创建一个接收符号的栈,此时预先在栈中放入左括号 ( 与字符串末尾的右括号相对应。3,规定符号优先级:加减法与括号级别为3,乘除号的级别为2,次方符号级别为1。注意,这里的优先级与数学中的优先级正好相反! 4,从左到右扫描字符串:(1)遇到左括号直接放入栈中。(2)若为数字则直接将数字放入另外一个字符串(假设名为new)。(3)若为运算符,逐一从栈中弹出符号放入名为new的字符串中直到遇到优先级大于或等于当前被扫描的运算符。之后,将当前被扫描的运算符放入栈中。(5)若遇到右括号,此时需要把左右括号之间的所有运算符弹出。即依次弹出栈中的运算符放入名为new的字符串中直到遇到左括号。然后丢弃左括号。
计算后缀表达式
此过程分为以下几个具体步骤:1,从左至右扫描后缀表达式字符串:(1),若当前字符为数字,则直接放入栈中。(2),若当前字符为运算符,则将栈顶的两个数字拿出来与做相关运算,然后将结果放入栈中。
计算前缀表达式
计算前缀表达式与计算后缀表达式基本相同。具体分为以下几个步骤:1,将前缀表达式翻转。(2)使用计算后缀表达式算法,但此时需要注意:在计算后缀表达式时,若后缀表达式为ab+,则运算结果为a+b;而前缀经过翻转也成为一个后缀表达式,例如ab+,但是实际运算是b+a!
创建栈
不管是计算表达式的过程中还是转换表达式的过程中,我们都需要用到栈。这个栈实现可以由我们自己来做,因为Linux系统自带通用链表,我们可以直接使用 list.h 头文件来帮助我们创建栈。
以下为list.h的部分内容(也是所需内容):
/*
* Simple doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
struct list_head {
struct list_head *next, *prev;
};
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
#ifndef CONFIG_DEBUG_LIST
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
#else
extern void list_add(struct list_head *new, struct list_head *head);
#endif
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
//#define list_entry(ptr, type, member) \
//container_of(ptr, type, member)
#define list_entry(ptr, type, member)\
((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); \
pos = pos->next)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
以下为处理前中后缀表达式的代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include "list.h"
#define postfix_calculate(rec, pre, ch) calculate(rec, pre, ch)
#define prefix_calculate(rec, pre, ch) calculate(pre, rec, ch)
typedef struct operator Operator;
/*list_add : 入栈
list_del : 出栈
list_for_each_safe : 遍历栈
list_entry : 提取
*/
struct operator
{
char ch;
float num;
struct list_head list;
};
char* input(char* s);
char* change(struct list_head* head,char* q);
int judge(Operator* rec, char ch);
void reverse(char* q);
float evaluate(char* equation, char* s);
float calculate(float rec, float pre, char ch);
float evaluate(char* equation, char* s)
{
Operator* current = NULL;
Operator* rec = NULL;
Operator* pre = NULL;
struct list_head* pos = NULL;
struct list_head* n = NULL;
LIST_HEAD(head);
while(*equation)
{
if(*equation>='0' && *equation <='9')
{
current = malloc(1*sizeof(Operator));
current->num = *equation - 48;
list_add(&(current->list),&head);
}
else
{
list_for_each_safe(pos,n,&head)
{
rec = list_entry(pos,Operator,list);
if(pre != NULL)
{
if(strcmp(s, "prefix") == 0)
rec->num = prefix_calculate(rec->num, pre->num, *equation);
else
rec->num = postfix_calculate(rec->num, pre->num, *equation);
list_del(&(pre->list));
free(pre);
rec = NULL;
pre = NULL;
break;
}
pre = rec;
}
}
++equation;
}
return list_entry(head.next, Operator, list)->num;
}
float calculate(float rec, float pre, char ch)
{
float sum = 0;
switch(ch)
{
case '+':
sum = rec + pre;
break;
case '-':
sum = rec - pre;
break;
case '*':
sum = rec * pre;
break;
case '/':
if(pre == 0)
{
printf("Divisor cannot be zero!");
exit(0);
}
sum = rec / pre;
break;
case '^':
sum = (float)pow((double)rec, (double)pre);
break;
}
return sum;
}
char* input(char* s)
{
char ch = 0;
char* q = NULL;
char* origin = NULL;
int count = 0;
printf("enter an infix expression:\n");
while(1)
{
ch = getchar();
if(ch==10)
{
q = realloc(q,(count + 1)*sizeof(char));
q[count] = 0;
++count;
break;
}
q = realloc(q,(count + 1)* sizeof(char));
q[count] = ch;
++count;
}
origin = q;
if(strcmp(s,"prefix") == 0)
{
while(*q)
{
if(*q == '(')
*q = ')';
else if (*q == ')')
*q = '(';
++q;
}
q = origin;
reverse(q);
}
q[count - 1] = ')';
q = realloc(q,(count + 1)*sizeof(char));
q[count] = 0;
return q;
}
char* change(struct list_head* head, char* q)
{
int count = 0;
char* postfix = NULL;
struct list_head* pos = NULL;
struct list_head* n = NULL;
Operator* current = NULL;
Operator* rec = NULL;
current = (Operator*)malloc(1*sizeof(Operator));
current->ch = '(';
list_add(&(current->list), head);
current = NULL;
while(*q)
{
if(*q >='0' && *q <= '9')
{
postfix = realloc(postfix,(count+1)*sizeof(char));
postfix[count] = *q;
++count;
}
else if(*q == '(')
{
current = malloc(1*sizeof(Operator));
current->ch = *q;
list_add(&(current->list), head);
current = NULL;
}
else if(*q == ')')
{
list_for_each_safe(pos, n, head)
{
rec = list_entry(pos,Operator,list);
if(rec->ch == '(')
{
list_del(&(rec->list));
free(rec);
break;
}
else
{
postfix = realloc(postfix,(count +1)*sizeof(char));
postfix[count] = rec->ch;
list_del(&(rec->list));
free(rec);
++count;
}
}
}
else
{
list_for_each_safe(pos, n, head)
{
rec = list_entry(pos, Operator, list);
if(judge(rec, *q))
break;
else
{
postfix = realloc(postfix,(count +1)*sizeof(char));
postfix[count] = rec->ch;
list_del(&(rec->list));
free(rec);
++count;
}
}
current = malloc(1*sizeof(Operator));
current->ch = *q;
list_add(&(current->list), head);
current = NULL;
}
++q;
}
postfix = realloc(postfix,(count+1)*sizeof(char));
postfix[count] = 0;
return postfix;
}
int judge(Operator* rec, char ch)
{
char* operators[3] = { "^",
"*/%",
"+-()",
};
int rec_row = 0;
int ch_row = 0;
int i = 0;
int j = 0;
for(i = 0; i<3; ++i)
for(j = 0; operators[i][j]!=0; ++j)
if(operators[i][j] == rec->ch)
rec_row = i;
else if(operators[i][j] == ch)
ch_row = i;
if(ch_row <= rec_row)
return 1;
else
return 0;
}
void reverse(char* q)
{
int i = 0;
int j = strlen(q) - 1;
char temp = 0;
for(;i < j;++i,--j)
{
temp = q[i];
q[i] = q[j];
q[j] = temp;
}
return;
}
int main(void)
{
char* postfix = NULL;
char* infix = NULL;
float sum = 0;
/*初始化头结点*/
LIST_HEAD(head);
/*中缀转前缀,并计算前缀*/
infix = input("prefix");
postfix = change(&head, infix);
reverse(postfix);//前缀表达式生成
reverse(postfix);
sum = evaluate(postfix, "prefix");
printf("%.2f",sum);
/*中缀转后缀,并计算后缀*/
infix = input("postfix");
postfix = change(&head, infix);
sum = evaluate(postfix, "postfix");
printf("%.2f",sum);
return 0;
}
以上内容是作者对 Pal, Debdutta_ Halder, Suman - Data structures and algorithms with C (2018, Alpha Science International)此书中第六章的理解。若有误,请告知作者,感激不尽!