字符串中找出第一个只出现一次的字符。比如说输入“abaccdeff“,则输出“b“。
最直观的想法是从头开始扫描这个字符串中的每个字符。当访问某个字符时拿这个字符和后面没个字符 做比较,如果在后面没有发现重复字符,则该字符就是要找的。如果字符串有n个字符,没个字符可能与后面O(n)个字符比较,所以这个思路的时间复杂度为O(n^2)。当然面试官时不会满意这种思路的,要换。
除此以外还可以统计每一个字符在该字符串中出现的次数。那么肯定就需要一个数据容器存放每隔字符的出现次数,而且在这个容器中可以根据字符来查找它出现的次数,就是说此容器的作用是把一个字符映射成一个数字,那么必然要使用到哈希表。
所以我们定义哈希表键值key是字符,值value是字符出现的次数。并且对字符串扫描两次:第一次每扫描到一个字符就在哈希表对应项中把次数加1;第二次每扫描到一个字符就能从哈希表中得到该字符出现的次数。那么第一个只出现一次的字符就是符合要求的输出了。
第一次扫描时,在哈希表中更新一个字符出现的次数的时间是 O(n) 。如果字符串长度为 n, 那么第一次扫描的时间复杂度是 O(n)。第二次扫描时,同样 0(1)能读出一个字符出现的次数,所以时间复杂度仍然是 O(n)。这样算起来,总的时间复杂度是 O(n)。
ok,清楚了这些思路之后就可以动手写代码了:
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
public class FirstNotRepeatChar {
public static void main(String[] args){
System.out.println(firstNotRepeatChar("google"));
System.out.println(firstNotRepeatChar("aabccdbd"));
System.out.println(firstNotRepeatChar("abcdefg"));
System.out.println(firstNotRepeatChar("gfedcba"));
System.out.println(firstNotRepeatChar("zgfedvba"));
// System.out.println(firstNotRepeatChar(null));
}
private static char firstNotRepeatChar(String s){
if(s==null||s.length()<1){
throw new IllegalArgumentException("Arg should not be null or empty");
}
/**
* 第一次扫描:只记录只出现一次的字符的位置。其他的value都记为-2
*/
Map<Character,Integer> map=new LinkedHashMap<Character,Integer>();
for(int i=0;i<s.length();i++){
char c=s.charAt(i);
if(map.containsKey(c)){
map.put(c, -2);//当出现一样的,当然就表示这个字符出现了两个或两个以上,可以直接置value为-2
}else{
map.put(c, i);//如果是第一次出现,那么记录下位置
}
}
/**
* 第二次扫描:找出第一个只出现一次的字符
*/
Set<Map.Entry<Character,Integer>> entrySet=map.entrySet();
//记录只出现一次的字符的索引
int index=Integer.MAX_VALUE;
//记录只出现一次的字符
char result='\0';//初始化
//找最小索引对应的字符
for(Map.Entry<Character, Integer> entry:entrySet){
if(entry.getValue()>=0&&entry.getValue()<index){
index=entry.getValue();
result=entry.getKey();
}
}
return result;
}
}
注意了,这里面的测试用例:
功能测试:字符串中存在只出现一次的字符,字符串中不存在只出现一次的字符,字符串中所有字符都只出现一次。
特殊输入测试:字符串为null。