算法實戰5:多種數據結構實現四則運算

問題還是挺多:

(1)例如輸入非運算符沒有報錯

(2)對於“(2)”這種無法運算

(3)暫時沒有加入小數,基於整型運算


-----------------------------------------------------------------

(一)用棧實現四則運算

其實每次看到棧的章節的時候都會拿四則運算做例子,這個確實是個很具體的例子,其實遞歸也是一種棧的實現,不過是編譯器幫我們做好了。

實現這個運算難度也不是很大,大家先別急着就要考慮(2+3*3)/(2^(2+1)-3) 這種蛋疼無比的計算,可先從簡單的同類運算開始:

例如:2+3+4

我們計算的時候是這麼進行

2先壓入棧,然後壓入+,再次壓入3

這時候壓入+號,我們知道這次的+號和上次+號是同個運算優先級,所以我們遵從左-》右的計算順序,

彈出3、+、2 求出Calc(2,'+',3)  返回結果,壓入棧5,以及這次+號,然後壓入4 ,運行到結尾,再次出棧4,+,5

計算Calc(5,'+',4);返回9計算完畢


再如:2+3*4+3

同理壓入2,+,3,

到達第二個符號的時候,是*號,優先級高於+號,所以繼續壓入,不彈出。

壓入*,4

這時候用遇到+號這種運算符好,我們知道*號的優先級高,所以先計算3*4

然後壓入結果棧成爲:2+12

這時候判斷之前的加號優先級和現在一樣,繼續出棧計算得棧:4

前面無符號來,加入這時候的+號和3 然後再彈出。。


其實只要遵循這種思路就能把四則運算模擬出來了,當然有人利用兩個棧,一個盛放數字,一個是符號

個人比較喜歡直接一個棧

下面我貼出三個版本,就是按照這種由簡單-->難,不斷完善

版本一看了就好,後面就細節更改,無所謂,而且比較亂。。


//============================================================================
// Name        : CalcSimu.cpp
// Author      : YLF
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
#include <vector>
#include <stdio.h>

using namespace std;

int CalcSimu(char *str);
int Calc(char num1,char opt,char num2);
int Level(char c);
int CalcSimu2(char *str);
int Calc2(int num1,int opt,int num2);
int Level2(char c);
int CalcSimu3(char *str);
int Calc3(int num1,int opt,int num2);
int Level3(char c);

int main() {

	char a[100];
	gets(a);
	cout<<CalcSimu3(a);
	return 0;
}

/**
 * 版本一:
 * 簡單加算:範圍不能超過128 否則變成負數
 * 而且只針對個位數相運算
 */
int CalcSimu(char *str){
	int numCount=0,optCount=0;
	vector<char> stack;
	int curLevel=0,preLevel=0;

	while(*str != '\0'){
		if(*str>'0' && *str<'9'){
			numCount++;
			stack.push_back(*str);
		}else{
			if(optCount == 0){//如果是之前沒有運算符
				optCount++;
				stack.push_back(*str);
				//獲得運算符級別
				preLevel = Level(*str);
			}else{//之前已經有運算符了
				curLevel = Level(*str);
				if(preLevel>=curLevel){//之前的運算符優先級更高,直接計算
					do{
						char num2 = stack.at(stack.size()-1);
						stack.pop_back();
						char opt = stack.at(stack.size()-1);
						stack.pop_back();
						char num1 = stack.at(stack.size()-1);
						stack.pop_back();
						int result = Calc(num1,opt,num2);
						int temp = stack.size();
						if(stack.size()>1)//如果前一個運算符的優先級也還高,繼續彈出
							preLevel = Level(stack.at(stack.size()-1));
						stack.push_back(result);
					}while(stack.size()>1 && preLevel>=curLevel);
					preLevel = curLevel;

				}else{//當前運算符更高級別,繼續壓入
					preLevel = curLevel;
				}
				stack.push_back(*str);//把當前運算符壓入
			}
		}
		str++;
	}
	while(stack.size()>1){
		char num2 = stack.at(stack.size()-1);
		stack.pop_back();
		char opt = stack.at(stack.size()-1);
		stack.pop_back();
		char num1 = stack.at(stack.size()-1);
		stack.pop_back();
		int result = Calc(num1,opt,num2);
		stack.push_back(result);
	}
	return stack.at(0);

}

