题目:给定一个化学式formula(作为字符串),返回每种原子的数量。
原子总是以一个大写字母开始,接着跟随0个或任意个小写字母,表示原子的名字。
如果数量大于 1,原子后会跟着数字表示原子的数量。如果数量等于 1 则不会跟数字。例如,H2O 和 H2O2 是可行的,但 H1O2 这个表达是不可行的。
两个化学式连在一起是新的化学式。例如 H2O2He3Mg4 也是化学式。
一个括号中的化学式和数字(可选择性添加)也是化学式。例如 (H2O2) 和 (H2O2)3 是化学式。
给定一个化学式,输出所有原子的数量。格式为:第一个(按字典序)原子的名子,跟着它的数量(如果数量大于 1),然后是第二个原子的名字(按字典序),跟着它的数量(如果数量大于 1),以此类推。
示例 1:
输入:
formula = “H2O”
输出: “H2O”
解释:
原子的数量是 {‘H’: 2, ‘O’: 1}。
示例 2:
输入:
formula = “Mg(OH)2”
输出: “H2MgO2”
解释:
原子的数量是 {‘H’: 2, ‘Mg’: 1, ‘O’: 2}。
示例 3:
输入:
formula = “K4(ON(SO3)2)2”
输出: “K4N2O14S4”
解释:
原子的数量是 {‘K’: 4, ‘N’: 2, ‘O’: 14, ‘S’: 4}。
注意:
所有原子的第一个字母为大写,剩余字母都是小写。
formula的长度在[1, 1000]之间。
formula只包含字母、数字和圆括号,并且题目中给定的是合法的化学式。
来源:力扣(LeetCode)
思路:
我们用一个整型数 n 来表示我们正在遍历的位置
辅助结构:HashMap
递归中分三类
1、当我们碰到左括号时就递归解决左括号即其对应的有括号内的内容。
2、碰到右括号便返回下标+1。
3、碰到元素,则将元素及其数量加入哈希表中。
代码以及详解如下:
public String countOfAtoms(String formula) {
Map<String,Integer> map = new HashMap<String,Integer>();
helper(formula,0,map);
List<String> list = new ArrayList<String>();
for(String name:map.keySet()) {
list.add(name);
}
Collections.sort(list);
StringBuffer sb = new StringBuffer();
for(String name:list) {
sb.append(name);
int num = map.get(name);
if(num>1) {
sb.append(num);
}
}
return sb.substring(0,sb.length());
}
public int helper(String s,int n,Map<String,Integer> map) {
int size = s.length();
while(n<size) {
Map<String,Integer> tempMap = new HashMap<String,Integer>();
if(s.charAt(n) == '(') {//如果遇到左括号递归处理
n = helper(s,n+1,tempMap);
//递归完成之后便处理完毕该括号内部,用tempMap记录括号内部情况,返回有括号下标+1
int nums = 0;
while(n<size&&Character.isDigit(s.charAt(n))) {//有括号处是否有数字
nums = nums*10+s.charAt(n)-'0';
n++;
}
if(nums>0) {
multiply(tempMap,nums);//如果下标大于1(即nums>0)则将里面的元素个数乘于nums
}
combine(map,tempMap);//合并两个map到map里
}
else if(s.charAt(n)==')') {
return n+1;
}
else {//遇到普通元素
int i = n;
n++;
while(n<size&&Character.isLowerCase(s.charAt(n))) {n++;}
String s1 = s.substring(i,n);
int tempNum = 0;
while(n<size&&Character.isDigit(s.charAt(n))){
tempNum = tempNum*10+s.charAt(n)-'0';
n++;
}
if(tempNum == 0) {tempNum = 1;};
if(map.get(s1)!=null) {
int nn = map.get(s1);
map.remove(s1);
map.put(s1, nn+tempNum);
}
else {map.put(s1, tempNum);}
}
}
return n+1;
}
public void multiply(Map<String,Integer> map,int nums) {//用来将map里的数据倍乘
List<String> list = new ArrayList<String>();
for(String name:map.keySet()) {
list.add(name);
}
while(0<list.size()) {
int num = map.get(list.get(0));
map.remove(list.get(0));
map.put(list.get(0), num*nums);
list.remove(0);
}
}
public void combine(Map<String,Integer> map1,Map<String,Integer> map2) {
for(String name:map2.keySet()) {
if(map1.get(name)!=null) {//如果map1包含map2里的name
int tempNum = map1.get(name);
map1.remove(name);
map1.put(name, map2.get(name)+tempNum);
}
else {
map1.put(name, map2.get(name));
}
}
}
不要在意递归的过程,不要深入,只需知道递归前于递归后即可。
感悟:
编程是一件坚持下去就会越来越有意思的事情,通过这道题更让我认识到,难的东西都只是刚开始,当我们持之以恒的越过那座山,便会领略到新的风景提升到新的意境!