一、有限 List 求交集
解决方案: 使用集合(Set)快速查找,时间复杂度 O(n + m)
// 有限 List 求交集(去重)
fun findCommonElements(list1: List<Int>, list2: List<Int>): Set<Int> {
val set1 = list1.toSet()
val set2 = list2.toSet()
return set1 intersect set2
}
// 保留重复元素的最小次数(例如 list1=[2,2,3], list2=[2,2,2] → 返回 [2,2])
fun findCommonElementsWithDuplicates(list1: List<Int>, list2: List<Int>): List<Int> {
val countMap1 = list1.groupingBy { it }.eachCount()
val countMap2 = list2.groupingBy { it }.eachCount()
return countMap1.flatMap { (num, count1) ->
val count2 = countMap2[num] ?: 0
List(min(count1, count2)) { num }
}
}
测试用例:
fun main() {
// 测试去重版本
val listA = listOf(1, 2, 2, 3)
val listB = listOf(2, 2, 4)
println(findCommonElements(listA, listB)) // 输出 [2]
// 测试保留重复版本
println(findCommonElementsWithDuplicates(listA, listB)) // 输出 [2, 2]
}
二、无限 List 求交集
假设条件: 两个无限 List 必须是有序的(如递增),否则无法有效求解!
解决方案: 双指针法逐步生成元素并比较,时间复杂度 O(k)(k 为找到的交集元素数量)
// 生成无限递增序列(示例1:自然数;示例2:平方数)
fun infiniteSequence1(): Sequence<Int> = generateSequence(0) { it + 1 }
fun infiniteSequence2(): Sequence<Int> = generateSequence(0) { (it + 1) * (it + 1) }
// 双指针法求交集(要求两个序列递增)
fun findCommonInfiniteElements(seq1: Sequence<Int>, seq2: Sequence<Int>): Sequence<Int> = sequence {
val iterator1 = seq1.iterator()
val iterator2 = seq2.iterator()
if (!iterator1.hasNext() || !iterator2.hasNext()) return@sequence
var a = iterator1.next()
var b = iterator2.next()
while (true) {
when {
a == b -> {
yield(a)
a = iterator1.next()
b = iterator2.next()
}
a < b -> a = iterator1.next()
else -> b = iterator2.next()
}
}
}
测试用例:
fun main() {
// 生成两个无限序列的交集(自然数 vs 平方数,交集为 0,1,4,9,16,...)
val common = findCommonInfiniteElements(infiniteSequence1(), infiniteSequence2())
// 取前5个交集元素测试
println(common.take(5).toList()) // 输出 [0, 1, 4, 9, 16]
}
三、复杂度分析
| 场景 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 有限 List 去重 | O(n + m) | O(n + m) |
| 无限 List 有序 | O(k)(k为实际找到的元素数) | O(1) |
四、关键点总结
-
有限 List:
- 去重用
Set,保留重复次数用 计数哈希表 - 示例:
[1,2,2,3]和[2,2,4]的交集为[2]或[2,2]
- 去重用
-
无限 List:
- 必须有序(如递增),否则无法高效求解
- 使用
generateSequence生成无限流,双指针逐步比较
-
注意事项:
- 无限流处理需设置终止条件(如
take(5)取前几个元素) - 若无限流无序,只能通过哈希表处理,但内存会无限增长(不可行)
- 无限流处理需设置终止条件(如
口诀:
有限列表用集合,
无限有序双指针。
无序无限无解药,
内存爆炸要记牢!