问题
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:
s = “abaccdeff”
返回 “b”
s = “”
返回 " "
限制:
0 <= s 的长度 <= 50000
分析
- 使用哈希表(HashMap)来存储
要求的是:字符串中,第一个只出现一次的字符
=>对字符串进行遍历,统计其中的各个字符出现的次数,最后确认第一个只出现一次的字符,即可。
因为统计的时候,对容器的要求:既要确定该字符是什么,又要可以记录该字符出现的次数。并且,该容器的大小是不能确定的,最大为26(因为s只包含小写字母)。
==>想到了用集合来进行存储。
在集合中,list类的,与数组比较相似,不能完全满足要求;set类的也不能满足要求;=>map类的可以满足要求。
采用hashmap来作为容器,统计该字符串中每个元素出现的次数;
(1)时间复杂度:O(n)
=>在第一次扫描时,在哈希表中更新一个字符出现的次数的时间是O(1)。如果字符串的长度为n,那么,第一次扫描的时间复杂度是O(n)。
=>第二次扫描时,同样在O(1)时间内能读出一个字符出现的次数,所以时间复杂度仍是O(n)。
==>总的时间复杂度为:O(n)
(2)空间复杂度:O(1)
使用hashmap,构建起来的辅助数组的大小是一个常数,因此可以认为这种算法的空间复杂度为O(1)
- 使用“有序”哈希表(LinkedHashMap)来存储
要求的是:字符串中,第一个只出现一次的字符
=>仍旧采用集合来存储
==>只不过在存储某个字符(key)的出现次数时,对它的键值的设置处比较巧妙:
如果该字符是第一次出现,则put(item,1);
如果该字符不是第一次出现,则修改他的键值为-1:put(item,-1);
=>因为在第一次遍历string的时候,对于其中的字符(以key来存储)的键值的设置比较巧妙,所以第二趟遍历的时候,可以直接遍历linkedhashmap的对象(理论上可以比直接遍历string要快一点)
采用linkedhashmap的(map接口的)entrySet():返回所有的key-value构成的集合(用Set存储):
其中,出现的第一个:item.value != -1 的item对应的key,
即为:string中第一个只出现一次的字符。
原理:
LinkedHashMap:可以保证在遍历Map元素时,可以按照添加的顺序实现便利------>对于频繁的遍历操作,此类执行效果高于HashMap
1)时间复杂度:O(n)
=>在第一次扫描时,在哈希表中更新一个字符出现的次数的时间是O(1)。如果字符串的长度为n,那么,第一次扫描的时间复杂度是O(n)。
=>第二次扫描时,扫描的是建立起来的LinkedHashMap的对象,理论上会比直接遍历string得到结果的时间短。
==>总的时间复杂度为:O(n)
(2)空间复杂度:O(1)
使用linkedhashmap,构建起来的辅助数组的大小是一个常数,因此可以认为这种算法的空间复杂度为O(1)
- 用一维数组 来实现简单的“哈希表”
实现的思路为:
(1)string为空 的特殊情况
=>如果该string为空,直接return ’ ';
(2)string中包含只出现一次的字符
=>对string进行遍历,在遍历的过程中,对于string中的元素,将该字符对应的map[]中的值,+1;
=>对string进行第二遍遍历,如果某个字符c对应的的map[c-‘a’] == 1,表示该字符只出现了一次,return c;
(3)string中不包含只出现一次的字符
=>如果该string中不包含只出现一次的字符,return ’ ';
1)时间复杂度:O(n)
=>在第一次扫描时,在哈希表中更新一个字符出现的次数的时间是O(1)。如果字符串的长度为n,那么,第一次扫描的时间复杂度是O(n)。
=>第二次扫描时,同样在O(1)的时间内能读出一个字符出现的次数。
==>总的时间复杂度为:O(n)
(2)空间复杂度:O(1)
构建起来的辅助数组的大小是一个常数26*4bit=104bit,因此可以认为这种算法的空间复杂度为O(1)
- 用string类的方法来求解
实现
1、使用哈希表(HashMap)来存储
class Solution {
public char firstUniqChar(String s) {
//定义一个hashmap,来统计字符串中各个字符出现的次数
Map<Character,Integer> map = new HashMap<Character,Integer>();
char resChar = ' ';//因为如果没有 只出现一次的字符,要求返回一个但空格,所以将 一个但空格设置为resChar的初始值