描述
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
后台会用以下方式调用Insert 和 FirstAppearingOnce 函数
示例
输入:"google" 返回值:"ggg#ll"
知识点:数组,队列,Map
难度:⭐⭐
题解
解题思路
这道题主要因为是关于找不重复的字符,因此可以通过 bitMap、哈希 等数据类型保存不重复字符,多个解法灵活计算。
还可以利用队列的先进先出的特性,对头只保存字符流中第一个只出现一次的字符
方法一:队列+bitMap
图解

算法流程:
- 使用整形数组保存出现频次,字符的ASCII码为下标,值为出现次数,队列保存bitMap中记录的未出现过的数组
- Insert 插入字符:
- 字符首次出现时,队列保存最先出现的不重复字符
- 重复出现的则从队列删除字符元素
- 获取第一个不重复字符:
- 队列为空,输出 ‘#’
- 队列不为空,输出对头元素。因为队头元素只保存第一个只出现一次的字符
Java 版本代码如下:
import java.util.*;
public class Solution {
// count数组为bitMap,字符的ASCII码为下标,值为出现次数
private int[] count = new int[128];
Queue<Character> queue = new LinkedList<>();
public void Insert(char ch) {
// 先判断是否0再计数
if(count[ch]++ == 0) {
// 队列保存最先出现的不重复字符
queue.offer(ch);
} else {
// 重复出现的则删除
queue.remove(Character.valueOf(ch));
}
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce() {
if(queue.isEmpty()) {
return '#';
}
// 队头元素只保存第一个只出现一次的字符
return queue.peek();
}
} 复杂度分析:
时间复杂度 O(1):数组和队列相关操作都是常数级别
空间复杂度 O(N):队列需要大小为N级别的空间
总结:
方法二:bitMap+index
算法流程:
- index用于记录某个字符出现的位置的指针
- bitMap的下标为字符的ASCII码,值为字符在字符流的下标,并初始化数组为-1,
- Insert:
- bitMap[ch] == -1,表示第一次出现,记录其在字符流中的位置,并更新下标指针
- bitMap[ch] >= 0,说明某个字符出现过了, 重置标志位
- 获取字符
- minIndex 保存当前插入的所有字符中,第一个不重复字符的下标
- 遍历每个可能的字符,bitMap[i]大于等于0且小于minIndex , 更新当前arr中不重复字符在字符流中的最小位置,获取结果字符
Java 版本代码如下:
public class Solution {
//index用于记录某个字符出现的位置
private int index = 0;
// 下标为字符的ASCII码,值为字符在字符流的下标
private int[] arr = new int[128];
public Solution(){
//构造函数初始化数组为-1,
for(int i=0;i<128;i++)
arr[i]=-1;
}
public void Insert(char ch) {
if(arr[ch]==-1) {
//第一次出现时,记录其在字符流中的位置,并更新下标指针
arr[ch]=index++;
}else if(arr[ch]>=0) {
//大于0,说明某个字符出现过了
//多次出现时,重置
arr[ch]=-2;
}
}
public char FirstAppearingOnce() {
//方便比较出最靠前的那个出现1次的字符
int minIndex = Integer.MAX_VALUE;
char ch='#';
for(int i=0;i<128;i++) {
// 表示ASCII为i的字符出现过
if(arr[i]>=0 && arr[i] < minIndex) {
ch = (char) i;
// 更新当前arr中不重复字符在字符流中的最小位置,即第一个不重复字符的下标
minIndex = arr[i];
}
}
return ch;
}
}
复杂度分析:
时间复杂度 O(1):数组相关操作都是常数级别,for循环也是常数级别操作
空间复杂度 O(1):bitMap数组都需要大小为常数级别128大小的空间
方法三:双bitMap
Java 版本代码如下:
public class Solution {
// 字符出现的次数
int[] count = new int[128];
// 字符出现的位置
int[] index = new int[128];
//用于标记字符流的位置
int number = 0;
public void Insert(char ch) {
count[ch]++;
index[ch] = number++;
}
public char FirstAppearingOnce() {
int minIndex = number;
char ch = '#';
for (int i = 0; i < 128; i++) {
if (count[i] == 1 && index[i] < minIndex) {
ch = (char) i;
minIndex = index[i];
}
}
return ch;
}
}复杂度分析:
时间复杂度 O(1):数组和队列相关操作都是常数级别
空间复杂度 O(1):bitMap数组都需要大小为常数级别128大小的空间
方法四:hash
Java 版本代码如下:
import java.util.Map;
import java.util.LinkedHashMap;
public class Solution {
Map<Character,Integer> map = new LinkedHashMap<>();
public void Insert(char ch){
if(map.containsKey(ch))
//value记录的是次数
map.put(ch,map.get(ch)+1);
else
//第一次出现
map.put(ch,1);
}
public char FirstAppearingOnce() {
//遍历,找到出现次数为1的
for(char ch:map.keySet()){
if(map.get(ch)==1)
return ch;
}
return '#';
}
}复杂度分析:
时间复杂度 O(N):Map的相关操作
空间复杂度 O(N):Map需要开辟空间

京公网安备 11010502036488号