题目及用例
package pid387;
/* 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
案例:
s = "leetcode"
返回 0.
s = "loveleetcode",
返回 2.
*/
public class main {
public static void main(String[] args) {
String [] testTable = {"leetcode","A man, a plan, a canal: Panama","loveleetcode"};
for (String ito : testTable) {
test(ito);
}
}
private static void test(String ito) {
Solution solution = new Solution();
int rtn;
long begin = System.currentTimeMillis();
System.out.print(ito);
System.out.println();
//开始时打印数组
rtn= solution.firstUniqChar(ito);//执行程序
long end = System.currentTimeMillis();
System.out.println(":rtn" );
System.out.print(rtn);
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法一(成功,54ms,速度一般)
使用hashmap,以character为key,index为value,一旦出现重复key,设value为-1
最后遍历hashmap,设min为length,如果value不为1,且比min小,则min=value
最后如果min还等于length,则说明全部重复,设min=-1
package pid387;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map.Entry;
public class Solution {
public int firstUniqChar(String s) {
int length=s.length();
if(length==0){
return -1;
}
if(length==1){
return 0;
}
HashMap<Character, Integer> map=new HashMap<>();
for(int i=0;i<length;i++){
Character now=s.charAt(i);
if(!map.containsKey(now)){
map.put(now, i);
}
else{
map.put(now, -1);
}
}
int min=length;
for(Character key: map.keySet()){
int now=map.get(key);
if(now!=-1){
if(now<min){
min=now;
}
}
}
if(min==length){
min=-1;
}
return min;
}
}
解法二(别人的)
用linkedhashmap可以最后不用循环找第一个,直接找到
用一个LinkedHashMap实现,key为字母,value如果第一次插入存入索引,再次插入存-1,然后遍历HashMap,找到第一个不是-1的value(LinkedHashMap保证了插入顺序,所以找到的一定是第一个唯一字
解答:
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> sCount = new LinkedHashMap<Character, Integer>();
for (int i = 0; i < s.length(); i++) {
char letter = s.charAt(i);
if (sCount.containsKey(letter)) {
sCount.put(letter, -1);
}
else {
sCount.put(letter, i);
}
}
Iterator iter = sCount.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<Character, Integer> entry = (Map.Entry)iter.next();
if (entry.getValue() == -1) {
continue;
}
return entry.getValue();
}
return -1;
}
}
解法三(成功,20ms,较快)
这个题目,由于这里只有小写字母且ASCII码是连续的,于是可以把数组当作hash表用,0为初始值,正数为普通index+1,如果有多个则为integer.max
先遍历一遍字符串,在遍历一遍数组,找到有一个而且是最初的index
public int firstUniqChar(String s) {
//里面每格对应一个小写字母,0对应a,25对应z
//里面的值为对应字母的位置+1,0为初始值,正数为普通index+1,如果有多个则为integer。max
int[] index=new int[26];
for(int i=0;i<s.length();i++){
int nowIndex=index[s.charAt(i)-'a'];
if(nowIndex==0){
//这说明之前没有过这个字母
index[s.charAt(i)-'a']=i+1;
continue;
}
//如果不为0,则说明之前已经有过了,直接变成max
index[s.charAt(i)-'a']=Integer.MAX_VALUE;
}
int result=Integer.MAX_VALUE;
for(int i=0;i<26;i++){
if(index[i]>0&&index[i]<Integer.MAX_VALUE){
//这说明有这个字母,而且只出现一次
//找到最小的下标
if(result>index[i]){
result=index[i];
}
}
}
if(result==Integer.MAX_VALUE){
//这说明都重复了
return -1;
}
else{
//result存储的是index+1
return result-1;
}
}
解法4(别人的)
算法的思路就是遍历一遍字符串,然后把字符串中每个字符出现的次数保存在一个散列表中。这个过程的时间复杂度为 O(N),其中 N 为字符串的长度。
接下来需要再遍历一次字符串,这一次利用散列表来检查遍历的每个字符是不是唯一的。如果当前字符唯一,直接返回当前下标就可以了。第二次遍历的时间复杂度也是 O(N)。
class Solution {
public int firstUniqChar(String s) {
HashMap<Character, Integer> count = new HashMap<Character, Integer>();
int n = s.length();
// build hash map : character and how often it appears
for (int i = 0; i < n; i++) {
char c = s.charAt(i);
count.put(c, count.getOrDefault(c, 0) + 1);
}
// find the index
for (int i = 0; i < n; i++) {
if (count.get(s.charAt(i)) == 1)
return i;
}
return -1;
}
}
复杂度分析
时间复杂度: O(N)只遍历了两遍字符串,同时散列表中查找操作是常数时间复杂度的。
空间复杂度: O(N) 用到了散列表来存储字符串中每个元素出现的次数。