简体   繁体   English

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

[英]Is it possible to find the first instance of a value from a Hashmap within the same loop?

Ok so I have an interview coding problem, and the problem states that in one loop I must find the first instance of a non repeating character. 好的,所以我有一个面试编码问题,该问题指出在一个循环中我必须找到一个非重复字符的第一个实例。 So for example if the string was "abcab" it would return c since a and b repeat. 因此,例如,如果字符串为“ abcab”,则由于a和b重复,它将返回c。

I have the following which iterates through the entire string, and inputs the amount of characters that occur into a hash map, and it works. 我有以下代码遍历整个字符串,并将出现在哈希图中的字符数输入,并且可以正常工作。

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


            }



        }


}

Now obviously I can just iterate again and find the first instance where the key has a value of 1, but It must be one loop. 现在显然我可以再次迭代并找到第一个实例,该实例的键的值为1,但是它必须是一个循环。 Is there anyway to do this given what I have or am I approaching this incorrectly? 鉴于我已有的东西还是有做错的方法吗?

Here is one way to do it in a single loop. 这是在单个循环中执行此操作的一种方法。

The method can handle Unicode characters from the supplementary planes, and has been modified to return the first non repeating character, instead of a boolean . 该方法可以处理来自补充平面的Unicode字符,并且已被修改为返回第一个非重复字符,而不是boolean

Note: The code requires Java 8+. 注意:该代码需要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));
}

Test 测试

System.out.println(findFirstCharacter("abcab"));
System.out.println(findFirstCharacter("abcbca"));
System.out.println(findFirstCharacter("😀😈😍😀😈"));

Output 输出量

c
null
😍

The key point is to use a LinkedHashSet to hold all unique characters, which remembers the insertion order, hence, allows to retrieve the first one: 关键是使用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) only adds the character if it is not contained in the Set and returns whether it has added it. seen.add(c)仅在字符Set不包含字符的情况下添加字符,并返回是否已添加字符。 It's the often forgotten contract of all collections to return whether they have added the element, which together with the contract of Set makes additional contains checks obsolete. 它是所有集合中经常被遗忘的合同,用于返回是否添加了元素,这与Set的合同一起使其他contains检查变得过时。

So if seen.add(c) was successful, we add the character to the set of unique characters, otherwise, we remove it. 因此,如果seen.add(c)成功,则将字符添加到唯一字符集中,否则将其删除。

At the end of the loop, unique contains all remaining unique characters in the encounter order, so we can simply return its first element. 在循环结束时, unique包含按遇到顺序排列的所有其余唯一字符,因此我们可以简单地返回其第一个元素。

The tricky part is that you need to filter out chracters with multiple occurence. 棘手的部分是您需要过滤出多次出现的字符。

Your approach of counting the occurrences forces a second iteration over the map to filter the results. 您计算发生次数的方法会强制在地图上进行第二次迭代以过滤结果。

Also you don't meet the requierment since you don't remember the actual index of the character. 另外,由于您不记得字符的实际索引,因此您不符合要求。

I would do this with an extra collection like that (not tested): 我会用这样的一个额外的集合来做到这一点(未经测试):

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

Of cause you have to do an aditional iteration over the result map for output (since System.out.println(map); is a hidden iteration). 当然,您必须对结果图进行附加的迭代才能输出(因为System.out.println(map);隐藏的迭代)。


But how do you identify the first nonrepeating character without an additional loop? 但是,如何在没有附加循环的情况下识别第一个非重复字符? – Holger –霍尔格

Sorry, missed that part. 抱歉,错过了那部分。

to get that we change seenCharacters from Collection and HashSet to List and ArrayList . 得到的结果是,我们将seenCharactersCollectionHashSet更改为ListArrayList

 List<Character> seenCharacters = new ArrayList<>();

After the loop we cheat: 循环后,我们作弊:

 seenCharacters.retainAll(map.keySet()); // hidden iteration
 retun seenCharacters.isEmpty()?
            ' ':
            seenCharacters.get(0); 

But wait, we are not required to output the position, so we do not need the Map which makes it much easier ... 但是,等等,我们不需要输出位置,因此我们不需要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); 

A possible and elegant solution could be done with this (required java 8) 可以用这种方法完成一个优雅的解决方案(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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM