lang:計算器

前言

目前這個計算器還存在的問題:數字只能是單個整數、只能進行加減乘除、取餘、乘方的操作!
計算器是什麼?輸入2+3X1,會得到5的結果。這也是在做字符串識別,同樣涉及編譯原理的知識。先算2+3,還是先算3X1,這需要判斷優先級!


自定義棧

自定義的棧要求就是能放入和彈出自定義對象!用STL的容器也行!關鍵在於知道什麼時候該釋放內存,在哪釋放內存,釋放哪裏的內存!

下面這個自定義棧使用的企業鏈表的方式構建的,每一個節點只有一個next指針,所以在站內部是訪問不到對象的其他元素的!!!
企業鏈表就像掛衣服一樣!自定義一個對象,其中包含一個LinkNode節點,在傳遞進來時,把整個對象類型轉換成LinkNode*,拿出來時再轉換成自定義的數據對象就行!在這個過程中,實例對象除了LinkNode的next節點,其他數據都被隱藏起來了。

//LinkStack.h
#pragma once


#include <iostream>
#include <string>
#include <cmath>

//用鏈表來實現棧

//節點
typedef struct LINKNODE {
	struct LINKNODE* next;
}LinkNode;
//棧
typedef struct LINKSTACK {
	LinkNode head;//頭節點
	int size;
}LinkStack;



//初始化
LinkStack* Init_LinkStack();
//入棧
void Push_LinkStack(LinkStack* stack, LinkNode* data);
//返回棧頂元素
LinkNode* Top_LinkStack(LinkStack* stack);
//出棧
void Pop_LinkStack(LinkStack* stack);
//返回棧元素個數
int Size_LinkStack(LinkStack* stack);
//是否爲空
bool IsEmpty(LinkStack* stack);
//清空棧
void Clear_LinkStack(LinkStack* stack);
//銷燬棧
void FreeSpace_LinkStack(LinkStack* stack);



//LinkStack.cpp
#include "LinkStack.h"




//初始化
LinkStack* Init_LinkStack() {

	//LinkStack* stack = (LinkStack*)malloc(sizeof(LinkStack));
	LinkStack* stack = new LinkStack;
	stack->head.next = nullptr;//沒有後續節點
	stack->size = 0;//大小爲0
	return stack;
}
//入棧
void Push_LinkStack(LinkStack* stack, LinkNode* data) {

	if (stack == nullptr||data==nullptr) {
		return;
	}
	//把data節點接到頭節點後面
	data->next = stack->head.next;
	stack->head.next = data;
	stack->size++;

}
//返回棧頂元素
LinkNode* Top_LinkStack(LinkStack* stack) {
	if (stack == nullptr || stack->size == 0) {
		return nullptr;
	}
	return stack->head.next;//第一個有效節點
}
//出棧
void Pop_LinkStack(LinkStack* stack) {
	if (stack == nullptr||stack->size==0) {
		return;
	}
	//第一個有效節點
	LinkNode* pNext = stack->head.next;
	stack->head.next = pNext->next;
	//刪除該節點,數據內存在鏈表外面,所以內部操作不能刪除內存
	/*delete pNext;
	pNext = nullptr;*/
	stack->size--;


}
//返回棧元素個數
int Size_LinkStack(LinkStack* stack) {
	if (stack == nullptr) {
		return -1;
	}
	return stack->size;
}
//是否爲空
bool IsEmpty(LinkStack* stack) {
	if (stack == nullptr||stack->size==0) {
		return true;
	}
	return false;
}
//清空棧
void Clear_LinkStack(LinkStack* stack) {
	if (stack == nullptr) {
		return;
	}
	//先拿到第一個有效節點
	stack->head.next = nullptr;
	stack->size = 0;

}
//銷燬棧
void FreeSpace_LinkStack(LinkStack* stack) {

	if (stack == nullptr) {
		return;
	}
	//能回收的內存只能是stack這一個頭節點
	//free(stack);
	stack->head.next = nullptr;
	stack->size = 0;
	delete stack;
	stack = nullptr;

}



自定義數據結構

typedef struct MYCHAR {
	LinkNode node;
	char* p;
}MyChar;

中綴表達式轉後綴表達式

