题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).
这个题目既然是找是否出现,并且由于每个字符需要返回第一次出现,因此需要记录次数,可以使用数组来完成出现次数的统计,而数组可以改进成TreeSet,因为实现了排序,或者是使用Map集合来统计次数。
写法1:
利用数组统计次数,这里可以建一个二维数组,但是,考虑到ASCII码中,字符对应某个数字,所以可以将字符对应的数字当做数组的角标,因此,可以只用一个一维数组即可。(也就是说,当你写一个arr[a],是会被认成arr[97],而Map不行)
注意:ASCII码一共是127个数,而小写z对应的是122,因此数组可以设置的最小角标数为122,所以最小必须创建一个123的数组,也可以128,这样所有的字符都能统计(PS:有人用256我是没想到为什么,大家知道的可以告诉我)。
当然,也可以使用偏移量,毕竟A-Z对应的ASCII码为65-90,a-z对应的ASCII码值为97-122,用65到122这58个数也可以代替,所以使用偏移量需要做减法,然后创建大小58的数组即可。甚至可以用两次偏移,这样就只用大小52的数组了。但是为了少死一点脑细胞(我不想换算),我使用的是123个。不死脑细胞还占用空间较小的做法。
字符对应的数字,将该数字作为索引,该索引处的元素用来统计出现的次数,遍历完后,再进行一次遍历,找到次数只为1的,并返回该数组中索引所表示的字符。就是这么个逻辑。
public class Solution { public int FirstNotRepeatingChar(String str) { if(str==null || str.length() == 0) return -1; int[] count = new int[123]; //类似hash的方法来存储字符出现的次数 for(int i=0; i < str.length();i++) count[str.charAt(i)]++;//进行统计 //添加完字符后,再访问一遍数组,可以找到某个只出现一次的字符 for(int i=0; i < str.length();i++){ if(count[str.charAt(i)]==1) return i; //这里不能遍历count数组,因为遍历count数组只能返回出现1次的字符,或者是某个字符出现的次数 //只有再拿着原字符串数组的角标遍历,才能将角标和出现次数对应上 } return -1; } } //这个方法也可以改写成Set。只不过需要保证是有序的Set。
写法2:
使用Map集合来做,逻辑大致相同,只是添加的时候改动比较大。添加时,需要和Map的key比较,然后对Value进行维护。
import java.util.HashMap; public class Solution { public int FirstNotRepeatingChar(String str) { if(str.length() == 0|| str == null){ return -1; } HashMap<Character,Integer> map = new HashMap<>(); for(int i = 0; i < str.length();i++){ //Map的逻辑是,如果Map不包含这个key,那就添加,注意添加的是字符,和出现的次数(添加时默认是1) if(!map.keySet().contains(str.charAt(i))){ map.put(str.charAt(i),1); }else{//包含,则计数+1.map.get(str.charAt(i))就是拿到这个具体的次数即value map.put(str.charAt(i),map.get(str.charAt(i))+1); } } for(int i = 0; i < str.length();i++){//老规矩,只能对原字符串再次遍历 if(map.get(str.charAt(i)) == 1){ return i; } } return -1;//没找到就返回-1 } } //keySet() 返回一个 SetMap视图中包含的键。