字符流中第一个不重复的字符
字符流中第一个不重复的字符
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
这道题的类初始结构一开始我没看懂:
public class Solution {
//Insert one char from stringstream
public void Insert(char ch)
{
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
}
}
其实意思是,每次字符输入流都会调用insert进行输入,然后在不知道什么时候会调用FirstAppearingOnce()输出第一个不重复的字符。这道题的一个点是我们只提供方法供调用,并不知道什么时候调用,所以要处理各种可能的调用情况。
时间复杂度O(1),空间复杂度O(1):无须存储所有字符
这道题目的大致思路其实都差不多,只不过看了许多答案,发现都是存储了所有字符,然后再进行遍历判断
其实并不需要这样,这样子 HR 哪里会给 Offer 呢? 用户 txlstars 的回答和本文的优化相同(绝对不是面向 Ctrl+C 编程的~)字符出现次数的判断(不重复字符):
这个做法大致相同,利用 Hash 思想采用128大小的计数数组进行计数也好,或者是使用 Map 键值对映射也好,都差不多,使用数组会更简单。字符出现顺序的判断(第一个字符):
这里就是改进的关键之处了,容易发现,字符流中不重复的字符可能同时存在多个,我们只要把这些 “不重复字符” 保存起来就可以,而无需保存那些重复出现的字符,而为了维护字符出现的顺序,我们使用队列(先进先出)这一结构,先出现的不重复字符先输出:
- 入队:获取字符流中的一个字符时,当我们判断它是不重复时,将它加入队列;
- 输出/出队:注意,因为队列中存储的 “不重复字符” 在一系列的流读取操作后,随时有可能改变状态(变重复),所以,队列中的字符不能直接输出,要先进行一次重复判断,如果发现队头字符已经重复了,就将它移出队列并判断新的队头,否则,输出队头的值;
import java.util.Queue; import java.util.LinkedList; import java.lang.Character; public class Solution { int[] charCnt = new int[128]; Queue<Character> queue = new LinkedList<Character>(); //Insert one char from stringstream public void Insert(char ch) { if (charCnt[ch]++ == 0) //新来的单身字符,入队 queue.add(ch); } //return the first appearence once char in current stringstream public char FirstAppearingOnce() { Character CHAR = null; char c = 0; //这里只能查看,不能弹出来,因为可能要多次查看这一个字符,只有判断它不再是不重复字符才能弹出来 while ((CHAR = queue.peek()) != null) { c = CHAR.charValue(); if (charCnt[c] == 1) //判断是否脱单了,没脱单则输出 return c; else queue.remove(); //脱单了就移出队列,它不会再回来了 } return '#'; //队空,返回# } }
这道题的解法作者说的时间和空间都是O(1)的,想一想确实是这样。先看空间,charCnt 的大小是确定的,queue中存放的数量也不会超过128(因为一旦重复就不会入队列了,所以最多把128个都放进去)。时间的话,insert是O(1)的,而FirstAppearingOnce最多每次把queue都查一遍,但是queue大小的上限是确定的,所以它的时间消耗上限也是确定的。
很巧妙的思路,它的主要优化是在,我们如果直接使用哈希表的话,最后会遇到查找当前第一个只出现一次的字符的问题,这如果直接做的话只能遍历字符串,那这就是O(n)的了,但是这里使用一个队列将曾经的第一个只出现一次的字符都存起来,调用的时候直接检验有效性。因为这个队列和哈希都是依赖于元素的有限性的,所以哈希能用的地方这个队列都可以用,挺好的方法。
另外,我们默认这里说的字符就是ASCII码,一共128个,值从0到127.否则没办法算了,UTF-8一共有一百多万个
-
java的char和int也是可以直接转换的
-
一开始我写的代码是这样:
while(hash[te]>1 &&!firstChar.isEmpty()){ firstChar.poll(); te=firstChar.peek(); }这里就有个问题,比如队列只有一共元素,那poll之后再peek就是空指针了,所以应该改写为这样,在poll之后再判断是否为空,最一开始也先判断一下是否为空:
if(firstChar.isEmpty()){ return '#'; } Character te=firstChar.peek(); while(hash[te]>1){ firstChar.poll(); if(!firstChar.isEmpty()) te=firstChar.peek(); else return '#'; }

浙公网安备 33010602011771号