韩顺平_PHP程序员玩转算法公开课(第一季)09_使用栈完成高级计算器(1)_学习笔记_源代码图解_PPT文档整理

文西马龙:http://blog.csdn.net/wenximalong/

堆栈——最神奇的数据结构


4.完成一个自定义优先级的综合计算器

把 字符串 3+2*6-2 把结果计算出来 你拿到的是字符串
问:
有没有一个函数,可以把字符串当做一个运算字符来执行的?
如果有,那么此函数的底层一定是用栈来实现的
即使你使用 一个函数就可以把这个算出,它的底层一定是堆栈来实现的

步骤
1.界面搞定
2.思路=====>分析图
3+2*6-2 从人的眼睛看 是 3+12-2=>15-2=>13,但是怎么让计算机知道呢, 计算机可没长眼睛,它怎么知道要先算2*6,然后再3+12,最后再15-2呢。计算机要扫描3 + 2 * 6 - 2一个个的字符


思路【怎么做】
思路[怎么做] ★现在的这个思路还不是很完美的,还是有考虑不周全的地方,先死后活,先有思路了,在后面再完善。★
1.程序扫描表达式,一个一个的扫描
2.当发现这个字符是数字的时候,直接入数栈
3.如果发现是运算符
3.1如果符号栈为空,就直接入符号栈
3.2如果符号栈不为空,就要判读。如果当前运算符的优先级小于等于符号栈顶的这个运算符的优先级,就计算,并把计算结果入数栈,然后把当前符号入栈(然后把当前符号入栈,此处会有问题,碰到复杂运算的时候,先考虑简单的,后面再优化,先死后活)
3.3如果符号栈不为空,就要判读。如果当前运算符的优先级大于符号栈顶的这个运算符的优先级,就入栈
4.当扫描完毕后,就依次弹出数栈和符号栈的数据,并计算,最终留在数栈的值,就是运算结果。

3+2*6-2
过程:
(1)
首先扫描到一个数字3,直接入数栈
(2)继续扫描发现是一个+,现在符号栈是空的,直接入栈
(3)继续扫描发现是一个数字2,直接入数栈
(4)继续扫描发现是一个*,现在符号栈不为空,并且*大于当前符号栈栈顶+的运算优先级,直接入栈
(5)继续扫描发现是一个数字6,直接入数栈
(6)继续扫描发现是一个-,现在符号栈不为空,并且-小于当前符号栈栈顶*的运算优先级,则计算。怎么计算呢,从数栈弹出两个数,从符号栈弹出一个运算符。即从数栈弹出6和2,然后从符号栈弹出*,并用变量接收。注意此时两个栈的top的指向变化
$num1=6;
$num2=2;
$oper=*;
$res=6*2; ==>12

图片大,在新窗口中打开图片,观看完整图片

(7)结果12要重新入栈

(8)继续扫描发现是一个-,直接入符号栈(然后把当前符号入栈,此处会有问题,碰到复杂运算的时候,先考虑简单的,后面再优化,先死后活)
(9)继续扫描发现是一个数字2,直接入数栈

至此扫描完毕
(10)
现在开始依次弹出,先从数栈弹出2和12,从符号栈弹出-,计算;然后再把结果10入数栈

(11)再从数栈弹出10和3,从符号栈弹出+,计算;然后再把结果13入栈,符号栈为空,最终结果13。


现在思路有了,怎样转成代码
思路----(长期艰苦的练习)----->代码
注意:在我们开发中,根据需要可以灵活的增加你需要的函数,不要想着一步到位

输入界面

calculator.php

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
	</head>
	<body>
		<h1>高级计算器</h1>
		<form action='jisuan.php'>
			请输入一个运算表达式<input type='text' name='exp' />
			<input type='submit' value='计算' />
		</form>
	</body>
</html>

结果

