【问题标题】:Partition an Array with duplicate elements into arrays with unique elements将具有重复元素的数组划分为具有唯一元素的数组
【发布时间】:2015-01-26 06:27:46
【问题描述】:

我有一个结构如下的数组:

String Array = {"1","2","3","41","56","41","72","72","72","78","99"}

我想将这个数组划分为多个数组,这些数组的值不重复......就像这样:

String Array1 = {"1","2","3","41","56","72","78","99"}
String Array2 = {"41","72"}
String Array3 = {"72"}

在 Java 中有没有直接的方法可以做到这一点,或者我必须用丑陋的循环来做到这一点(开个玩笑!)?

谢谢!

更新

我会让这个问题变得更难一些......现在我有一个结构如下所示的地图:

Map<String,String> map = new HashMap(){{
    put("1@@96","10");
    put("2@@100","5");
    put("3@@23","100");
    put("41@@34","14");
    put("56@@22","25");
    put("41@@12","100");
    put("72@@10","100");
    put("72@@100","120");
    put("72@@21","0");
    put("78@@22","7");
}}

请注意,值并不重要,但键很重要... 我该怎么做才能将此地图划分为类似的子地图:

Map map1 = {"1@@96" => "10"
            "2@@100" => "5"
            "3@@23" => "100"
            "41@@34" => "14"
            "56@@22" => "25"
            "72@@10" => "100"
            "78@@22" => "7"
            }

Map map2 = {
            "41@@12" => "100"
            "72@@100" => "120"
            }

Map map3 = {
            "72@@100" => "120"
            }

就像在地图的第一部分之前('@@'之前)是我希望唯一性所基于的 ID...这就像数组示例但有点困难和复杂...

抱歉中途换了个问题……

【问题讨论】:

  • 丑陋的循环是什么意思?您可以遍历原始列表并将数字放入适当的结果列表中,方法是跟踪给定数字的出现次数和 N 指针,每个结果列表一个,以知道将下一个数字放在每个列表中的位置.不过,这需要对初始数组进行排序。
  • 我正在寻找 JDK 或其他库中的方法...但如果遇到最坏的情况,我一定会考虑您的建议...谢谢...
  • 为什么Array1有8个item,Array2有2个items,Array3有1个?这背后有什么逻辑吗?
  • Array1 中的项目在Array 中仅出现一次,Array2 中的项目出现两次,等等
  • 更新版本和原始版本完全没有区别,您仍然可以使用以下答案中提供的解决方案,因为您可以将地图视为 条目的数组。

标签: java arrays unique duplicate-removal


【解决方案1】:

库中可能什么都没有(似乎不够通用),但有一些想法:

O(n) 时间和 O(n) 空间复杂度。在这里,您只需计算每个数字出现的次数,然后将它们放入那么多结果数组中。

@Edit:正如@mpkorstanje 指出的那样,如果在最坏的情况下将输入从数字更改为字符串或任何其他对象,这将降级为 O(n^2)。但在这种情况下,您应该针对您正在处理的数据修改哈希恕我直言,因为它分布不均。

   public List<List<Integer>> split(int[] input) {
      Map<Integer, Integer> occurrences = new HashMap<>();
      int maxOcc = 0;
      for (int val : input) {
         int occ = 0;
         if (occurrences.containsKey(val)) {
            occ = occurrences.get(val);
         }
         if (occ + 1 > maxOcc) {
            maxOcc = occ + 1;
         }
         occurrences.put(val, occ + 1);
      }
      List<List<Integer>> result = new ArrayList<>(maxOcc);
      for (int i = 0; i < maxOcc; i++) {
         result.add(new LinkedList<>());
      }
      for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
         for (int i = 0; i < entry.getValue(); i++) {
            result.get(i).add(entry.getKey());
         }
      }
      return result;
   }

O(nlogn) 时间和 O(1) 空间复杂度(不计算结果数组)但不保留顺序并“破坏”输入数组。在这里,您利用了数组已经排序的事实,因此您可以遍历它并继续将元素添加到适当的结果列表中,具体取决于您查看的是重复条目还是“新”条目。

   public List<List<Integer>> split(int[] input) {
      Arrays.sort(input);
      int maxDup = getMaxDuplicateNumber(input);
      List<List<Integer>> result = new ArrayList<>(maxDup);
      for(int i = 0; i < maxDup; i++) {
         result.add(new LinkedList<>());
      }
      int count = 0;
      result.get(0).add(input[0]);
      for(int i = 1; i < input.length; i++) {
         if(input[i] == input[i-1]) {
            count++;
         } else {
            count = 0;
         }
         result.get(count).add(input[i]);
      }
      return result;
   }

   private int getMaxDuplicateNumber(int[] input) {
      int maxDups = 1;
      int currentDupCount = 1;
      for(int i = 1; i < input.length; i++) {
         if(input[i] == input[i - 1]) {
            currentDupCount++;
         } else {
            currentDupCount = 1;
         }
         if(currentDupCount > maxDups) {
            maxDups = currentDupCount;
         }
      }
      return maxDups;
   }

【讨论】:

  • 不错!更糟糕的是,你认为这可以在 O(1) 空间中完成,所有的垃圾箱都一个接一个地放置。例如这样输出是一个包含 n 个严格递增值序列的数组,其中 n 是最大 bin 数?
【解决方案2】:

没有循环就无法做到这一点。但是您可以使用一个集合来删除一些循环。您可以根据自己的喜好添加数据结构陷阱。

我在这里假设 bin 中元素的顺序必须与输入数组中元素的顺序一致。如果没有,这可以更有效地完成。

public static void main(String[] args) {
    String[] array = { "1", "2", "3", "41", "56", "41", "72", "72", "72",
            "78", "99" };

    List<Set<String>> bins = new ArrayList<>();

    for (String s : array) {
        findOrCreateBin(bins, s).add(s);
    }

    System.out.println(bins); // Prints [[1, 2, 3, 41, 56, 72, 78, 99], [41, 72], [72]]

}

private static Set<String> findOrCreateBin(List<Set<String>> bins, String s) {
    for (Set<String> bin : bins) {
        if (!bin.contains(s)) {
            return bin;
        }
    }

    Set<String> bin = new LinkedHashSet<>();
    bins.add(bin);
    return bin;
}

【讨论】:

  • 你能得到预期的输出吗?
  • 在最坏的情况下,如果初始数组的所有条目都相等(即 {3,3,3,3,3,3,3}),这将是 O(n^2)。您可以通过排序降低到 O(nlogn) 时间和 O(1) 空间(不计算结果集/数组)。
  • @MateuszDymczyk 可以添加一个查找来计算出现次数,但我觉得这是某人的家庭作业。
  • 好吧,我猜你也可以在 O(n) 时间内用 O(n) 空间和出现计数来完成它
  • 严格来说不是。您可以选择一组不同的字符串,使它们的哈希值都相等,在这种情况下,我们回到 O(n^2)。如果垃圾箱中的顺序不重要,您的想法会更好,否则您无法排序。添加了更多注释。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
相关资源
最近更新 更多