【算法解析】巧用哈希表与组合公式:高效统计数组中相同数对的数量
现有以下题目:1512. 好数对的数目
现有一个整数数组,要求统计数组中满足条件的索引对 (i, j)(其中 i < j 且 nums[i] = nums[j])数量。
一、暴力循环
两层for循环可以遍历所有的索引对,但它的时间复杂度是O(n^2),空间复杂度是O(n)。代码如下:
1 | class Solution { |
有没有办法能把时间复杂度降到O(n)。
二、组合公式+哈希表
输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5)
根据上述示例,我们可以对问题进行分解,先计算关于1,再计算关于2的对数,再计算关于3的对数。
对于每个数字出现的次数:我们可以使用组合数公式直接计算出该数字包含多少对:
- 当数组中某个数字出现1次时,代表该数字没有满足条件的索引对
- 当数组中某个数字出现2次时,代表该数字有1对满足条件的索引对
- 当数组中某个数字出现3次时,代表该数字有3对满足条件的索引对
- 当数组中某个数字出现4次时,代表该数字有6对满足条件的索引对
- …
- 当数组中某个数字出现n次时,代表有$\frac{n*(n-1)}{2}$对满足条件的索引对
上述公式表示n
个元素中选取 2 个的组合数,更用法的公式如:从n个元素选出m个元素的公式为:
$$\frac{n!}{m!(n-m)!}$$
我们只需计算出给定数组中不同数字的组合数并相加就能得到答案。
使用哈希表(如 std::unordered_map
)遍历数组,记录每个数字出现的次数,时间复杂度O(n)。
1 |
|
上述代码中第二个for循环用了auto类型推导,关于它的详细用法可以参考:
[C++中auto类型推导的常见用法](https://wangjunstf.github.io/2025/09/18/cpp-zhong-auto-lei-xing-tui-dao-de-chang-jian-yong-fa/)
在这里它根据freq的元素类型推断entry的类型。
代码中的 entry
是一个常量引用,它指向 std::unordered_map<int, int>
中的一个键值对(std::pair
)。更准确地说,它的类型是:
const std::pair<const int, int>&
下面是关键点的解析:
组成部分 | 含义 | 说明 |
---|---|---|
const |
常量 | 表示不能通过 entry 修改 unordered_map 中的元素。 |
std::pair |
键值对 | unordered_map 中的每个元素都是一个 pair 对象。 |
<const int, int> |
模板参数 | pair 的第一个成员(first )是 const int (键),第二个成员(second )是 int (值)。 |
& |
引用 | 避免在循环中拷贝整个 pair 对象,提升效率。 |
在你的代码中:
entry.first
是键(即数组中的数字,例如1
,2
,3
),因为是const
,所以不可修改。entry.second
是值(即该数字出现的频率,例如3
,2
,1
),这里被用来计算好数对的数量:count += v * (v - 1) / 2;
。
这种使用 const auto&
来遍历关联容器的做法非常常见,因为它既安全又高效。
简单来说,entry
就像是 freq
这个哈希表中每个单元的只读视图,让你能安全地读取每个数字及其对应的出现次数。