剑指offer(36):第一个只出现一次的字符

来源:互联网 发布:东方财富mac 编辑:程序博客网 时间:2024/06/06 19:03

题目描述

在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符的位置。若为空串,返回-1。位置索引从0开始。

分析

直观思路:从头到尾扫描字符串,访问到某个字符是将其和后面的每个字符比较。如果没有发现重复字符,则只出现一次,即找到该字符。但是如果字符串有n个字符,则每个字符需要与后面的O(n)个字符比较,总的时间复杂度为O(n2)

推荐思路:只进行一趟扫描,时间复杂度为O(n),利用一个map容器存储字符及其出现的次数,另外一个map容器存储字符及其第一次出现的索引。扫描完成后,通过第一个map可以在O(1)时间内找到只出现1次的字符(利用LinkedHashMap,保持输出和输入顺序一致),接着可以找到该字符第一次出现的索引。总的时间复杂度为O(n)

牛客AC:

import java.util.LinkedHashMap;import java.util.Map;import java.util.Map.Entry;public class Solution {    public int FirstNotRepeatingChar(String str) {        if(str == null || str.length() <= 0)            return -1;        // LinkedHashMap 保证输出顺序与输入顺序一致        Map<Character, Integer> mapCount = new LinkedHashMap<Character, Integer>(); // 字符和对应的而次数        Map<Character, Integer> mapIndex = new LinkedHashMap<Character, Integer>(); // 字符和首次出现的索引        for(int i = 0; i < str.length(); i++) {            char c = str.charAt(i);            if(mapCount.containsKey(c)) {                int count = mapCount.get(c) + 1;                // mapCount.replace(c, count);  // replace java1.8才开始支持                mapCount.remove(c);                mapCount.put(c, count);            } else {                mapCount.put(c, 1);                mapIndex.put(c, i);            }        }        char resChar = '0';        int resIndex = -1;        for(Entry<Character, Integer> entry : mapCount.entrySet()) {            // 只出现一次的字符和首次出现的索引            if(entry.getValue() == 1) {                resChar = entry.getKey();                resIndex = mapIndex.get(resChar);                //System.out.println(resIndex);                break;            }        }        return resIndex; // 如需返回首次出现的索引,修改即可    }}

相关问题扩展

1、定义一个函数,输入两个字符串,从第一个字符串中删除在第二个字符串中出现过的所有字符。例如从第一个字符串“We are students”中删除在第二个字符串“aeiou”中出现过的字符得到“W r Stdnts”。
思路:使用HashMap存储第二个字符串,对第一个字符串进行一次扫描,每扫描到一个字符就判断map.containsKey(),如果出现,则删除该字符。

public String removeAppeared(String iniStr, String appearStr) {    if(iniStr == null || iniStr.length() <= 0)        return null;    if(appearStr == null || appearStr.length() <= 0)        return iniStr;    // map存储第二个字符串    Map<Character, String> map = new HashMap<Character, String>();    for(int i = 0; i < appearStr.length(); i++) {        char c = appearStr.charAt(i);        map.put(c, null);    }    // 判断是否出现过    StringBuffer sb = new StringBuffer();    for(int i = 0; i < iniStr.length(); i++) {        char c = iniStr.charAt(i);        if(map.containsKey(c))            continue;        sb.append(c);    }    return sb.toString();}

2、定义一个函数,删除字符串中所有重复出现的字符。例如输入“google”,删除重复字符之后的结果是“gole“。
思路:和上题类似,对字符串进行一趟扫描,每扫描到一个字符就判断map.containsKey(),如果出现,则删除该字符。

    public String removeRepeat(String iniStr) {        if(iniStr == null || iniStr.length() <= 0)            return null;        Map<Character, Boolean> map = new HashMap<Character, Boolean>();        StringBuffer sb = new StringBuffer();        for(int i = 0; i < iniStr.length(); i++) {            char c = iniStr.charAt(i);            if(map.containsKey(c)) {    // 如果map包含key,则该字符一定出现过                continue;            } else {                map.put(c, true);                sb.append(c);            }        }        return sb.toString();    }

3、在英语中,如果两个单词中出现的字母相同,并且每个字母出现的次数也相同,那么这两个单词互为变位词(Anagram)。例如silent与listen、evil与live等。请完成一个函数,判断输入的两个字符串是不是互为变位词。
思路:使用HashMap存储字符串中每个字符出现的次数。对第一个字符串扫描一次,每扫描一个字符,相应的字符的次数增加1;对第二个字符串扫描一次,每扫描一个字符,相应的字符的次数减去1。如果扫描完第二个字符后,HashMap中所有的值都是0,那么这两个字符串就是互为变位词。

public boolean isAnagram(String str1, String str2) {        if(str1 == null || str1.length() <= 0 ||                str2 == null || str2.length() <=0 || str1.length() != str2.length())            return false;        boolean isAnagram = false; // 返回值        // 遍历第一个字符串,存储字符及其次数        Map<Character, Integer> map = new HashMap<Character, Integer>();        for(int i = 0; i < str1.length(); i++) {            char c = str1.charAt(i);            if(map.containsKey(c)) {                int count = map.get(c) + 1; // 每次加1                map.remove(c);                map.put(c, count);            } else {                map.put(c, 1);            }        }        // 比那里第二个字符串,更新次数        for(int i = 0; i < str2.length(); i++) {            char c = str2.charAt(i);            if(map.containsKey(c)) {                int count = map.get(c) - 1; // 每次减1                map.remove(c);                map.put(c, count);            } else {                return isAnagram;            }        }        // 判断所有的字符的次数是否为0        for(Integer value : map.values()) {            if (value != 0)                 return isAnagram;        }        isAnagram = true;        return isAnagram;    }

参考
1. 何海涛,剑指offer名企面试官精讲典型编程题(纪念版),电子工业出版社

0 0