int Calc(char num1,char opt,char num2){
	int a = num1-'0';
	int b = num2-'0';
	int i =0;
	int re = 1;
	switch(opt){
	case '+':
		return a+b+'0';
	case '-':
		return a-b+'0';
	case '*':
		return a*b+'0';
	case '/':
		return a/b+'0';
	case '%':
		return a%b+'0';
	case '^':
		for(i=0;i<b;i++)
			re *=a;
		return re+'0';
	default:
		return 0+'0';
	}
}

int Level(char c){
	switch(c){
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
	case '%':
		return 2;
	case '^':
		return 3;
	case '(':
	case ')':
		return 4;
	default:
		return 0;
	}
}


//********************************************
/**
* 改進:
* 2:多位數加入
* 3:求餘問題
*
*/
int CalcSimu2(char *str){
	int num=0,optCount=0;
	vector<int> stack;
	int curLevel=0,preLevel=0;

	while(*str != '\0'){
		if(*str>='0' && *str<='9'){
			do{
				num=num*10+(*str-'0');
				str++;
			}
			while(*str>='0' && *str<='9');
			stack.push_back(num);//直接壓入整數;
			num=0;
			str--;
		}else{
			if(*str == '('){
				optCount=0;
				str++;
				continue;
			}
			if(optCount == 0){//如果是之前沒有運算符
				optCount++;
				stack.push_back(*str);
				//獲得運算符級別
				preLevel = Level2(*str);
			}else{//之前已經有運算符了
				curLevel = Level2(*str);
				if(preLevel>=curLevel){//之前的運算符優先級更高,直接計算
					do{
						int num2 = stack.at(stack.size()-1);
						stack.pop_back();
						int opt = stack.at(stack.size()-1);
						stack.pop_back();
						int num1 = stack.at(stack.size()-1);
						stack.pop_back();
						int result = Calc2(num1,opt,num2);
						int temp = stack.size();
						if(stack.size()>1){//如果前一個運算符的優先級也還高,繼續彈出
							preLevel = Level2(stack.at(stack.size()-1));
						}
						stack.push_back(result);
					}while(stack.size()>1 && preLevel>=curLevel);
					preLevel = curLevel;

				}else{//當前運算符更高級別,繼續壓入
					preLevel = curLevel;
				}
				stack.push_back(*str);//把當前運算符壓入
			}
		}
		str++;
	}
	while(stack.size()>1){
		int num2 = stack.at(stack.size()-1);
		stack.pop_back();
		int opt = stack.at(stack.size()-1);
		stack.pop_back();
		int num1 = stack.at(stack.size()-1);
		stack.pop_back();
		int result = Calc2(num1,opt,num2);
		stack.push_back(result);
	}
	return stack.at(0);

}

int Calc2(int num1,int opt,int num2){
	int a = num1;
	int b = num2;
	int i =0;
	int re = 1;
	switch(opt){
	case '+':
		return a+b;
	case '-':
		return a-b;
	case '*':
		return a*b;
	case '/':
		return a/b;
	case '%':
		return a%b;
	case '^':
		for(i=0;i<b;i++)
			re *=a;
		return re;
	default:
		return 0;
	}
}

int Level2(char c){
	switch(c){
	case ')':
		return 0;
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
		return 2;
	case '%':
		return 3;
	case '^':
		return 4;
	case '(':
		return 5;
	default:
		return -1;
	}
}

