剑指offer-54、字符流中第一个不重复的字符

题⽬描述

请实现⼀个函数⽤来找出字符流中第⼀个只出现⼀次的字符。例如,当从字符流中只读出前两个字符" go "时,第⼀个只出现⼀次的字符是" g "。当从该字符流中读出前六个字符" google "时,第⼀个只出现⼀次的字符是" l "。

返回值描述:如果当前字符流没有存在出现⼀次的字符,返回 # 字符。

思路及解答

有序哈希表

可以直接使用哈希的数据结构来存取我们的字符,对与重复的字符可以对值进行统计或者标记都行。这里要用LinkedHashMap,因为题目要求到了要出现的第一个不重复的字符,所以如果不使用有序map的话,那么我们就不能保证取到的是第一个不重复的字符。

java 复制代码
public class Solution {
    //Insert one char from stringstream
    //因为后面要遍历保证有序,所以这里使用LinkedHashMap
    Map<Character,Integer> map = new LinkedHashMap<>();
    
    public void Insert(char ch){
        if(map.containsKey(ch)){
            map.put(ch,-1);
        }else{
            map.put(ch,1);
        }
    }
  //return the first appearence once char in current stringstream
    public char FirstAppearingOnce(){
        for(Character i : map.keySet()){
            if(map.get(i) == 1){
                return i;
            }
        }
        return '#';
    }
}
  • 时间复杂度:插入O(1),查询最坏O(n)
  • 空间复杂度:O(n)

队列+计数数组(最优)

结合了队列的先进先出特性和数组的快速访问能力,能够高效解决动态字符流中的首个不重复字符查找问题

java 复制代码
public class Solution {
    private int[] count = new int[128];  // ASCII字符计数数组
    private Queue<Character> queue = new LinkedList<>();  // 维护候选字符顺序
    
    // 插入字符到流中
    public void Insert(char ch) {
        count[ch]++;  // 字符出现次数加1
        queue.add(ch);  // 字符加入队列
        
        // 清理队列头部已重复的字符
        while (!queue.isEmpty() && count[queue.peek()] > 1) {
            queue.poll();
        }
    }
    
    // 返回当前第一个不重复字符
    public char FirstAppearingOnce() {
        return queue.isEmpty() ? '#' : queue.peek();
    }
}
  • 时间复杂度:每个字符的插入操作是均摊O(1),查询操作是严格的O(1)
  • 空间复杂度:O(1)(固定大小的数组和队列)

双数组记录

通过记录字符首次出现的位置和状态,在查询时进行全局扫描

java 复制代码
public class Solution {
    private int[] position = new int[128];  // 记录字符位置或状态
    private int index = 1;  // 当前字符位置索引
    
    public Solution() {
        // 初始化,-1表示未出现过
        for (int i = 0; i < 128; i++) {
            position[i] = -1;
        }
    }
    
    public void Insert(char ch) {
        if (position[ch] == -1) {
            // 第一次出现,记录位置
            position[ch] = index;
        } else if (position[ch] > 0) {
            // 重复出现,标记为-2
            position[ch] = -2;
        }
        index++;
    }
    
    public char FirstAppearingOnce() {
        int minIndex = Integer.MAX_VALUE;
        char result = '#';
        
        // 扫描找到最小正整数值对应的字符
        for (int i = 0; i < 128; i++) {
            if (position[i] > 0 && position[i] < minIndex) {
                minIndex = position[i];
                result = (char) i;
            }
        }
        return result;
    }
}
  • 时间复杂度:插入O(1),查询O(1)(固定128次循环)
  • 空间复杂度:O(1)
相关推荐
之歆5 小时前
Spring AI入门到实战到原理源码-MCP
java·人工智能·spring
yangminlei5 小时前
Spring Boot3集成LiteFlow!轻松实现业务流程编排
java·spring boot·后端
qq_318121595 小时前
互联网大厂Java面试故事:从Spring Boot到微服务架构的技术挑战与解答
java·spring boot·redis·spring cloud·微服务·面试·内容社区
J_liaty5 小时前
Spring Boot整合Nacos:从入门到精通
java·spring boot·后端·nacos
阿蒙Amon6 小时前
C#每日面试题-Array和ArrayList的区别
java·开发语言·c#
daidaidaiyu6 小时前
Spring IOC 源码学习 一文学习完整的加载流程
java·spring
2***d8857 小时前
SpringBoot 集成 Activiti 7 工作流引擎
java·spring boot·后端
五阿哥永琪7 小时前
Spring中的定时任务怎么用?
java·后端·spring
gelald7 小时前
AQS 工具之 CountDownLatch 与 CyclicBarry 学习笔记
java·后端·源码阅读
better_liang7 小时前
每日Java面试场景题知识点之-XXL-JOB分布式任务调度实践
java·spring boot·xxl-job·分布式任务调度·企业级开发