查看: 2555|回复: 2
打印 上一主题 下一主题

表达式计算器(数据结构)

[复制链接]
跳转到指定楼层
沙发
发表于 2014-11-29 19:38:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 伊海 于 2014-11-29 19:43 编辑

要求:写一个控制台程序,该程序实现输入一个表达式能够根据表达式计算出相应的结果。
例如:例如:输入字符串"1+2*(3-4)/5=",输出结果:0.6

基本要求:能够对+ - * /  进行运算,能够对优先级进行运算和括号匹配运算。
扩展要求:
1.支持多数据类型输入,如: 0x12F(十六进制)、 12FH(十六进制)、  234O(八进制) 、‘F’(字符) 、0000 1111B(二进制)
2.支持未知数赋值运算  如: x=3  则  3X+5=14
3.支持常用函数运算 如: log、 ln、 sin、 sqrt等;
4.支持大数据计算;
5.支持自定义运算符。

这个题目涉及的东西很多很多,在往后待我慢慢给大家分析。
罗列下:
1.动态双向链表(这是我的算法,或许你可以试试数组,但是这两者的优劣在何处?我希望你能明白),表达式的校对
2.中缀表达式转化为后缀表达式
3.后缀表达式的计算
4.文件读写
5.不同进制的转换
6.大整数算法

(1)从大方向大致列举出这六点,现在我们就一项一项进行搞定。

  1. //我们首先建立一个动态双向链表:
  2. /* 建立一个头结点*/
  3.       Formula_head = new node;
  4.       init_node(Formula_head);
  5.       Formula_head->ch = getchar();
  6.       Formula_follow = Formula_head;

  7. /* 从用户读取输入*/
  8.         for(;;){
  9.          
  10.          newnode = new node;    /* 新增加一个节点来存储输入*/
  11.          init_node(newnode);    /* 初始化节点数据 */
  12.      
  13.          while(1){    /* 获取输入数据,并且删除空格*/
  14.          
  15.        newnode->ch = getchar();
  16.        //cin >> newnode->ch ;
  17.           if(' ' != newnode->ch ){
  18.            
  19.            //print_link_data(Formula_follow);
  20.            
  21.            break;
  22.            
  23.           }
  24.          }
  25.      
  26.          /* 结束输入*/
  27.          if('\n' == newnode->ch){  
  28.       
  29.           break;
  30.          
  31.          }
  32.    
  33.          /* 加入节点*/
  34.          Formula_follow = add_node(Formula_follow, newnode);
  35.          
  36.         }

  37. //读取表达式之后,需要对表达式进行校对,处理未知数和防止输入不规则表达式(错误表达式):
  38.         /* 检查是否是未知数算式*/
  39.         if( check_unknow(Formula_head) ){
  40.          
  41.          x_head = Formula_head;
  42.          Formula_head = NULL;
  43.          X_flag = 1;
  44.          continue;
  45.          
  46.         }
  47.    
  48.         /*如果有未知数的处理*/
  49.            if(X_flag){
  50.             
  51.          Formula_head = add_x_in_link(Formula_head,x_head);
  52.          
  53.         }
  54.    
  55.         /*检查错误*/
  56.         if(check(Formula_head)){
  57.          
  58.          continue;
  59.          
  60.         }

  61. /***************************************************
  62. ** 函数名称:check
  63. ** 函数功能:扫描链表,检查表达式是否错误
  64. ** 入口参数:node *head
  65. ** 出口参数:
  66. ***************************************************/
  67. int check(node *head)
  68. {
  69. int brackets = 0;

  70. for(; head; head=head->next){
  71.   
  72.   /*连续出现2个运算符,报错*/
  73.   if(('+'==head->ch) || ('-'==head->ch) ||
  74.      ('*'==head->ch) || ('/'==head->ch)){
  75.    
  76.     if(('+'==head->next->ch) || ('-'==head->next->ch) ||
  77.        ('*'==head->next->ch) || ('/'==head->next->ch)){
  78.    
  79.         cout<<"erro: Consecutive two operators!"<<endl;
  80.    
  81.         return 1;
  82.        }
  83.    
  84.   }
  85.   
  86.   /* 括号不匹配,报错*/
  87.   if('(' == head->ch){
  88.    
  89.    brackets++;
  90.    
  91.   }
  92.   if(')' == head->ch){
  93.    
  94.    brackets--;
  95.    
  96.    if(brackets<0){
  97.    
  98.     cout<<"erro: brackets is not right,please check it out!"<<endl;
  99.     return 1;
  100.    
  101.    }
  102.   }
  103.   
  104. }
  105. /* 括号不匹配*/
  106. if(0 != brackets){
  107.   
  108.   cout<<"erro: brackets is not right,please check it out!"<<endl;
  109.   return 1;
  110.    
  111. }

  112. /*没错返回0*/
  113. return 0 ;

  114. }

  115. /***************************************************
  116. ** 函数名称:check_unknow
  117. ** 函数功能:检查是否为未知数算式
  118. ** 入口参数:node *head
  119. ** 出口参数:
  120. ***************************************************/
  121. int check_unknow(node *head)
  122. {

  123. if(('x'==head->ch) && ('='==head->next->ch) && (NULL!=head->next->next)){
  124.   
  125.   return 1 ;
  126.   
  127. }

  128. return 0;
  129. }