//********************************************
/**
* 改進:
* 1:括號問題
* 2:float類型
*/
int CalcSimu3(char *str){
	int num=0,optCount=0;
	vector<int> stack;
	int curLevel=0,preLevel=0;

	while(*str != '\0'){
		if(*str>='0' && *str<='9'){
			do{
				num=num*10+(*str-'0');
				str++;
			}
			while(*str>='0' && *str<='9');
			stack.push_back(num);//直接壓入整數;
			num=0;
			str--;
		}else{
			if(*str == '('){//如果出現左括號,則當成新的一次計算,因爲它的優先級最高了
				optCount=0;
				stack.push_back(*str);
				str++;
				continue;
			}
			if(optCount == 0){//如果是之前沒有運算符
				optCount++;
				stack.push_back(*str);
				//獲得運算符級別
				preLevel = Level3(*str);
			}else{//之前已經有運算符了
				curLevel = Level3(*str);
				if(preLevel>=curLevel){//之前的運算符優先級更高,直接計算
					do{
						int num1,num2,result,opt;

						if(stack.size()>=3){
							num2 = stack.at(stack.size()-1);
							stack.pop_back();
							opt = stack.at(stack.size()-1);
							stack.pop_back();
							num1 = stack.at(stack.size()-1);
							stack.pop_back();
							result = Calc3(num1,opt,num2);
						}
						if(stack.size()>=1){//如果前一個運算符的優先級也還高,繼續彈出
							opt = stack.at(stack.size()-1);//取出前一個運算符
							if(opt == '(' && *str == ')'){
								stack.pop_back();//彈出這個左括號
								str++;
								if(*str != '\0')
									curLevel=Level3(*str);
								else{
									stack.push_back(result);
									break;
								}
								if(stack.size()!=0)//如果這時候退到起始點了
									opt = stack.at(stack.size()-1);
							}
							else if(opt == '('){
								optCount=1;
								stack.push_back(result);//左括號不用彈出,因爲還沒有遇見右括號
								break;
							}

							if(stack.size()!=0)
								preLevel = Level3(opt);
							else
								optCount=0;
						}
						stack.push_back(result);
					}while(stack.size()>1 && preLevel>=curLevel);
					if(*str == '\0')
						break;
					preLevel = curLevel;

				}else{//當前運算符更高級別,繼續壓入
					preLevel = curLevel;
				}
				stack.push_back(*str);//把當前運算符壓入
			}
		}
		str++;
	}
	while(stack.size()>1){
		int num2 = stack.at(stack.size()-1);
		stack.pop_back();
		int opt = stack.at(stack.size()-1);
		stack.pop_back();
		int num1 = stack.at(stack.size()-1);
		stack.pop_back();
		int result = Calc3(num1,opt,num2);
		stack.push_back(result);
	}
	return stack.at(0);

}

int Calc3(int num1,int opt,int num2){
	int a = num1;
	int b = num2;
	int i =0;
	int re = 1;
	switch(opt){
	case '+':
		return a+b;
	case '-':
		return a-b;
	case '*':
		return a*b;
	case '/':
		return a/b;
	case '%':
		return a%b;
	case '^':
		for(i=0;i<b;i++)
			re *=a;
		return re;
	default:
		return 0;
	}
}

int Level3(char c){
	switch(c){
	case ')':
		return 0;
	case '+':
	case '-':
		return 1;
	case '*':
	case '/':
		return 2;
	case '%':
		return 3;
	case '^':
		return 4;
	case '(':
		return 5;
	default:
		return -1;
	}
}

(2+3*3)/(2^(2+1)-3)
2

(2+4*3)/(2^2-1)
4

(2+3)+2


(二)用二叉樹實現四則運算

二叉樹實現的結構似乎看起來更清晰:

只要遵循優先級高的在下層,低的在上層即可,每次更新root

見Calc類的實現 主要是Add函數

void Add(int value){
		Node* p = new Node();
		p->value = value;
		p->p = NULL;
		p->left = NULL;
		p->right = NULL;

		//讀入數字
		if(isNum(value)){
			if(root == NULL)
				root = p;
			else{
				root->right = p;
				p->p = root;
			}
		}else{//讀入的是運算符號
			while(root->p!=NULL && Level(root->value)>=Level(value))
					root = root->p;
			if(Level(root->value)>=Level(value)){
				p->left = root;
				root->p = p;
				root = p;
			}else{//高優先級放在數的下方,即越底層,運算優先級越大
				p->left = root->right;
				root->right->p = p;
				root->right = p;
				p->p = root;
				root = p;
			}
		}
	}

