【问题标题】:Return elements that appear only once in array返回在数组中只出现一次的元素
【发布时间】:2021-07-11 17:49:25
【问题描述】:

我有一个数组,我的目标是返回唯一元素。我编写了对数组进行排序并将唯一元素存储在 ArrayList 中的代码,但不是只返回[7],而是返回[2,5,7]。为什么会这样?

public static void main(String[] args) {
    ArrayList<Integer> list = new ArrayList<>();
    int [] arr = {2, 5, 14, 14, 2, 7, 5};
    Arrays.sort(arr);
    for (int i =0; i<arr.length-1; i++){
        if(arr[i]!=arr[i+1]){
            list.add(arr[i]);
        }
    }
    System.out.println(list);
}

【问题讨论】:

  • 使用 Set 并且不必排序(或者可能带有 distinct 的 lambda)
  • 您需要检查arr[i-1] 以及arr[i+1](并处理端点)。
  • @Andy 获取 distinct 元素,而不是只出现一次的元素。

标签: java arrays


【解决方案1】:

我建议使用数组来存储数组中每个元素的频率,并将频率为 1 的元素(唯一元素)添加到list。

int max = Arrays.stream(arr).max().getAsInt();
int [] freq = new int[max+1];
for (int i=0; i<arr.length; i++)
    ++freq[ arr[i] ];
for (int e : arr)
    if (freq[ e ] == 1)
        list.add( e );
System.out.println(list);

请注意,这仅在 max 不是很大 (HashMap 来存储频率。

【讨论】:

    【解决方案2】:

    因为当您对原始数组进行排序时,您将得到 {2,2,5,5,7,14} 并且您正在比较相邻元素,因此在第一次迭代中您将比较 2 和 2 即索引 0 和 1 处的元素,并且因为它们是相等的,您不会将索引0 处的元素添加到结果列表中,但是在第二次迭代中,您将比较2 和5(位置上的元素1 和2),因为它们是不等于 2 将被添加到结果列表中。没有必要那样做,你可以简单地从arr 创建Set&lt;Integer&gt; result,因为Set 不允许重复元素,你的问题就解决了。所以你可以写这样的东西:

    Set<Integer> result = new HashSet<Integer>(Arrays.asList(arr))
    

    【讨论】:

      【解决方案3】:

      无需统计频率。

      如果数组元素最多可以出现两次,则有一个非常简单的方法,即使用单个 Set。对于每个数组成员,如果它不在集合中,则添加它,如果它是删除它。您将留下独特的元素。

      Set<Integer> seen = new HashSet<>();
      for(int i : arr)
      {
          if(seen.contains(i)) 
              seen.remove(i);
          else 
              seen.add(i);
      }
      

      如果元素可以出现两次以上,则需要第二个 Set

      Set<Integer> unique = new HashSet<>();
      Set<Integer> seen = new HashSet<>();
      for(int i : arr)
      {
          if(unique.contains(i)) 
          {
              unique.remove(i);
              seen.add(i);
          }
          else if(!seen.contains(i))
          {
              unique.add(i);
          }
      }
      

      【讨论】:

        【解决方案4】:

        如果您被允许使用频率图,也许您可​​以尝试构建数组中所有值的频率图,然后迭代映射条目并添加值 == 1 的所有项目:

        // A map with key as array digit and value as it's frequency
        Map<Integer, Integer> frequencyMap = new HashMap<>();
        ArrayList<Integer> list = new ArrayList<>();
        int [] arr = {2, 5, 14, 14, 2, 7, 5};
        
        // Build Frequency Map
        for (int j : arr)
            frequencyMap.merge(j, 1, Integer::sum);
        
        // Add all items with frequency = 1 to list
        for (Map.Entry<Integer, Integer> intToFrequency : frequencyMap.entrySet()) {
            if (intToFrequency.getValue() == 1) {
                list.add(intToFrequency.getKey());
            }
        }
        System.out.println(list);
        

        【讨论】:

          【解决方案5】:

          当您对数组进行排序时,您会得到 [2,2,5,5,7,14,14]。在你的 for 循环中,你只检查它后面的元素是否不同。当 i=1 时,arr[1]!=arr[2] 因此即使 2 不是唯一元素,也会添加 arr[1]。而是检查它之前和之后的值是否不同。您可能需要更改 for 循环的索引。

          另一种方法是您可以创建一个只存储唯一值的集合。并在将每个值添加到集合后循环遍历数组。该集合将存储所有唯一值。然后,如果您的方法必须返回 ArrayList,则可以将该集合转换为 ArrayList。

          【讨论】:

            【解决方案6】:

            您将数组中的每个数字分成 2 对不同的对,您应该更改循环中 if 语句中的索引,以便数组中的每个值都是 1 对的一部分。

            【讨论】:

              【解决方案7】:

              你有正确的想法,但过于复杂。

              创建字典/哈希映射要容易得多,并且在遍历数组时,如果值不在哈希映射中,请将其存储。

              我们使用字典/哈希映射,因为它为我们提供了恒定的查找时间。

              在迭代结束时,您的字典/哈希映射将包含您在数组中的所有唯一值。

              【讨论】:

                【解决方案8】:

                在排序数组中检测重复值的现有代码需要稍作更改,以借助布尔标志和嵌套循环跳过重复值:

                public static List<Integer> getUnique(int ... arr) {
                    List<Integer> list = new ArrayList<>(); 
                    
                    System.out.print("Input: " + Arrays.toString(arr) + " -> ");
                
                    Arrays.sort(arr);
                    
                    for (int i = 0, j = 0; i < arr.length; i++) {
                        boolean dupFound = false;
                        for (j = i + 1; j < arr.length && arr[i] == arr[j]; j++) {
                            dupFound = true;
                            i = j;
                        }
                        if (dupFound) {
                            continue;
                        }
                        list.add(arr[i]);
                    }
                    return list;
                }
                

                测试:

                System.out.println(getUnique(2, 5, 14, 14, 2, 7, 5));
                System.out.println(getUnique(2, 5, 14, 14, 20, 7, 7, 5, 14));
                

                输出:

                Input: [2, 5, 14, 14, 2, 7, 5] -> [7]
                Input: [2, 5, 14, 14, 20, 7, 7, 5, 14] -> [2, 20]
                

                如果允许使用Set,则无需对输入数组进行预排序并使用两个集合即可解决此任务:一个跟踪不同的值,另一个跟踪基于唯一值的值如果元素未包含在集合中,Set::add 返回 true,如果集合中有重复元素,则返回 false:

                public static Set<Integer> getUnique(int ... arr) {
                    Set<Integer> distinct = new HashSet<>();
                    Set<Integer> unique = new HashSet<>();
                    
                    System.out.print("Input: " + Arrays.toString(arr) + " -> ");
                    
                    for (int x : arr) {
                        if (distinct.add(x)) { // no duplicate found yet
                            unique.add(x);     // count as unique
                        } else {
                            unique.remove(x);  // remove a duplicate
                        }
                    }
                    return unique;
                }
                

                测试及其结果与上图相同。

                【讨论】:

                  猜你喜欢
                  • 2015-09-13
                  • 2016-10-21
                  • 2021-08-31
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2021-07-24
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多