【字符串】字符流中第一个不重复的字符

题目描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
返回值描述:如果当前字符流没有存在出现一次的字符,返回#字符。


哈希法

我想到的第一种方法是哈希法,用一个哈希表来存储每个字符的信息,字符作为key,value格式为min_index num,即字符最早出现的下标和它出现的次数。每次插入字符时,判断哈希表中是否包含该字符,若包含则将其出现的次数加一,否则将该字符加入哈希表中。使用result变量存储第一个出现的字符,若当前插入的字符和result相同或者result='#'则需要遍历哈希表找到第一个不重复字符

import java.util.HashMap;
public class Solution {
    
    
    String str = "";
    int index = 0; // 字符数量
    // value: char_index char_num
    HashMap<String, String> hashMap = new HashMap<>();
    String result = "";

    //Insert one char from stringstream
    public void Insert(char ch) {
    
    
        str += ch;
        String s = String.valueOf(ch);
        if (hashMap.containsKey(s)) {
    
    
            String value = hashMap.get(s);
            int num = Integer.parseInt(value.split(" ")[1]) + 1;
            hashMap.put(s, value.split(" ")[0] + " " + num);
        } else {
    
    
            hashMap.put(s, index + " " + 1);
        }
        index++;
        if (s.compareTo(result) == 0 || result.length() == 0 || result == "#") {
    
    
            // 上一次的结果需要修改
            result = "#";
            int min_index = Integer.MAX_VALUE;
            for (String key : hashMap.keySet()) {
    
    
                String value = hashMap.get(key);
                int ind = Integer.parseInt(value.split(" ")[0]);
                int num = Integer.parseInt(value.split(" ")[1]);
                if (num == 1 && ind < min_index) {
    
    
                    min_index = ind;
                    result = key;
                }
            }
        }
    }
    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
    
    
        return result.charAt(0);
    }
}

这个方法在时间上击败88%代码

优化方案1

在上面的方法的基础上,可以进行一些优化,上面方法的哈希表的值存储了字符最早出现的位置和它出现的次数,我们可以直接存储字符出现的次数,然后遍历字符串,判断字符的出现次数是否为1,若为1直接返回即可得到最早的不重复字符,就不需要存储字符的位置了。

插入字符时,存在两种情况,第一种是哈希表中不包含该字符,这是一个新字符,如果当前result='#',此时没有不重复的字符,则直接让新字符作为第一个不重复的字符,若已存在第一个不重复的字符,则不作任何处理,结果不变;第二种情况是哈希表中已经包含该字符,这是一个重复的字符,若这个重复的字符之前是result,则它不能再作为result,我们将result设为#,让程序重新遍历字符串寻找第一个不重复的字符。

import java.util.HashMap;

public class Solution {
    
    
    String str = "";
    // value: char_index char_num
    HashMap<Character, Integer> hashMap = new HashMap<>();
    char result = '#';

    //Insert one char from stringstream
    public void Insert(char ch) {
    
    
        str += ch;
        if (!hashMap.containsKey(ch)) {
    
    
        	// 这是一个新字符
            hashMap.put(ch, 1);
            if (result == '#')
                result = ch; // 此时没有不重复的字符,就将新字符作为result
        } else {
    
    
        	// 这是一个重复字符
            hashMap.put(ch, hashMap.get(ch) + 1);
            if (ch == result)
                result = '#'; // 如果它之前是第一个不重复的字符,那么现在它已经重复了
            if (result == '#') {
    
    
                for (int i = 0; i < str.length(); i++) {
    
    
                    if (hashMap.get(str.charAt(i)) == 1) {
    
    
                        result = str.charAt(i);
                        return;
                    }
                }
            }
        }
    }

    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
    
    
        return result;
    }
}

这个方法在时间上击败98%的代码

优化方案2

我们需要找到第一个不重复的字符,但是字符串中可能存在很多不重复的字符,所以我们除了找到不重复的字符,还需要存储字符的优先性(出现顺序),这里可以使用队列来存储不重复的字符,队列头就是第一个不重复的字符。此外还需要存储字符出现的次数,考虑到字符只有128个(),因此除了使用哈希表,也可以用数组来存储每个字符出现的次数,将字符的ASCII码作为数组下标。

Insert函数:插入字符时,将字符出现的次数加1,若该字符为新字符,则将其添加到队列中。

FirstAppearingOnce:在寻找第一个不重复字符时,遍历队列,判断对头元素的出现次数是否为1,若为1则直接返回,否则要将其从队列中移除,因为这个字符已经重复了,并且以后也不会再加入队列中。

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Queue;

public class Solution {
    
    
    Queue<Character> queue = new LinkedList<>();
    char count[] = new char[128]; // 存储字符出现的次数

    //Insert one char from stringstream
    public void Insert(char ch) {
    
    
        count[ch]++; // 字符出现次数加一
        if (count[ch] == 1)
            queue.add(ch); // 若这个字符只出现了一次,则把它添加到队列中
    }

    //return the first appearence once char in current stringstream
    public char FirstAppearingOnce() {
    
    
        char result = '#';
        while (!queue.isEmpty()) {
    
    
            if (count[queue.peek()] == 1)
                return queue.peek();
            queue.remove(); // 这是一个重复的字符,删除
        }
        return result;
    }
}

这个方法在时间上击败94%的代码,和方法二性能差不多,思路更加简洁巧妙

猜你喜欢

转载自blog.csdn.net/weixin_43486780/article/details/113867817