剑指 Offer 50. 第一个只出现一次的字符
在字符串 s
中找出第一个只出现一次的字符。如果没有,返回一个单空格
。 s
只包含小写字母。
示例:
s = “abaccdeff”
返回 “b”
[HTML_REMOVED]
s = “”
返回 ” “
限制:
0 <= s 的长度 <= 50000
解题思路
直接遍历一遍字符串,用Map
保存每个字符出现了多少次,再次遍历字符串,找到第一个只出现一次的字符即可。
Java代码
class Solution {
public char firstUniqChar(String s) {
//从头至尾遍历每个字符,用一个Map存好,key为字符,value为出现的次数
if(s == null || s.length() == 0) return ' ';
Map<Character,Integer> map = new HashMap<>();
for(int i = 0;i < s.length();i++){
map.merge(s.charAt(i),1,Integer::sum);//merge方法介绍在后面
}
for(int i = 0;i < s.length();i++){
if(map.get(s.charAt(i)) == 1){
return s.charAt(i);
}
}
return ' ';
}
}
merge方法介绍
处理映射时的一个难点就是更新映射项。正常情况下,可以很容易的得到一个键关联的原值,完成更新,再放回更新后的值。不过必须考虑一个特殊情况,即键第一次出现。比如类似上面的例子,我们需要使用一个映射统计一个单词在文件中出现的频度。看到一个单词(word
)时,我们将计数器增1
,学过C++
的朋友肯定觉得太简单了,直接counts[word]++
即可,因为counts
中没有为word
的键时,会自动添加该键。但Java
中就没有那么方便了,Java
中的做法如下所示:
counts.put(word,counts.get(word)+1);
这是可以的,不过有一种情况除外:就是word
是第一次出现时。在该情况下,get
会返回null
,因此会出现一个NullPointerException
异常。
避免该情况一个简单的做法是使用getOrDefault
方法:
counts.put(word,counts.getOrDefault(word,0) + 1);
当然也可以使用putIfAbsent
方法。
//如果传入的key已经有对应的value存在,返回存在的value,若不存在,则添加key和value,返回null
counts.putIfAbsent(word,0);
counts.put(word,counts.get(word) + 1);
但是还有更好的做法,merge
方法可以简化这个常见的操作。
counts.merge(word,1,Integer::sum);
如果键原先不存在,将把word
与1
关联,否则使用Integer.sum
函数组合原值和1
(也就是将原值与1
求和)。
merge
方法在下算法题和以及工作中还是很常用的,应该吃透。
下面贴一下Map
中merge
方法的源码:
default V merge(K key, V value,
BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
Objects.requireNonNull(remappingFunction);
Objects.requireNonNull(value);
V oldValue = get(key);
V newValue = (oldValue == null) ? value :
remappingFunction.apply(oldValue, value);
if(newValue == null) {
remove(key);
} else {
put(key, newValue);
}
return newValue;
}
这里的源码还是很简单的,基本不用解释了。
老哥,这个是因为使用的事HashMap吗,我看到使用LinkedMap不需要