《面试必考》— 找出重复的元素

        哈喽,各位道友,在平时面试过程中,避免不了一些笔试题(算法&SQL),今天分享想一个经典的笔试题,从一个数组、集合中找出重复的元素

//获取集合中某个重复的元素
 List<String> list = Arrays.asList("1","2","3","3","4");
 
//方法一:使用Stream API找到重复的元素
List<String> duplicates = list.stream()
        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .entrySet().stream()
        .filter(entry -> entry.getValue() > 1)
        .map(Map.Entry::getKey)
        .collect(Collectors.toList());
System.out.println("重复的元素: " + duplicates);

//方法二:使用HashMap
List<String> duplicates = new ArrayList<>();
HashMap<String , Integer> map = new HashMap<>();
for (String s : list) {
    if (map.containsKey(s)){
        map.put(s,map.get(s)+1);
    }else {
        map.put(s,1);
    }
}
map.entrySet().stream().filter(entry -> entry.getValue() > 1).forEach(entry -> duplicates.add(entry.getKey()));
System.out.println("重复的元素: " + duplicates);


//方法三:简单粗暴for双循环
        List<String> duplicates = new ArrayList<>();
//         遍历列表中的每一个元素
        for (int i = 0; i < list.size(); i++) {
            // 再次遍历剩余的元素,检查是否有重复
            for (int j = i + 1; j < list.size(); j++) {
                if (list.get(i).equals(list.get(j))) {
                    // 如果还没有添加到重复列表中,则添加
                    System.out.println(list.get(i));
                    }
                }
            }
   
//方法四:二分法查找
List<String> duplicates = new ArrayList<>();
Collections.sort(list);
// 对排序后的列表进行遍历
for (int i = 0; i < list.size(); i++) {
    String current = list.get(i);
    // 检查下一个元素是否相同,如果是,则使用二分查找确认重复次数
    if (i + 1 < list.size() && current.equals(list.get(i + 1))) {
        int firstOccurrence = binarySearchFirstOccurrence(list, current, 0, i);
        int lastOccurrence = binarySearchLastOccurrence(list, current, i, list.size() - 1);
        if (lastOccurrence - firstOccurrence > 0) {
            duplicates.add(current);
        }
    }
}

private static int binarySearchFirstOccurrence(List<String> list, String target, int start, int end) {
    while (start <= end) {
        int mid = start + (end - start) / 2;
        if (list.get(mid).compareTo(target) < 0) {
            start = mid + 1;
        } else if (list.get(mid).compareTo(target) > 0) {
            end = mid - 1;
        } else {
            if (mid == start || !list.get(mid - 1).equals(target)) {
                return mid;
            }
            end = mid - 1;
        }
    }
    return -1;
}

private static int binarySearchLastOccurrence(List<String> list, String target, int start, int end) {
    while (start <= end) {
        int mid = start + (end - start) / 2;
        if (list.get(mid).compareTo(target) < 0) {
            start = mid + 1;
        } else if (list.get(mid).compareTo(target) > 0) {
            end = mid - 1;
        } else {
            if (mid == end || !list.get(mid + 1).equals(target)) {
                return mid;
            }
            start = mid + 1;
        }
    }
    return -1;
}

                               
//方法五:用于存储首次遇到的元素
Set<String> seen = new TreeSet<>(); 
// 用于存储重复元素
Set<String> duplicates = new LinkedHashSet<>(); 
for (String element : list) {
    // 如果元素已经存在seen中,则表明它是重复的
    if (!seen.add(element)) {
        duplicates.add(element);
    }
}
System.out.println("重复的元素: " + duplicates);    

//方法六:在多线程环境下查找重复元素,可以使用ConcurrentHashMap来避免线程安全问题
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
List<String> duplicates = new ArrayList<>();
for (String element : list) {
    map.compute(element, (key, value) -> value == null ? 1 : ++value);
}
map.forEach((key, value) -> {
    if (value > 1) {
        duplicates.add(key);
    }
});
System.out.println("重复的元素: " + duplicates);                                                                                                                                    
   
