在List中找到一个带有重复项的唯一元素

时间:2017-11-02 13:02:44

标签: java arrays unique

我们有列表,如果整数(或其他一些对象),如:

[0, 0, 1, 1, ... , 777777, ... , 999999, 999999]

Java代码:

List<Integer> ints = new LinkedList<Integer>();
for (int i = 0; i < 999999; i++) {
    ints.add(i); // add first value
    if (i!=777777){
        ints.add(i); // add duplicate value in not '5'
    }
}

我以这种方式解决了这个问题:

Integer unique = null;
while (true) {
    unique = ints.get(0);
    ints.remove(0);
    if (ints.contains(unique)) {
        ints.remove(ints.indexOf(unique));
    } else {
        break;
    }
}
System.out.println(unique);// 777777

大约需要30毫秒。使用ArrayList,它的工作时间更长。 问题是如何以最佳(正确/优雅/最快)方式从此列表中获取一个唯一值(例如777777)。

7 个答案:

答案 0 :(得分:3)

由于您在循环中运行ints.contains(unique)ints.indexOf(unique)等线性时间操作,因此您的实现效率非常低。因此,在最糟糕的情况下,您的运行时间为O(n^2)

如果数字总是排序的(就像在你的例子中那样),你可以在列表的元素上迭代一次,找到不等于list.get(i)和{{list.get(i-1)的元素list.get(i+1) 1}}。这将采用O(n)

如果数字并不总是排序,您可以在O(nlogn)中对它们进行排序,然后像以前一样继续。

或者您可以迭代数字一次,计算每个数字的出现次数(您可以将计数存储在HashMap中),并找到计数为1的数字。这将需要{{1无论输入列表是否已排序。

答案 1 :(得分:1)

您使用的是错误的收藏品。列表不是此问题中的正确集合。
您应该使用 HashSet 来解决此问题。

private void find(List<Integer> ints){
   HashSet<Integer> set = new HashSet<>();
   for(int i=0;i<ints.size();i++){
       if(set.contains(i)){
          set.remove(i);
       }else{
          set.add(i);
       }
   }
   Iterator<Integer> itr = set.iterator();
   if(itr.hasNext()){
      System.out.println("Unique value is :"+itr.next());
   }else{
      System.out.println("There is no duplicated value");
   }
}

此方法仅适用于2个和1个计数列表项。如果您有相同的数字3次,这种方法会发现它是重复的。

排序可能是答案,但排序最多只有O(logn),在O(n)之后找到重复的元素。如果您可以在创建列表时创建哈希集,则此方法会将重复的元素查找为O(1)。

答案 2 :(得分:1)

首先获取唯一元素,然后使用集合api的频率方法来获取重复项

Set<String> uniqueSet = new HashSet<String>(list);

for (String temp : uniqueSet) {
    if(Collections.frequency(list, temp)==1) {
      System.out.println(temp);
    }

}

这将从列表中返回唯一元素。

答案 3 :(得分:0)

如下所示尝试jdk8的流媒体API如何:

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

public class FiindUnique {

    public static void main(String[] args) {
        System.out.println("Hello World");
        Collection<String> list = Arrays.asList("A", "B", "C", "D", "A", "B", "C");

        //solution 1
        List<String> distinctElements = list.stream().filter(name -> Collections.frequency(list, name) == 1).collect(Collectors.toList());
        System.out.println(distinctElements);

        //Solution 2

        List<String> distinctElements2 = list.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
            .entrySet()
            .stream()
            .filter(e -> e.getValue() == 1)
            .map(e -> e.getKey())
            .collect(Collectors.toList());
         System.out.println(distinctElements2);
    }
}

答案 4 :(得分:0)

除了其他答案给出的Set解决方案之外,如果您知道重复元素仅出现两次(或偶数次)并且只有一个不重复的数字,您可以对所有值进行异或运算,以找到唯一的数字。

public int findUnique(int[] nums) { // Or (List<Integer> nums), it works also
    int xor = 0;
    for(int i : nums){
        xor ^= i;
    }
    return xor;
}

此解决方案比其他解决方案更快,更短。


您也可以采用Java 8样式进行此操作,但是它当然会变慢:

public int findUnique(int[] nums) {
    return Arrays.stream(nums)
            .reduce((acc,i) -> acc ^= i)
            .orElseThrow(IllegalArgumentException::new);
}

答案 5 :(得分:0)

通过二分查找找到反转点,即唯一数偶数和奇数索引前的数相同,唯一数后奇数和偶数的数相同,所以我们可以进行二分查找找到反演点的时间复杂度将为 log(N)

答案 6 :(得分:-1)

您认为数组已排序吗? 这应该更快。

HashSet<Integer> s = new HashSet<Integer>();

for ( Integer i : ints){
    if ( s.contains(i)){
        s.remove(i); // remove dups
    }
    else{
        s.add(i); //add uniques
    }
}

for ( Integer unique: s){
// should contain one value
    System.out.println(unique);// 777777
}