【问题标题】:Grouping elements from lists into sub lists without duplicates in Java在Java中将列表中的元素分组到子列表中而不重复
【发布时间】:2019-09-21 10:28:28
【问题描述】:

我正在研究“分组字谜”。 问题陈述:给定一个字符串数组,将字谜组合在一起。

我可以对字谜进行分组,但我无法避免已经分组的字谜。我想避免重复。一个元素只能属于一个组。在我的代码中,一个元素属于多个组。

这是我的代码:

       public class GroupAnagrams1 {

           public static void main(String[] args) {
                 String[] input = {"eat", "tea", "tan", "ate", "nat", "bat"};
                 List<List<String>> result = groupAnagrams(input);
                 for(List<String> s: result) {
                      System.out.println(" group: ");
                            for(String x:s) {
                                System.out.println(x);
                            }
                   }
      }

      public static List<List<String>> groupAnagrams(String[] strs) {

            List<List<String>> result = new ArrayList<List<String>>();

            for(int i =0; i < strs.length; i++) {
                Set<String> group = new HashSet<String>();
                   for(int j= i+1; j < strs.length; j++) {
                       if(areAnagrams(strs[i], strs[j])) {
                            group.add(strs[i]);
                            group.add(strs[j]);
                     }
            }

                 if(group.size() > 0) {
                      List<String> aList = new ArrayList<String>(group); 
                      result.add(aList);
                 }
           }
      return result;


    }

这里是检查两个字符串是否是字谜的方法。

 private static boolean areAnagrams(String str1, String str2) {
         char[] a = str1.toCharArray();
         char[] b = str2.toCharArray();
        int[] count1 = new int[256];
        Arrays.fill(count1, 0);
        int[] count2 = new int[256];
        Arrays.fill(count2, 0);
        for(int i = 0; i < a.length && i < b.length; i++) {
           count1[a[i]]++;
           count2[b[i]]++;
         }
        if(str1.length() != str2.length())
              return false;
        for(int k=0; k < 256; k++) {
              if(count1[k] != count2[k])
                    return false;
        }
        return true;
      }
     }

预期输出:

 group: 
    tea
    ate
    eat
 group: 
    bat
 group: 
    tan
    nat

实际输出:

  group: 
     tea
     ate
     eat
  group: 
     tea
     ate
  group: 
     tan
     nat

组的显示顺序无关紧要。它的显示方式无关紧要。

偏好:请随意提交使用 HashMaps 的解决方案,但我更愿意看到不使用 HashMaps 和使用 Java8 的解决方案

【问题讨论】:

  • 查看您的if(areAnagrams(strs[i], strs[j])) 声明。如果有两个匹配项,您只会添加到组中。你永远不会在一个群体中得到“蝙蝠”。

标签: java


【解决方案1】:

我会在使用流时采取稍微不同的方法:

public class Scratch {
    public static void main(String[] args) {
        String[] input = { "eat", "tea", "tan", "ate", "nat", "bat" };

        List<List<String>> result = groupAnagrams(input);

        System.out.println(result);

    }

    private static List<List<String>> groupAnagrams(String[] input) {
        return Arrays.asList(input)
                     // create a list that wraps the array

                     .stream()
                     // stream that list

                     .map(Scratch::sortedToOriginalEntryFor)
                     // map each string we encounter to an entry containing
                     // its sorted characters to the original string

                     .collect(Collectors.groupingBy(Entry::getKey, Collectors.mapping(Entry::getValue, Collectors.toList())))
                     // create a map whose key is the sorted characters and whose
                     // value is a list of original strings that share the sorted
                     // characters: Map<String, List<String>>

                     .values()
                     // get all the values (the lists of grouped strings)

                     .stream()
                     // stream them

                     .collect(Collectors.toList());
                     // convert to a List<List<String>> per your req
    }

    // create an Entry whose key is a string of the sorted characters of original
    // and whose value is original
    private static Entry<String, String> sortedToOriginalEntryFor(String original) {
        char c[] = original.toCharArray();
        Arrays.sort(c);
        String sorted = new String(c);

        return new SimpleEntry<>(sorted, original);
    }
}

这会产生:

[[eat, tea, ate], [bat], [tan, nat]]

如果您想消除重复的字符串(例如,如果“bat”在您的输入中出现两次),那么您可以在您的Collectors.groupingBy 调用中调用toSet() 而不是toList(),并根据需要更改返回类型。

【讨论】:

    【解决方案2】:

    我也建议为此使用 java Streams。因为你不希望这是另一种解决方案:

    public static List<List<String>> groupAnagrams(String[] strs) {
        List<List<String>> result = new ArrayList<>();
        for (String str : strs) {
            boolean added = false;
            for (List<String> r : result) {
                if (areAnagrams(str, r.get(0))) {
                    r.add(str);
                    added = true;
                    break;
                }
            }
    
            if (!added) {
                List<String> aList = new ArrayList<>();
                aList.add(str);
                result.add(aList);
            }
        }
        return result;
    }
    

    您的解决方案中的问题是您将每次迭代都提前了一步,因此您只需生成不完整的组["tea", "ate"] 而不是["bat"]

    我的解决方案使用不同的方法来检查您是否有一个组,其中第一个单词是搜索单词的字谜。如果没有,请创建一个新组并继续。

    因为我会使用 Java 流,正如我在开头所说的,这里是我使用流的初始解决方案:

    List<List<String>> result = new ArrayList<>(Arrays.stream(words)
            .collect(Collectors.groupingBy(w -> Stream.of(w.split("")).sorted().collect(Collectors.joining()))).values());
    

    要生成排序的字符串键来对字谜进行分组,您可以查看here 以获取更多解决方案。

    结果是我提供的解决方案都是这样的:

    [[eat, tea, ate], [bat], [tan, nat]]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多