题目:在字符串中找出第一个只出现一次的字符。如输入"abaccdeff",则输出'b'.
方法1,数组实现hashmap
在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符的位置。若为空串,返回-1。位置索引从0开始。
如果从头开始让每一个字符都与其后面的字符相比较,当第一个出现一次的字符,就结束程序。但是这种算法的时间复杂度是O(n^2);
思路:我们可以遍历统计每个字符在字符串中出现的次数,把它保存在数组中,可以利用哈希算法,让字符的ASCII码与数组的键值保持一定的关系,数组的值用来保存次数,这样就形成key-value对,根据字符的ASCII值就可以找到它它在数组中保存它出现的次数的位置。
题目中是全部由字母组成,大写字母的ASCII码在65-90,小写字母在97-122之间,一共有52个字母。
(1)创建一个数组大小为52的数组,来保存每个字母出现的次数;
(2)ASCII码值与数组索引之间的关系,
A(ASCII码65)保存在数组中0位置,即大写字母的ASCII码(设为m)与其在数组中的位置之间的关系:m-65
a(ASCII码97)保存在数组中26位置,即小写写字母的ASCII码(设为n)与其在数组中的位置之间的关系:n-71
(3)第一次遍历字符串,更新字母出现次数,每扫描到一个字符就在其数组中对应的位置将其次数+1;
(4)第二次遍历字符串,查找字母出现次数,每扫描到一个字符就查找器在数组中对应位置的值是否为1,找到第一个,将数组索引大小返回并退出程序。
方法2,直接hashmap
由于题目与字符出现的次数有关,我们是不是可疑统计每个字符在该字符串中出现的次数,要达到这个目的,我们需要一个数据容器来存放每个字符出现的次数。在这个容器中可以根据字符来查找它出现的次数,也就是说这个容器的作用就是把一个字符映射称一个数字。在常用的数据容器中,哈希表正是这个用途。
为了解决这个问题,我们可以定义哈希表的键值(key)是字符,而值(Value)是该字符出现的次数。同时我们还需要从头开始扫描字符串两次。第一次扫描字符串时,每扫描到一个字符就在哈希表中的对应项中把次数加1.接下来第二次扫描时,每扫描到一个字符就能从哈希表中得到该字符出现的次数。这样第一个只出现一次的字符就是符合要求的输出。
代码:
import java.util.LinkedHashMap;
public class Offer35 {
//方法1,数组实现哈希表
public int firstNotReaptingChar(String str){
int index = 0;
if(str == null){
return -1;
}
char [] charArray = str.toCharArray();
int [] count = new int[52];
for(int i = 0;i<charArray.length;i++){
if(charArray[i] >= 65 && charArray[i]<=90)
count[charArray[i]-65]++;
if(charArray[i]>=97&& charArray[i]<=122)
count[charArray[i]-71]++;
}
for(int i = 0;i<charArray.length;i++){
if(charArray[i]>=65 && charArray[i]<=90)
if(count[charArray[i]-65] == 1){
index = i;
break;
}
if(charArray[i]>=97 && charArray[i]<=122)
if(count[charArray[i]-71] == 1){
index = i;
break;
}
}
return index;
}
/*
* LinkedHashMap就闪亮登场了,它虽然增加了时间和空间上的开销,但是通过维护一个运行于所有条目的双向链表,
* LinkedHashMap保证了元素迭代的顺序。该迭代顺序可以是插入顺序或者是访问顺序。
*/
//方法2
public Character firstNotReaptingChar_2(String str){
if(str == null)
return null;
char [] strChar = str.toCharArray();
LinkedHashMap<Character, Integer> map = new LinkedHashMap<Character, Integer>();
for(char item : strChar){
if(map.containsKey(item))
map.put(item, map.get(item)+1);
else
map.put(item, 1);
}
for(char key:map.keySet()){
if(map.get(key) == 1)
return key;
}
return null;
}
public static void main(String[] args) {
Offer35 of35 = new Offer35();
//功能测试,1,字符串中存在只出现一次的字符
String str1 = "abaccdeff";
System.out.println("测试1,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar(str1));
System.out.println("测试1,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar_2(str1));
//功能测试,2,字符串中不存在只出现一次的字符
String str2 = "aaab";//abacbdcdeeff
System.out.println("测试2,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar(str2));
System.out.println("测试2,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar_2(str2));
//功能测试,3,字符串中所有字符只出现一次
String str3 = "abcDFRG";//abacbdcdeeff
System.out.println("测试3,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar(str3));
System.out.println("测试3,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar_2(str3));
//特殊输入测试,4,字符串为NULL指针
String str4 = null;//abacbdcdeeff
System.out.println("测试4,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar(str4));
System.out.println("测试4,第一个只出现一次的字符的位置为: "+of35.firstNotReaptingChar_2(str4));
}
}
运行结果:
测试1,第一个只出现一次的字符的位置为: 1
测试1,第一个只出现一次的字符的位置为: b
测试2,第一个只出现一次的字符的位置为: 3
测试2,第一个只出现一次的字符的位置为: b
测试3,第一个只出现一次的字符的位置为: 0
测试3,第一个只出现一次的字符的位置为: a
测试4,第一个只出现一次的字符的位置为: -1
测试4,第一个只出现一次的字符的位置为: null