jisuan.php

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
	</head>
	<body>
		<h1>计算结果 </h1>
		<?php
			$exp=$_GET['exp'];
			//echo $exp.'结果是=';
			//$exp='3+2*6-2';
			//定义一个数栈和一个符号栈
			$numsStack=new MyStack();
			$operStack=new MyStack();

			$index=0; //$index就是一个扫描的标记
			while(true){
				//依次取出字符
				$ch=substr($exp,$index,1); //从$exp里的$index位置取出1个字符

				//判断$ch是不是一个运算符号
				if($operStack->isOper($ch)==true){
					//说明是运算符
					/*
					3.如果发现是运算符
					3.1如果符号栈为空,就直接入符号栈
					3.2如果符号栈不为空,就要判读。如果当前运算符的优先级小于等于符号栈顶的这个运算符的优先级,就计算,并把计算结果入数栈,然后把当前符号入栈(然后把当前符号入栈,此处会有问题,碰到复杂运算的时候,先考虑简单的,后面再优化,先死后活)
					3.3如果符号栈不为空,就要判读。如果当前运算符的优先级大于符号栈顶的这个运算符的优先级,就入栈
					 */
					//把思路转成代码
					if($operStack->isEmpty()){
						$operStack->push($ch);
					}else{
						//需要一个函数,来获取运算符的优先级
						//定义 *和/为1 +和-为0
						$chPRI=$operStack->PRI($ch); //这是当前的
						$stackPRI=$operStack->PRI($operStack->getTop()); //这是栈里的
						if($chPRI<=$stackPRI){
							//计算
							//从数栈里依次出栈两个数
							$num1=$numsStack->pop();
							$num2=$numsStack->pop();
							//再从符号栈里取出一个运算符
							$oper=$operStack->pop();

							//这里还需要一个计算的函数
							$res=$operStack->getResult($num1,$num2,$oper);

							//然后还要把$res入数栈
							$numsStack->push($res);

							//把当前这个符号再入符号栈。???这里会有问题,一会再解决
							$operStack->push($ch);
						}else{
							//如果当前运算符的优先级大于符号栈顶的这个运算符的优先级,就入栈
							$operStack->push($ch);
						}
					}
				}else{
					//是数字,就入数栈
					$numsStack->push($ch);
				}

				$index++; //让$index指向下一个字符
				//判断是否已经扫描完毕
				if($index==strlen($exp)){
					break;
				}
				//当扫描完毕后,就break
			}

			/*
			 4.当扫描完毕后,就依次弹出数栈和符号栈的数据,并计算,最终留在数栈的值,就是运算结果。
			*/
			//只要符号栈不空,就一直计算
			while(!$operStack->isEmpty()){
				$num1=$numsStack->pop();
				$num2=$numsStack->pop();
				$oper=$operStack->pop();
				$res=$operStack->getResult($num1,$num2,$oper);
				$numsStack->push($res);
			}

			//当退出while后,在数栈中一定有一个数,这个数就是最后结果
			echo $exp.'='.$numsStack->getTop();


			//这是我们自定义的栈
			class MyStack{
				private $top=-1; //默认是-1,表示该栈是空的
				private $maxSize=5; //$maxSize表示栈最大容量
				private $stack=array();

				//增加一个函数[提示,在我们开发中,根据需要可以灵活的增加你需要的函数,不要想着一步到位]
				//计算的函数
				public function getResult($num1,$num2,$oper){
					$res=0;
					switch($oper){
						case '+':
							$res=$num1+$num2;
						break;
						case '-':
							//$res=$num1-$num2;要注意减的顺序,这样不对
							$res=$num2-$num1; //注意顺序
						break;
						case '*':
							$res=$num1*$num2;
						break;
						case '/':
							$res=$num2/$num1; //注意顺序
						break;

					}
					return $res;
				}

				//返回栈顶的字符,只取,但是不出栈
				public function getTop(){
					return $this->stack[$this->top];
				}

				//判断优先级的函数
				//定义 *和/为1 +和-为0。先死后活,先考虑简单的,至于带括号的,在后面再改进。
				public function PRI($ch){
					if($ch=='*'||$ch=='/'){
						return 1;
					}else if($ch=='+'||$ch=='-'){
						return 0;
					}
				}

				//判断栈是否为空
				public function isEmpty(){
					if($this->top==-1){
						return TRUE;
					}else{
						return FALSE;
					}
				}
				
				//判断是不是一个运算符
				public function isOper($ch){
					if($ch=='-'||$ch=='+'||$ch=='*'||$ch=='/'){
						return TRUE;
					}else{
						return FALSE;
					}
				}

				//入栈的操作
				public function push($val){
					//先判断栈是否已经满了
					if($this->top==$this->maxSize-1){ //5-1=4 0 1 2 3 4
						echo'<br/>栈满,不能添加';
						return;
					}
					$this->top++; //先加再放
					$this->stack[$this->top]=$val; //就入栈了
				}

				//出栈的操作,就是把栈顶的值取出
				public function pop(){
					//判断是否栈空
					if($this->top==-1){
						echo'<br/>栈空';
						return;
					}

					//把栈顶的值,取出
					$topVal=$this->stack[$this->top];
					$this->top--;
					return $topVal;
				}

				//显示栈的所有数据的方法
				public function showStack(){
					if($this->top==-1){
						echo'<br/>栈空';
						return;
					}

					echo'<br/>当前栈的情况是...';
					for($i=$this->top;$i>-1;$i--){ //反着显示
						echo'<br/>stack['.$i.']='.$this->stack[$i]; //从栈顶开始显示
					}
				}
			}
		?>
	</body>
</html>

代码写这么长,有压力,一次性敲完了,然后运行,如果哪个地方出错了,就要排查了。
这么长的代码,你就是跟着视频敲,也 很容易出错。视频中的可以运行,你的就不一定了。代码敲多了,在敲'->'的时候少敲'-',或者operStack敲成operstack等等,都是很容易发生的。

如果运算式是:30+2*6-2的时候,上面的代码就出问题了

上面的jisuan.php代码就有需要改进的地方了,在下一篇中讲解。


韩顺平_PHP程序员玩转算法公开课_学习笔记_源代码图解_PPT文档整理_目录

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值