Leetcode solution 772: Basic Calculator III

 Problem Statement 

 

Implement a basic calculator to evaluate a simple expression string.

The expression string may contain open ( and closing parentheses ),the plus + or minus sign -non-negative integers and empty spaces .

The expression string contains only non-negative integers, +-*/ operators,open ( and closing parentheses ) and empty spaces . The integer division should truncate toward zero.

You may assume that the given expression is always valid. All intermediate results will be in the range of [-2147483648,2147483647].

Some examples:

"1 + 1" = 2
" 6-4 / 2 " = 4
"2*(5+5*2)/3+(6/2+8)" = 21
"(2+6* 3+5- (3*14/7+2)*5)+3"=-12

 

Note: Do not use the eval built-in library function.

Problem link

 

Video Tutorial

You can find the detailed video tutorial here

 

Thought Process

This is an extension problem to Basic Calculator and Basic Calculator II. The differences are this time it really looks like a real calculator where it can do "+-*/" and with brackets.

 

It‘s still the same thought process with exactly the same idea before: having two stacks,  one stack for the operator and the other for the operands,and with exactly the same calculating preferences before: calculate * and / before + and -. When seeing right bracket,continue popping till you see the left bracket.

 

This works fine assuming all the integers are non-negative,which they are supposed to be based on the problem description,and that‘s what most of the existing online leetcode solutions did as well.

 

As of 08/18/2019,this doesn‘t seem to be the case because leetcode decides to include "non-negative" integer test cases. See below case "-1+4*3/3/3"

 

分享图片

An "non-negative" integer case

 

 

Since we are practicing interviews,so let‘s also address the non-negative integer case as well

 

A few caveats

  • (New) Handle negative integer starting case:
    "-1 + 2" or "-(2+3)*4"
  • (New) Handle negative integer in between expression case:
    "1 - (-7)"
  • (New) Calculate whenever you can calculate since division order matters
    15 / 3 / 5 should be 1,but it won‘t work 3 / 5 = 0,then 15/0 invalid
  • Pay attention to the order when popping out operands and calculate,the order matters. 
  • The number might not be just single digit,so need to read the char and convert the numbers

Solutions

 

Two stacks for non-negative integer solution

