好,所以我有一个面试编码问题,该问题指出在一个循环中我必须找到一个非重复字符的第一个实例。因此,例如,如果字符串为“ abcab”,则由于a和b重复,它将返回c。
我有以下代码迭代整个字符串,并将出现在哈希图中的字符数输入,然后它起作用。
private static boolean findFirstCharacter(String s) {
HashMap<Character, Integer> map = new HashMap<Character, Integer>();
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(!map.containsKey(c)){
map.put(c,1);
}else{
int value = map.get(c);
value++;
map.put(c,value);
}
}
}
现在显然我可以再次迭代并找到第一个实例,该实例的键值为1,但是它必须是一个循环。考虑到我已有的东西还是有做错的方法吗?
答案 0 :(得分:5)
这是在单个循环中完成此操作的一种方法。
该方法可以处理来自补充平面的Unicode字符,并且已被修改为返回第一个非重复字符,而不是boolean
。
注意:该代码需要Java 8 +。
private static String findFirstCharacter(String s) {
Set<Integer> singles = new LinkedHashSet<>(), duplicates = new HashSet<>();
s.codePoints().forEach(ch -> {
if (! duplicates.contains(ch) && ! singles.add(ch)) {
singles.remove(ch);
duplicates.add(ch);
}
});
return (singles.isEmpty() ? null : new String(new int[] { singles.iterator().next() }, 0, 1));
}
测试
System.out.println(findFirstCharacter("abcab"));
System.out.println(findFirstCharacter("abcbca"));
System.out.println(findFirstCharacter(""));
输出
c
null
答案 1 :(得分:4)
关键是要使用LinkedHashSet
来保存所有唯一字符,该字符会记住插入顺序,因此可以检索第一个字符:
// better method name would be findFirstUniqueCharacter
private static char findFirstCharacter(String s) {
HashSet<Character> unique = new LinkedHashSet<>(), seen = new HashSet<>();
for(int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if(seen.add(c)) unique.add(c); else unique.remove(c);
}
return unique.isEmpty()? '_': unique.iterator().next();
}
seen.add(c)
仅在Set
中不包含该字符时才添加该字符,并返回是否已添加该字符。所有集合中经常被遗忘的合同会返回是否添加了元素,再加上Set
的合同会使其他contains
的支票过时。
因此,如果seen.add(c)
成功,则将字符添加到唯一字符集中,否则,将其删除。
在循环结束时,unique
包含按遇到顺序排列的所有其余唯一字符,因此我们可以简单地返回其第一个元素。
答案 2 :(得分:0)
棘手的部分是您需要过滤出多次出现的字符。
您计算发生次数的方法会在地图上进行第二次迭代以过滤结果。
您还不满足要求,因为您不记得字符的实际索引。
我会用这样的一个额外的集合来做到这一点(未经测试):
Collection<Character> seenCharacters = new HashSet<>();
Map<Character, Integer> map = new HashMap<>();
for(Character c : s.toCharArray()){
if(seenCharacters.contains(c)){ // at least second occurrence
map.remove(c);
} else { // first occurence
seenCharacters.add(c);
map.put(c,s.indexOf(c));
}
}
当然,您必须对结果图进行附加的迭代才能输出(由于System.out.println(map);
是隐藏的迭代)。
但是如何在没有附加循环的情况下识别第一个非重复字符? –霍尔格
对不起,错过了那部分。
我们将seenCharacters
从Collection
和HashSet
更改为List
和ArrayList
。
List<Character> seenCharacters = new ArrayList<>();
循环后,我们作弊:
seenCharacters.retainAll(map.keySet()); // hidden iteration
retun seenCharacters.isEmpty()?
' ':
seenCharacters.get(0);
但是,等等,我们不需要输出头寸,因此我们不需要Map
,它使操作变得更加容易...
List<Character> seenCharacters = new ArrayList<>();
List<Character> singleCharacters = new ArrayList<>();
for(Character c : s.toCharArray()){
if(seenCharacters.contains(c)){ // at least second occurrence
singleCharacters.remove(c);
} else { // first occurence
seenCharacters.add(c);
singleCharacters.add(c);
}
}
retun singleCharacters.isEmpty()?
' ':
singleCharacters.get(0);
答案 3 :(得分:0)
对此(可行的解决方案)可以做到(java 8必需)
private static boolean findFirstCharacter(String s) {
List<String> listChars = Arrays.asList(s.split(""));
Map<String, Long> map = listChars.stream().collect(Collectors.groupingBy(c -> c, Collectors.counting()));
for(String key : map.keySet()) {
if(map.get(key) == 1) {
System.out.println("Result: " + key);
return true;
}
}
return false;
}