中綴表達式就是我們寫的計算式子,這種式子計算機沒法認,所以需要轉換成後綴表達式。像這樣,5+4 => 5 4 +
1+2*3 => 1 2 3 * +
8+(3-1)*5 => 8 3 1 - 5 * +
如何得到後綴表達式?
遍歷中綴表達式中的數字和符號
1,對於數字,直接輸出
2,對於符號,
        \;\;\;\; (1)如果是左括號,左括號進棧
        \;\;\;\; (2)如果是運算符,運算符號與棧頂符號進行優先級比較
  \;   \;             \;\;\;\;\;\; 1> 如果棧頂符號優先級低,此符號進棧
  \;   \;  \;            \;\;\;\;\;\; 2> 如果棧頂符號優先級不低,將棧頂符號彈出並輸出,直到棧裏沒有元素或者棧頂符號                                   \;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;\;優先級低於此符號之後,此符號進棧
        \;\;\;\; (3)如果是右括號,將棧頂符號彈出並輸出,直到匹配到左括號
遍歷結束後將棧中所有符號彈出並輸出

^表示乘方操作、%表示MOD取餘操作,因爲這兩個和加減乘除一樣都是二元操作符,所以直接設定一下優先級,直接加入與加減乘除沒有區別。

//main.cpp

//創建MyChar
MyChar* CreateMyChar(LinkStack* stack, char* p) {
	MyChar* mychar = new MyChar;
	mychar->p = p;
	return mychar;
}

//判斷是否是數字
bool IsNumber(char* p) {
	return *p >= '0'&&*p <= '9';
}
//數字操作
void NumberOperate(char* p) {
	std::cout << *p;
}
//判斷是否是左括號
bool IsLeft(char* p) {
	return *p == '(';
}
//左括號操作
void LeftOperate(LinkStack* stack, char* p) {
	Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
}

//判斷是否是右括號
bool IsRight(char *p) {
	return *p == ')';
}

//右括號操作
void RightOperate(LinkStack* stack, char* p) {
	while (Size_LinkStack(stack) > 0) {
		MyChar* mychar = (MyChar*)Top_LinkStack(stack);
		//如果匹配到左括號
		if (IsLeft(mychar->p)) {
			Pop_LinkStack(stack);
			break;
		}
		//輸出並彈出
		std::cout << *(mychar->p);
		Pop_LinkStack(stack);
		//釋放內存
		delete mychar;
	}
}

//判斷運算符
bool IsOperator(char* p) {
	return *p == '+' || *p == '-' || *p == '*' || *p == '/'||*p=='%'||*p=='^';
}

//返回運算符優先級
int GetPriority(char* p) {
	if (*p == '^') {
		return 1;
	}
	else if (*p == '*' || *p == '/'||*p=='%') {
		return 2;
	}
	else if (*p == '+' || *p == '-') {
		return 3;
	}
	else if (*p=='('||*p==')') {
		return 4;
	}
	return 5;
}

//操作符操作
void OpOperate(LinkStack* stack, char* p) {

	if (stack == nullptr || p == nullptr) {
		return;
	}

	//取出棧頂符號
	MyChar* mychar = (MyChar*)Top_LinkStack(stack);
	//如果棧裏沒有符號,直接入棧
	if (mychar == nullptr) {
		Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
		return;
	}

	while (Size_LinkStack(stack) > 0) {

		mychar = (MyChar*)Top_LinkStack(stack);

		if (GetPriority(mychar->p) > GetPriority(p)) {	//如果優先級低
			Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));
			return;
		}
		//輸出並彈出
		std::cout << *(mychar->p);
		Pop_LinkStack(stack);
		//釋放內存
		delete mychar;

	}
	//沒有遇到優先級低的,所以只有等到所有元素彈完了後,再把此符號壓入棧
	Push_LinkStack(stack, (LinkNode*)CreateMyChar(stack, p));


}


int main(){
		char* str = (char*)"8+(3-1)*5";
		std::cout << "需要計算的式子爲:\n"<<str<<std::endl;
		char* p = str;

		//創建棧
		LinkStack* stack = Init_LinkStack();

		while (*p != '\0') {
			if (IsNumber(p)) {
				NumberOperate(p);
			}
			else if (IsLeft(p)) {
				LeftOperate(stack, p);
			}
			else if (IsRight(p)) {
				RightOperate(stack, p);
			}
			else if (IsOperator(p)) {
				OpOperate(stack, p);
			}
			p++;
		}

		//最後把棧裏所有符號彈出
		while (Size_LinkStack(stack) > 0) {
			MyChar* mychar = (MyChar*)Top_LinkStack(stack);
			//輸出並彈出
			std::cout <<*(mychar->p);
			Pop_LinkStack(stack);
			//釋放內存
			delete mychar;
			mychar = nullptr;
		}
		//清空棧
		FreeSpace_LinkStack(stack);
	
	system("pause");
}

