是否可以在同一循环中从Hashmap中找到值的第一个实例?

时间:2018-07-10 14:04:25

标签: java hashmap

好,所以我有一个面试编码问题,该问题指出在一个循环中我必须找到一个非重复字符的第一个实例。因此,例如,如果字符串为“ 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,但是它必须是一个循环。考虑到我已有的东西还是有做错的方法吗?

4 个答案:

答案 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);隐藏的迭代)。


  

但是如何在没有附加循环的情况下识别第一个非重复字符? –霍尔格

对不起,错过了那部分。

我们将seenCharactersCollectionHashSet更改为ListArrayList

 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;
}