Keep two stacks,operator and operands as explained in the above "Thought Process"

 

  1 public int calculate(String s) {
  2         if (s == null || s.length() == 0) {
  3             throw new IllegalArgumentException("invalid input");
  4         }
  5 
  6         int i = 0;
  7 
  8         Stack<Long> operands = new Stack<>();
  9         Stack<Character> operators = new Stack<>();
 10         StringBuilder number = new StringBuilder(); // deal with non single digit numbers
 11 
 12         while (i < s.length()) {
 13             char c = s.charAt(i);
 14 
 15             if (c == ‘ ‘) {
 16                 i++;
 17                 continue;
 18             }
 19 
 20             if (Character.isDigit(c)) {
 21                 number.append(c);
 22             } else if (this.isValidOperator(c)) {
 23                 if (number.length() != 0) {
 24                     operands.push(Long.parseLong(number.toString()));
 25                     number = new StringBuilder();
 26                 }
 27 
 28                 // Basically based on different priority of operators
 29                 if (operators.isEmpty()) {
 30                     // it says it only contains non-negative integer,but now we have "-1 + 2","-(2+3)*4"
 31                     operators.push(c);
 32                 } else if (!operators.isEmpty() && (c == ‘*‘ || c == ‘/‘) && (operators.peek() == ‘+‘ || operators.peek() == ‘-‘)) {
 33                     // do nothing,keep pushing because */ has higher priority than +-
 34                     operators.push(c);
 35                 } else if (!operators.isEmpty() && (c == ‘+‘ || c == ‘-‘) && (operators.peek() == ‘*‘ || operators.peek() == ‘/‘)) {
 36                     // calculate all previous expressions till hit the left bracket
 37                     while (!operators.isEmpty() && operators.peek() != ‘(‘) {
 38                         operands.push(this.calculateValue(operands,operators.pop()));
 39                     }
 40 
 41                     operators.push(c);
 42                 } else if (c == ‘(‘) {
 43                     operators.push(c);
 44                 } else if (c == ‘)‘) {
 45                     if (number.length() != 0) {
 46                         operands.push(Long.parseLong(number.toString()));
 47                         number = new StringBuilder();
 48                     }
 49 
 50                     while (!operators.isEmpty() && operators.peek() != ‘(‘) {
 51                         char op = operators.pop();
 52                         operands.push(this.calculateValue(operands,op));
 53 
 54                     }
 55                     operators.pop(); // pop out the left (
 56 
 57                 } else {
 58                     // for normal +- with previous +- || */ with previous */ case just calculate 1 step ahead
 59                     // but because 15 / 3 / 5 should be 1,so we have to calculate 
 60                     // every time we can calculate and get result 
 61 
 62                     if (!operators.isEmpty() && operators.peek() != ‘(‘) {
 63                         operands.push(this.calculateValue(operands,operators.pop()));
 64                     }
 65 
 66                     operators.push(c);
 67 
 68 
 69                 }
 70             }
 71             i++;
 72         }
 73 
 74         if (number.length() != 0) {
 75             operands.push(Long.parseLong(number.toString()));
 76         }
 77         // for "3+2*2" case that‘s why we need a while loop
 78         while (!operators.isEmpty()) {
 79             operands.push(this.calculateValue(operands,operators.pop()));
 80         }
 81 
 82         return (int)operands.pop().longValue();
 83     }
 84 
 85     private boolean isValidOperator(char op) {
 86         if (op == ‘+‘ || op == ‘-‘ || op == ‘*‘ || op == ‘/‘ || op == ‘(‘ || op == ‘)‘) {
 87             return true;
 88         }
 89         return false;
 90     }
 91 
 92     private long calculateValue(Stack<Long> operands,char op) {
 93         long o2 = operands.pop();
 94         long o1 = operands.pop();
 95 
 96         if (op == ‘+‘) {
 97             return o1 + o2;
 98         } else if (op == ‘-‘) {
 99             return o1 - o2;
100         } else if (op == ‘*‘) {
101             return o1 * o2;
102         } else if (op == ‘/‘) {
103             return o1 / o2;
104         } else {
105             throw new IllegalArgumentException("invalid op! " + op);
106         }
107     }

 

 

 

Time Complexity: O(N),N is the length of the string

Space Complexity: O(N),extra stack is needed

 

Two stacks for negative integer solution

Essentially how do address the below two situations given the current code structure

  • (New) Handle negative integer starting case. I simply just check if the first char in trimmed string is "-",and push a -1 into operands and "*" into operator. (e.g.,  -(a+b) = -1 * (a+b) and -a = -1 * a)
    "-1 + 2" or "-(2+3)*4"
  • (New) Handle negative integer in between expression case. This is a bit hacky because a  "-" in the middle of the expression could mean two things: a normal minus sign or a negative sigh denoting negative integer. Luckily "1 - - 2" would not be a valid case which means there should always be a left bracket before the negative sign for negative integer. What I did was using isNegativeNumberFollowingLeftBracket to find those cases and put a -1 and * into the operator and operands respectively
    "1 - (-7)"

 

  1 public int calculate(String s) {
  2         if (s == null || s.length() == 0) {
  3             throw new IllegalArgumentException("invalid input");
  4         }
  5         
  6         s = s.trim();
  7         int i = 0;
  8 
  9         Stack<Long> operands = new Stack<>();
 10         Stack<Character> operators = new Stack<>();
 11         StringBuilder number = new StringBuilder(); // deal with non single digit numbers
 12 
 13         while (i < s.length()) {
 14             char c = s.charAt(i);
 15 
 16             if (c == ‘ ‘) {
 17                 i++;
 18                 continue;
 19             }
 20 
 21             if (Character.isDigit(c)) {
 22                 number.append(c);
 23             } else if (this.isValidOperator(c)) {
 24                 if (number.length() != 0) {
 25                     operands.push(Long.parseLong(number.toString()));
 26                     number = new StringBuilder();
 27                 }
 28 
 29                 // Basically based on different priority of operators
 30                 if (operators.isEmpty()) {
 31                     // it says it only contains non-negative integer,"-(2+3)*4"
 32                     // this is to deal with the starting negative case
 33                     if (c == ‘-‘ && i == 0) {
 34                         operands.push(-1L);
 35                         operators.push(‘*‘);
 36                     } else {
 37                         operators.push(c);
 38                     }
 39                 } else if (!operators.isEmpty() && (c == ‘*‘ || c == ‘/‘) && (operators.peek() == ‘+‘ || operators.peek() == ‘-‘)) {
 40                     // do nothing,keep pushing because */ has higher priority than +-
 41                     operators.push(c);
 42                 } else if (!operators.isEmpty() && (c == ‘+‘ || c == ‘-‘) && (operators.peek() == ‘*‘ || operators.peek() == ‘/‘)) {
 43                     // calculate all previous expressions till hit the left bracket
 44                     while (!operators.isEmpty() && operators.peek() != ‘(‘) {
 45                         operands.push(this.calculateValue(operands,operators.pop()));
 46                     }
 47 
 48                     operators.push(c);
 49                 } else if (c == ‘(‘) {
 50                     operators.push(c);
 51                     // this is to deal with 1 * (-7+2) case
 52                     if (this.isNegativeNumberFollowingLeftBracket(s,i + 1)) {
 53                         operands.push(-1L);
 54                         operators.push(‘*‘);
 55                         while (i < s.length()) {
 56                             if (s.charAt(i) == ‘-‘) {
 57                                 // i++;  // skip this ‘-‘,later we i++ on line 129
 58                                 break;
 59                             }
 60                             i++;
 61                         }
 62                     }
 63                 } else if (c == ‘)‘) {
 64                     if (number.length() != 0) {
 65                         operands.push(Long.parseLong(number.toString()));
 66                         number = new StringBuilder();
 67                     }
 68 
 69                     while (!operators.isEmpty() && operators.peek() != ‘(‘) {
 70                         char op = operators.pop();
 71                         operands.push(this.calculateValue(operands,op));
 72 
 73                     }
 74                     operators.pop(); // pop out the left (
 75 
 76                 } else {
 77                     // for normal +- with previous +- || */ with previous */ case just calculate 1 step ahead
 78                     // but because 15 / 3 / 5 should be 1,so we have to calculate
 79                     // every time we can calculate and get result
 80                     if (!operators.isEmpty() && operators.peek() != ‘(‘) {
 81                         operands.push(this.calculateValue(operands,operators.pop()));
 82                     }
 83                     operators.push(c);
 84                 }
 85             }
 86             i++;
 87         }
 88 
 89         if (number.length() != 0) {
 90             operands.push(Long.parseLong(number.toString()));
 91         }
 92         // for "3+2*2" case that‘s why we need a while loop
 93         while (!operators.isEmpty()) {
 94             operands.push(this.calculateValue(operands,operators.pop()));
 95         }
 96 
 97         return (int)operands.pop().longValue();
 98     }
 99 
100     // given the current index(this would normally be the index after the ‘(‘,find out if there is
101     // a negative integer following the ‘(‘
102     private boolean isNegativeNumberFollowingLeftBracket(String s,int index) {
103         while (index < s.length()) {
104             char c = s.charAt(index);
105             if (c == ‘ ‘) {
106                 index++;
107                 continue;
108             }
109             if (c == ‘-‘) {
110                 return this.isDigitFollowing(s,index + 1);
111             } else {
112                 return false;
113             }
114         }
115         return false;
116     }
117 
118     // given the current index,find out if it‘s a digit following it.
119     private boolean isDigitFollowing(String s,int index) {
120         while (index < s.length()) {
121             char c = s.charAt(index);
122             if (c == ‘ ‘) {
123                 index++;
124                 continue;
125             }
126             if (Character.isDigit(c)) {
127                 return true;
128             }
129             return false;
130         }
131         return false;
132     }
133 
134 
135     private boolean isValidOperator(char op) {
136         if (op == ‘+‘ || op == ‘-‘ || op == ‘*‘ || op == ‘/‘ || op == ‘(‘ || op == ‘)‘) {
137             return true;
138         }
139         return false;
140     }
141 
142     private long calculateValue(Stack<Long> operands,char op) {
143         long o2 = operands.pop();
144         long o1 = operands.pop();
145 
146         if (op == ‘+‘) {
147             return o1 + o2;
148         } else if (op == ‘-‘) {
149             return o1 - o2;
150         } else if (op == ‘*‘) {
151             return o1 * o2;
152         } else if (op == ‘/‘) {
153             return o1 / o2;
154         } else {
155             throw new IllegalArgumentException("invalid op! " + op);
156         }
157     }

 

 

Time Complexity: O(N),extra stack is needed

 

References

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


1.github代码实践源代码是lua脚本语言,下载th之后运行thmain.lua-netTypevgg-savevgg_cifar10/-S0.0001,报错: 试看看安装lua:报错了,参考这篇文章:ubuntu18.04安装lua的步骤以及出现的问题_weixin_41355132的博客-CSDN博客问题解决,安装成功:情况并没有好转,出现相
此文为搬运帖,原帖地址https://www.cnblogs.com/zwywilliam/p/5999924.html前言在看了uwa之前发布的《Unity项目常见Lua解决方案性能比较》,决定动手写一篇关于lua+unity方案的性能优化文。整合lua是目前最强大的unity热更新方案,毕竟这是唯一可以支持ios热更新的办法。然而作
Rime输入法通过定义lua文件,可以实现获取当前时间日期的功能。1.TIMERime是一款可以高度自定义的输入法,相关教程可以查看往期文章,关于时间获取是指输入一个指定关键字,输出当前时间,效果如下(我定义了time关键字):实现如下:①在用户文件夹中新建一个rime.lua文件加入如下代码 ti
localfunctiongenerate_action(params)localscale_action=cc.ScaleTo:create(params.time,params.scale_x,params.scale_y)localfade_action=cc.FadeIn:create(params.time)returncc.Spawn:create(scale_action,fade_action)end
2022年1月11日13:57:45 官方:https://opm.openresty.org/官方文档:https://opm.openresty.org/docs#table-of-contents为什么建议使用opm不建议使用luarocks?http://openresty.org/cn/using-luarocks.html官方解释:请注意!LuaRocks并不是OpenResty官方推荐的装包方式。LuaRoc
在Lua中的table(表),就像c#中的HashMap(哈希表),key和value一一对应。元表:table的一个操作的拓展,里面包含关联了对应的方法,元方法就是其中一个。元方法:当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index键。如果__inde
表排序:table.sort(list[,comp])参数list:指定表,可选参数comp:排序函数,无参数时通常按升序排序。排序函数针对表中连续的序列,其间不可以存在空洞或nil,排序函数需要两个形参(对应表中每次参加比较的两个数据),需要一个比较两个形参表达式的返回值,不能含有等于关系,例如>=,<=,==。do
一、安装lua环境1.1安装依赖包[root@centos7~]#yuminstallgccreadline-devel1.2下线lua源码包并解压[root@centos7~]#wgethttp://www.lua.org/ftp/lua-5.3.5.tar.gz[root@centos7~]#tarxvflua-5.3.5.tar.gz-C/usr/local/src1.3进行编译[root@centos7~]
官网OpenResty® 是一个基于 Nginx 与Lua的高性能Web平台,其内部集成了大量精良的Lua库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态Web应用、Web服务和动态网关。OpenResty® 通过汇聚各种设计精良的 Nginx 模块(主要由
表参考《lua程序设计》可以认为,表是一种动态分配的对象,程序只能操作指向表的引用(或指针)。除此以外,Lua语言不会进行隐藏的拷贝(hiddencopies)或创建新的表--创建表a={}--创建空表k="x"a[k]=10--键“x”值10a[20]="great"--键20值“great”print(a["x"])-->10
https://github.com/galenho/crossover.git一个跨平台的lua游戏服务器开发框架,该框架采用多线程并发来处理消息,开发者只需要调用相应的接口函数并绑定相应的回调函数即可,在逻辑层表现为单线程的开发模式,使开发者易用,易调试,易维护,易扩展,同时拥有快速的响应能力。   框架使用面
参考链接:https://www.runoob.com/lua/lua-metatables.htmlhttps://www.jianshu.com/p/cb945e7073a3 元表是一个table,可以让我们改变table的行为,每个行为有对应的元方法例如,对table进行设置键值,查找键值,运算等,就会触发对应的元方法1--__index:table被访问时,如果找不到这
https://github.com/yuin/gopher-luahttps://github.com/yuin/gopher-lua Lua5.1ReferenceManual-contentshttp://www.lua.org/manual/5.1/ go中使用luapackagemainimport( lua"github.com/yuin/gopher-lua")funcmain(){ l:=lua.NewState() d
编译问题不要留到运行时才跑出来啊。早上9:00-中午3:00,6个小时,服了自己了。 写了一个测试,springboot+redis+lua执行到redisTemplate.execute(redisScript,idList)的时候一直报错,integer无法转换为string。我一直以为是lua脚本写错了,翻文档翻过来又翻过去,写法变了又变,还是解
        。。是字符串连接符,字典用=号连接,  注意fordoend都是连一起,  注意ifthen,  如果local在函数里,是可以访问,非local出了函数一样能用,  doend代码块也是一样,    注意点号表示,只能key是字符串,  注意括号不是必须
C语言与Lua之间的相互调用详解写一个C调用Lua的Demo编译运行C语言调用Lua编译问题总结正确的编译命令问题1:缺少-lm参数问题2:缺少-ldl参数​1、为什么会出现undefinedreferenceto‘xxxxx’错误?​2、-l参数和-L参数写一个C调用Lua的Demo编译运行add.c内容//你需要
1、动态输出打开E:\study\openresty\openresty-1.19.9.1-win64目录下的confginx.conf文件在server中增加一下代码 location/hello{ default_typetext/html; content_by_lua'ngx.say("<p>hello,world</p>")'; }运行后,效果如下图localhost
参见:lipp/lua-websockets:WebsocketsforLua.(github.com)github网址可能需手动转换lipp.github.com/lua-websockets/>github.com/lipp/lua-websocketswebsockets为底层的类似于TCP、UDP的socket(实现上基于更底层的socket),不同于上层的webserver服务端(Service)需并行地支持多
lua发送消息到rabbitmq,我们选择类库lua-resty-rabbitmqstomp 来完成这个任务。类库安装:进入nginx.conf中 lua_package_path 中对应的目录下的resty目录(没有则创建),执行:wget-chttps:/aw.githubusercontent.com/wingify/lua-resty-rabbitmqstomp/master/libes
1Lua介绍Lua是一门以其性能著称的脚本语言,被广泛应用在很多方面。Lua一般用于嵌入式应用,现在越来越多应用于游戏当中,魔兽世界,愤怒的小鸟都有用到。优势Lua极易嵌入到其他程序,可当做一种配置语言。提升应用性能,比如:游戏脚本,nginx,wireshark的脚本兼容性强,可以直接使用C