复制代码






回复

使用道具 举报

板凳
 楼主| 发表于 2014-11-29 19:39:22 | 只看该作者
本帖最后由 伊海 于 2014-11-29 19:41 编辑

这里将数据结构真正用于实际项目中,达到算法最优、代码最优的效果。
(2)我们应该到了中缀表达式转化为后缀表达式。
这个数据结构在计算器表达式的项目中是非常经典的。这里我就再啰嗦几句,中缀表达式也叫做中缀记法,
如1+2*3便是,因为操作符在中间嘛,所以就叫做中缀表达式。
那么什么叫做后缀表达式,举一反三,即为操作数在后面嘛,那么即是:123*+。
后缀表达式也叫做逆波兰表达式。
中缀表达式转化为后缀表达式,栈的用武之地来了,说到栈,第一个词语就是FILO,
先进后出。中缀表达式转化为后缀表达式也正是用了这一个特点。


  1. /***************************************************
  2. ** 函数名称:*mid_to_bk
  3. ** 函数功能:中缀转换成后缀表达式
  4. ** 入口参数:node *head
  5. ** 出口参数:
  6. ***************************************************/
  7. node *mid_to_bk(node *head)
  8. {
  9. node *mid = Separate_str(head); /* 处理后的中缀表达式链表*/

  10. node *bk = NULL; /* 后缀表达式头指针*/

  11. node *ptr = bk;

  12. node *oper = NULL; /* 符号堆栈 */

  13. node *newnode = NULL;

  14. node *code_node = NULL; /* 用来记录,保存地址 */

  15. /* 开始转换*/
  16. for(; mid; mid=mid->next){

  17. /* 将节点数据拷贝到新节点去*/
  18. newnode = new node;
  19. init_node(newnode);
  20. copy_node_data(newnode,mid);

  21. /* 如果遇到数字就直接入栈*/
  22. if(NUM == newnode->type){

  23. /*加入链表*/
  24. if(NULL == bk){
  25. ptr = bk = add_node(ptr,newnode);
  26. } else{
  27. ptr = add_node(ptr,newnode);
  28. }

  29. }

  30. /* 如果遇到特殊表达式*/
  31. else if(SP_OPERATOR == newnode->type){

  32. /* 如果前一个运算符也是特殊运算符就要出栈 */
  33. while(oper){

  34. if(SP_OPERATOR == oper->type){

  35. code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
  36. ptr = add_node(ptr,oper);
  37. oper = code_node;

  38. } else break;

  39. }

  40. oper = add_node(oper,newnode);

  41. }


  42. /*如果遇到普通运算符*/
  43. else if(OPERATOR == newnode->type){

  44. /*'('直接进栈*/
  45. if('(' == newnode->ch){
  46. oper = add_node(oper,newnode);
  47. }

  48. /*')'直接退栈到')'*/
  49. else if(')' == newnode->ch){
  50. while(oper){
  51. if('(' == oper->ch)break;/* 遇到'('退栈结束*/
  52. code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
  53. ptr = add_node(ptr,oper);
  54. oper = code_node;
  55. }

  56. oper = del_node(oper); /* 删除掉'('符号*/
  57. }

  58. /* 遇到+-全部退栈,直到遇到'(',或者退完为止*/
  59. else if(('+' == newnode->ch) || ('-' == newnode->ch)){
  60. while(oper){

  61. if('(' == oper->ch)break; /* 遇到'('退栈结束*/
  62. code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
  63. ptr = add_node(ptr,oper);
  64. oper = code_node;

  65. }
  66. oper = add_node(oper,newnode);
  67. }

  68. /* 遇到/* 把特殊运算符全部退栈处理*/
  69. else if(('*' == newnode->ch) || ('/' == newnode->ch)){
  70. while(oper){

  71. if('(' == oper->ch)break; /* 遇到'('和特殊运算符截止*/
  72. if(('+' == oper->ch) || ('-' == oper->ch))break;
  73. code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
  74. ptr = add_node(ptr,oper);
  75. oper = code_node;

  76. }
  77. oper = add_node(oper,newnode);
  78. }

  79. /*
  80. * 下面这段程序是调试使用
  81. */
  82. #if 0
  83. cout<<"mid: ";
  84. print_link_data(test);
  85. getchar();
  86. #endif

  87. }

  88. }


  89. /* 把最后留在堆栈里面的东西退出来*/
  90. while(oper){

  91. code_node = oper->befor; /* 将这个节点换到后缀表达式里面*/
  92. ptr = add_node(ptr,oper);
  93. oper = code_node;

  94. }


  95. ptr->next = NULL;

  96. /*
  97. * 下面这段程序是调试使用
  98. */
  99. #if 0
  100. cout<<"mid: ";
  101. print_link_data(bk);
  102. #endif

  103. return bk;
  104. }

