面试题50. 第一个只出现一次的字符
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
思路一: 暴力法:
- 时间: O(N^2)
- 空间:O(N)
public class Solution {
public int FirstNotRepeatingChar(String str) {
// 暴力法
int[] ans = new int[str.length()];
for(int i = 0; i < str.length(); i++){
for(int j = i+1; j < str.length(); j++){
if(str.charAt(i) == str.charAt(j)) {
ans[i] += 1;
ans[j] += 1;
}
}
}
boolean flag = false;
int pos;
for(pos = 0; pos < str.length(); pos++){
if(ans[pos] == 0) {
flag = true;
break;
}
}
if(flag == true) return pos;
else return -1;
}
}
思路二: 哈希表HashMap
步骤:
方法一的暴力法是两层循环,用于判断某字符后面是否有相同的字符; 可采用HashMap的键值判断是否重复(值为出现次数)
- 遍历字符串 s ,使用哈希表统计 “各字符数量是否 >1 ”。
- 再遍历字符串 s ,在哈希表中找到首个 “数量为 1 的字符”,并返回下标。
优化
键 - 值,可将值改为true(只出现一次) false(重复);(hashMap 结构的 Value 使用 Boolean 类型是一个更好的选择,原因如下:
- 一个字母出现的次数大于 1 次就不符合要求了,这个时候使用 Fasle 标记状态相对于 Integer 的不断递增更合理,也更省空间。
- 布尔值可以用来判断,可以简化代码逻辑。
复杂度
- 时间: O(N) N 为字符串 s 的长度;需遍历 s 两轮,使用 O(N) ;HashMap 查找的操作复杂度为 O(1);
- 空间: O(N) HashMap 需存储 N 个字符的键值对,使用 O(N)大小的额外空间。
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
//哈希表
HashMap<Character, Boolean> dic = new HashMap<>();
for(int i = 0; i < str.length(); i++){
if(!dic.containsKey(str.charAt(i))) dic.put(str.charAt(i), true);
else dic.put(str.charAt(i), false);
}
int pos = -1;
for(int i = 0; i < str.length(); i++){
if(dic.get(str.charAt(i))){
pos = i;
break;
}
}
return pos;
}
}
思路三 优化内存
优化思路:
按照ASCII的值进行保存,开辟固定大小的内存,英文字母的ascii为 a-z:97-122, A-Z:65-90, 0-9:48-57。,所以数组大小设置为58个即可(不是52是想省去判断大小写的麻烦)。
复杂度
- 时间: O(n)
- 空间: O(58). O(1)?
import java.util.HashMap;
public class Solution {
public int FirstNotRepeatingChar(String str) {
// 优化内存~ 数组代替哈希表
// 题目已知全是英文字母,则最多有58个字符,建立一个长度为58的数组,其ASCII码作为数组的位置索引
int[] res = new int[58];
for(int i = 0; i < str.length(); i++){
res[str.charAt(i) -'A']++;
}
for(int i = 0; i < str.length(); i++){
if(res[str.charAt(i) - 'A'] == 1) return i;
}
return -1;
}
}