最先想到的就是map散列的思想,在Java中,用HashMap,先遍历一次存下每个字符出现的次数,然后再弄一个循环,找到第一个值为1的key,然后查找这个key在字符串中的下标。
但是需要注意的是HashMap存储时是按hash值大小的顺序存的,对于输入顺序来说是无序的,没办法找到多个只出现一次的字符中,哪个是第一个出现的。
解决的办法就是在第二个循环中,不是遍历HashMap,而是遍历字符串,每取到一个字符就去HashMap中用get查找一次,这样就可以保证顺序输出,另外get操作的复杂度是常数,不用担心【外层遍历+内层查找】的复杂度超出O(N)的问题。
方案二:LinkedHashMap 是按输入顺序存储的,可以用来弥补HashMap无序存储的问题,这样在第二个循环中,就可以直接遍历 LinkedHashMap。
方案三(不成功):也是散列存储的思想,用int数组直接保存字符,会自动把字符转化为ASCII的int值,也是先遍历字符串,存每个字符出现的个数;第二个遍历用来取值,但是也会有一个顺序的问题,存储的顺序是按照字典序存的,同样没办法确定多个只出现一次的字符的先后顺序,并且没办法向方案1一样,【外层遍历字符串+内层查找数组】,因为这样时间复杂度就是O(NxN),不符合题目要求。
import java.util.*;
public class JZ50 {
/*// 方案三(不成功)
public static int FirstNotRepeatingChar(String str) {
int[] ids = new int[128]; // 注意开辟的空间为ASCII字符的个数,不是字符串的长度。因为字符会转化为ASCII的值
for (int i = 0; i < str.length(); i++) {
ids[str.charAt(i)] += 1;
}
for (int j = 0; j < 128; j++) {
if (ids[j] == 1) { // 会在多个只出现一次的字符中,先找到字典序最小的
return str.indexOf((char) j);
}
}
return -1;
}*/
// 方案二
public static int FirstNotRepeatingChar(String str) {
LinkedHashMap<Character, Integer> ids = new LinkedHashMap<>();
for (int i = 0; i < str.length(); i++) {
char key = str.charAt(i);
if (ids.containsKey(key)) {
ids.put(key, ids.get(key) + 1);
} else {
ids.put(key, 1);
}
}
// 迭代器遍历
Iterator<Map.Entry<Character, Integer>> iterator = ids.entrySet().iterator();
Map.Entry<Character, Integer> entry;
while (iterator.hasNext()) {
entry = iterator.next();
if (entry.getValue() == 1) {
return str.indexOf(entry.getKey());
}
}
return -1;
}
public static void main(String[] args) throws Exception {
String str = "google";
System.out.println(FirstNotRepeatingChar(str));
;
}
}