我這裏如果是數字,則正常加入,如果是符號,先根據優先級找到插入位置,然後插入!


//============================================================================
// Name        : CalcBTree.cpp
// Author      : YLF
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================

#include <iostream>
using namespace std;

/*
 * 四則運算,數據結構採用二叉樹
 */

struct Node{
	int value;
	Node* p;
	Node* left;
	Node* right;
};
class Calc{
private:
	Node* root;
private:
	void InVisit(Node* p){
		if(p==NULL)
			return;
		InVisit(p->left);
		Visit(p->value);
		InVisit(p->right);
	}
	void Visit(int value){
		if(value>='0' && value<='9')
			cout<<value-'0';
		else{
			cout<<(char)value;
		}
	}

	bool isNum(int value){
		if(value>='0' && value<='9')
			return true;
		return false;
	}
	bool isOpt(int value){
		return false;
	}

	int Cal(int a, int opt, int b){
		a = a-'0';
		b=b-'0';
		int i = 0;
		int re = 1;
		switch(opt){
		case '+':re = a+b;break;
		case '-':re = a-b;break;
		case '*':re = a*b;break;
		case '/':re = a/b;break;
		case '^':
			for(i=0;i<b;i++)
				re *= a;
			break;
		case '%':re =a%b;break;
		default:
			cerr<<"opt error!";
			return -1;
		}
		return re+'0';
	}
	void getResult(Node* p){
		if(isNum(p->left->value) && isNum(p->right->value)){
			int value = Cal(p->left->value,p->value,p->right->value);
			p->value = value;
			delete p->left;
			delete p->right;
			p->left = NULL;
			p->right = NULL;
			return;
		}
		if(!isNum(p->left->value))
			getResult(p->left);
		if(!isNum(p->right->value))
			getResult(p->right);
		if(isNum(p->left->value) && isNum(p->right->value)){
			int value = Cal(p->left->value,p->value,p->right->value);
			p->value = value;
			delete p->left;
			delete p->right;
			p->left = NULL;
			p->right = NULL;
			return;
		}
	}
	int Level(int c){
		switch(c){
		case '+':
		case '-':
			return 1;
		case '*':
		case '/':
			return 2;
		case '^':
			return 3;
		default:
			return 100;
		}
	}
public:
	Calc(){
		root = NULL;
	}
	~Calc(){}

	void Add(int value){
		Node* p = new Node();
		p->value = value;
		p->p = NULL;
		p->left = NULL;
		p->right = NULL;

		//讀入數字
		if(isNum(value)){
			if(root == NULL)
				root = p;
			else{
				root->right = p;
				p->p = root;
			}
		}else{//讀入的是運算符號
			while(root->p!=NULL && Level(root->value)>=Level(value))
					root = root->p;
			if(Level(root->value)>=Level(value)){
				p->left = root;
				root->p = p;
				root = p;
			}else{//高優先級放在數的下方,即越底層,運算優先級越大
				p->left = root->right;
				root->right->p = p;
				root->right = p;
				p->p = root;
				root = p;
			}
		}
	}

	int getResult(){
		getResult(root);
		return root->value-'0';
	}
	void Print(){
		while(root->p != NULL)
			root = root->p;
		InVisit(root);
	}
};

int main() {
	Calc c;
	char in=0;
//	c.Add('3');
//	c.Add('+');
//	c.Add('3');
	while(true){
		cin>>in;
		if(in != '='){
			c.Add(in);
		}else
			break;
	}
	c.Print();
	cout<<"="<<c.getResult();
	return 0;
}


這裏我侷限在個位數運算,不能超過十位數。。。。因爲isNum()只判斷來0-9

8/2+1-3+2^2=
8/2+1-3+2^2=6

就是給個思路罷了。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章