面試題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;
}
}