後綴表達式求解

後綴表達式裏只有運算符和數字!!!
遍歷後綴表達式中的運算符和數字
1,對於數字,直接進棧
2,對於符號
      \;\;\;(1)從棧中彈出右操作數
      \;\;\;(2)從棧中彈出左操作數
      \;\;\;(3)根據符號進行運算
      \;\;\;(4)將這個結果再壓入棧中
遍歷結束,棧中唯一數字爲計算結果!

當遇到負數時,負數前面的符號不是二元的,是一元的!但是可以當做是二元的,其左操作數當做是零!!!所以在只有右操作數,沒有左操作數時,就是遇到了一元運算符!!!
如果此時符號是減號,那麼左操作數補個零就行。
如果此時符號是其他的一元操作符,需要另外考慮。

前面顯示出後綴表達式但是沒有接收,所以需要用一個string的遍歷接收一下!

//main.cpp
//初始化後綴表達式的接收變量
std::string suffix_expr = "";
//在每一個輸出字符的地方,換成拼接字符串
suffix_expr += *p;
//suffix_expr += *(mychar->p);
//main.cpp
typedef struct MYNUM {
	LinkNode node;
	double val;
}MyNum;
double Caculate(double leftNum, double rightNum, char* p) {
	double ret = 0;
	switch (*p) {
	case '+'://加
		ret = leftNum + rightNum;
		break;
	case '-'://減
		ret = leftNum - rightNum;
		break;
	case '*'://乘
		ret = leftNum * rightNum;
		break;
	case '%'://取餘
		ret = (int)leftNum % (int)rightNum;
		break;
	case '/'://if (rightNum == 0) {
			std::cout << "除數不能爲零!!!" << std::endl;
			return -1;
		}
		ret = leftNum / rightNum;
		break;
	case '^'://乘方
		ret = pow(leftNum, rightNum);
		break;
	default:
		std::cout << "計算符號不對!!!" << std::endl;
		return -1;
	}
	return ret;

}


int main(){  

	char ch='y';
	do {

		//初始化字符串
		suffix_expr = "";

		//char* str = (char*)"8+(3-1)*5";
		char str[50];
		std::cout << "需要計算的式子爲:\n";
		std::cin >> str; 


	    //.....中間代碼省略

	    
		//創建新棧
		LinkStack* stack2 = Init_LinkStack();



		//後綴表達式成功得到了
		char* str2 = const_cast<char *>(suffix_expr.c_str());
		std::cout << "式子的後綴表達式爲:\n" << str2 << std::endl;

		p = str2;

		while (*p != '\0') {
			if (IsNumber(p)) {
				//數字直接入棧
				MyNum* mynum = new MyNum;
				mynum->val = *p - '0';//轉換成數字
				Push_LinkStack(stack2, (LinkNode*)mynum);
			}
			else if (IsOperator(p)) {
				//先彈出右操作數
				MyNum* right = ((MyNum*)Top_LinkStack(stack2));
				double rightNum = right->val;
				Pop_LinkStack(stack2);
				delete right;
				right = nullptr;

				//取出左操作數
				MyNum* left = ((MyNum*)Top_LinkStack(stack2));
				double leftNum=0;//當棧爲空時,即此運算符應該是一元運算符
				if (left ) {//如果棧不爲空
					leftNum = left->val;
					Pop_LinkStack(stack2);
					delete left;
					left = nullptr;
				}
				
				

				//根據操作符進行計算
				MyNum* mynum = new MyNum;
				mynum->val = Caculate(leftNum, rightNum, p);
				//把計算結果再壓入棧中
				Push_LinkStack(stack2, (LinkNode*)mynum);

			}

			p++;


		}

		if (Size_LinkStack(stack2) == 1) {
			MyNum* mynum = ((MyNum*)Top_LinkStack(stack2));
			std::cout << "計算結果是:\n" << mynum->val << std::endl;
			Pop_LinkStack(stack2);
			delete mynum;
			mynum = nullptr;
		}
		else {
			std::cout << "棧中元素還有 " << Size_LinkStack(stack2)<<" 個"<<std::endl;
		}

		//釋放棧
		FreeSpace_LinkStack(stack2);


		std::cout << "繼續嗎?Y/N" << std::endl;
		std::cin >> ch;
	}while (ch=='y'||ch=='Y');

	system("pause");
}

另外,這個計算器只能算1+2*(7%2-5^2)/9這個樣子的式子,遇到三角函數和對數沒辦法!
下面是實際效果:
在這裏插入圖片描述

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