//方法七:使用Bloom Filter(适用于大数据集)
//对于非常大的数据集,可以使用Bloom Filter来近似查找重复元素。
// Bloom Filter是一个空间效率极高的概率型数据结构,用于测试一个元素是否属于一个集合。它可能会产生假阳性,但不会产生假阴性。
import com.google.common.hash.BloomFilter;

BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), list.size());
List<String> duplicates = new ArrayList<>();
for (String element : list) {
    if (bloomFilter.mightContain(element)) {
        duplicates.add(element);
    } else {
        bloomFilter.put(element);
    }
}
System.out.println("重复的元素: " + duplicates); 


//方法八:使用BitSet(适用于整数列表)
//如果列表中的元素是整数,并且数值范围有限(例如,介于0到某个最大值之间)
//可以使用BitSet来标记每个元素是否出现过。这种方法特别节省空间,因为BitSet使用位来存储信息
List<Integer> duplicates = new ArrayList<>();
List<Integer> intList = list.stream()
        .map(Integer::parseInt)
        .collect(Collectors.toList());
int maxValue = intList.stream().max(Integer::compare).orElse(0);
BitSet bitSet = new BitSet(maxValue + 1);
for (Integer number : intList) {
    if (bitSet.get(number)) {
        duplicates.add(number);
    } else {
        bitSet.set(number);
    }
}
System.out.println("重复的元素: " + duplicates);   

        对于前面三种,按照时间复杂度和实际执行效率,我们可以对这四种方法进行排名。请注意,这里的排名主要基于算法的时间复杂度,同时也考虑了空间复杂度和实际执行效率。

        使用HashMap和Stream API过滤

                时间复杂度: O(n)

                空间复杂度: O(n)

                效率: 高,因为HashMap操作在平均情况下是常数时间,且Stream API的过滤操作也很快。

                适用场景: 数据量较大,需要高效查找重复元素的情况。

        使用Stream API的groupingBy和counting

                时间复杂度: O(n)

                空间复杂度: O(n)

                效率: 高,但可能略低于直接使用HashMap,因为内部可能有额外的管理开销。

                适用场景: Java 8及以上版本,偏好简洁代码风格,数据量适中。

        排序+二分查找

                时间复杂度: O(n log n)

                空间复杂度: O(1) 或 O(n),取决于排序算法是否原地排序。

                效率: 中等,适合数据量较大但已知数据接近排序或排序成本可以接受的情况。

                适用场景: 数据量大,且排序成本相对于查找成本较低。

        双重for循环

                时间复杂度: O(n^2)

                空间复杂度: O(1) 或 O(k),其中k是重复元素的数量。

                效率: 低,不适合数据量大的情况。

                适用场景: 数据量非常小,或者对性能要求不高,且代码简单性是优先考虑的。

        综上所述,从效率角度,使用HashMap和Stream API过滤的方法是最佳选择,其次是使用Stream API的groupingBy和counting,然后是排序+二分查找,最后是双重for循环。在实际应用中,应根据具体的数据量、性能需求以及代码的可读性和维护性来选择最合适的方法。

        对了,你能写出几种呢?有没有考虑SQL方面呢?

//你可以修改findDuplicates方法,使其接受List<String>并处理字符串形式的数字。
//这需要在方法内部将字符串转换为整数

import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

public List<Integer> findDuplicates(List<String> stringList) {
    List<Integer> intList = stringList.stream()
                                      .map(Integer::parseInt)
                                      .collect(Collectors.toList());

    int maxValue = intList.stream().max(Integer::compare).orElse(0);
    BitSet bitSet = new BitSet(maxValue + 1);
    List<Integer> duplicates = new ArrayList<>();

    for (Integer number : intList) {
        if (bitSet.get(number)) {
            duplicates.add(number);
        } else {
            bitSet.set(number);
        }
    }
    return duplicates;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值