复制代码



回复 支持 反对

使用道具 举报

地板
 楼主| 发表于 2014-11-29 19:40:39 | 只看该作者
(3)承接上,我们实现了中缀表达式转化为逆波兰表达式,这节,我们要做的肯定就是怎么样将逆波兰表达式计算出来。
其实这个计算过程是很简单了,如下:

  1. /***************************************************
  2. ** 函数名称:calculate
  3. ** 函数功能:对后缀表达式进行计算处理
  4. ** 入口参数:node *formula
  5. ** 出口参数:result[0]
  6. *******************************************
  7. ********/
  8. double calculate(node *formula)
  9. {

  10. double result[200];
  11. int i=0;
  12. for(; formula; formula = formula->next){

  13. /*数据再次进堆栈*/
  14. if(NUM == formula->type){
  15. result = formula->num;
  16. i++;
  17. }
  18. //由于前面进行了中缀表达式转化为逆波兰表达式,因此为这里的计算奠定了十足的运算基础。
  19. /*遇到普通符号就直接进行判断并且计算*/
  20. else if(OPERATOR == formula->type){
  21. /*加法计算*/
  22. if('+' == formula->ch){
  23. i--;
  24. result[i-1] = result[i-1]+result;
  25. }

  26. /*减法计算*/
  27. if('-' == formula->ch){
  28. i--;
  29. result[i-1] = result[i-1]-result;
  30. }

  31. /*乘法计算*/
  32. if('*' == formula->ch){
  33. i--;
  34. result[i-1] = result[i-1]*result;
  35. }

  36. /*除法计算*/
  37. if('/' == formula->ch){

  38. i--;

  39. /* 如果除数是0的话提示错误*/
  40. if(0 == result){

  41. cout<<"erro: the divvisor equal zero!Please check is out!"<<endl;
  42. exit(1);

  43. } else{
  44. result[i-1] = result[i-1]/result;
  45. }

  46. }

  47. }
  48. /*遇到特殊符号*/
  49. else{

  50. /*sin*/
  51. if(!strcmp(formula->str,"sin")){ //进行符号匹配,如果是sin运算符,进行项目的运算。这里读者其实可以进行改进
  52. result[i-1] = sin(result[i-1]);
  53. }

  54. /* cos*/
  55. else if(!strcmp(formula->str,"cos")){
  56. result[i-1] = cos(result[i-1]);
  57. }

  58. /* tan*/
  59. else if(!strcmp(formula->str,"tan")){
  60. result[i-1] = tan(result[i-1]);
  61. }

  62. /* log*/
  63. else if(!strcmp(formula->str,"log")){
  64. result[i-1] = log(result[i-1]);
  65. }

  66. /*sqrt*/
  67. else if(!strcmp(formula->str,"sqrt")){
  68. result[i-1] = sqrt(result[i-1]);
  69. }

  70. /*help*/
  71. else if(!strcmp(formula->str,"help")){

  72. r_file() ;

  73. } else{
  74. cout<<"have no this OPERATOR,if you want add it,please add it upon, thank you!\n"<<endl;
  75. exit(1);
  76. }

  77. }

  78. }
  79. return result[0];
  80. }
复制代码

//有些东西,看似很难,那是因为你没有突破心理的障碍,
//当你全身心投入去实践的时候,你会发现,一切都是那么简单并且有趣


回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入因仑

本版积分规则

快速回复 返回顶部 返回列表