C语言处理前,中,后缀算术表达式(附详细讲解)

在理解如何用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)此书中第六章的理解。若有误,请告知作者,感激不尽!

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值