剑指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)
相关推荐
武子康2 小时前
Java-209 Spring AMQP 整合 RabbitMQ 实战:XML 配置直连交换机、RabbitAdmin 自动声明与收发闭环
xml·java·spring·rabbitmq·java-rabbitmq·java-activemq
崎岖Qiu3 小时前
【设计模式笔记19】:建造者模式
java·笔记·设计模式·建造者模式
SUPER52666 小时前
本地开发环境_spring-ai项目启动异常
java·人工智能·spring
moxiaoran57536 小时前
Spring AOP开发的使用场景
java·后端·spring
小王师傅6611 小时前
【轻松入门SpringBoot】actuator健康检查(上)
java·spring boot·后端
醒过来摸鱼11 小时前
Java classloader
java·开发语言·python
专注于大数据技术栈11 小时前
java学习--StringBuilder
java·学习
loosenivy11 小时前
企业银行账户归属地查询接口如何用Java调用
java·企业银行账户归属地·企业账户查询接口·企业银行账户查询
IT 行者12 小时前
Spring Security 6.x 迁移到 7.0 的完整步骤
java·spring·oauth2