您的位置:首页 > 职场人生

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

2012-12-16 17:26 896 查看
文西马龙: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文档整理_目录
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