好数对的数目

标签: 数组 哈希表 数学 计数

难度: Easy

给你一个整数数组 nums

如果一组数字 (i,j) 满足 nums[i] == nums[j]i < j ,就可以认为这是一组 好数对

返回好数对的数目。

示例 1:

输入:nums = [1,2,3,1,1,3]
输出:4
解释:有 4 组好数对,分别是 (0,3), (0,4), (3,4), (2,5) ,下标从 0 开始

示例 2:

输入:nums = [1,1,1,1]
输出:6
解释:数组中的每组数字都是好数对

示例 3:

输入:nums = [1,2,3]
输出:0

提示:

  • 1 <= nums.length <= 100
  • 1 <= nums[i] <= 100

Submission

运行时间: 19 ms

内存: 15.9 MB

class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        # 暴力求解
        ans = 0
        length = len(nums)
        for i, value in enumerate(nums):
            for j in range(i+1, length):
                if value == nums[j]:
                    ans += 1
        return ans

Explain

这个题解采用了暴力法的思路。通过两层循环遍历数组,外层循环选取基准元素,内层循环查找之后的元素中与基准元素相同的元素。每当发现相同元素时,计数器增加,最终返回计数器的值作为结果。

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

空间复杂度: O(1)

class Solution:
    def numIdenticalPairs(self, nums: List[int]) -> int:
        # 初始化计数器
        ans = 0
        # 获取数组长度
        length = len(nums)
        # 外层循环:遍历每个元素,作为比较基准
        for i, value in enumerate(nums):
            # 内层循环:从当前元素的下一个开始比较
            for j in range(i+1, length):
                # 如果找到相同的元素,计数器增加
                if value == nums[j]:
                    ans += 1
        # 返回好数对的总数
        return ans

Explore

在某些情况下,选择使用暴力法是因为其实现简单直观。尤其当数据规模较小或者对执行效率的要求不是非常严格时,暴力法可以快速实现而无需考虑额外的数据结构和复杂的逻辑。此外,初学者可能会首先考虑暴力法作为问题解决的一种基础方法,以便更好地理解问题。但对于大规模数据,哈希表等数据结构确实可以提供更优的时间复杂度。

在这个算法中,`ans` 变量通过两层循环来确保每对好数对只被计算一次。外层循环固定一个基准元素,而内层循环从基准元素的下一个位置开始搜索,确保每次比较都是基准元素与其后面的元素进行比较。这样的循环结构保证了每个元素对只会被检查一次,从而避免了重复计数的情况。

暴力法的时间复杂度为O(n^2),其中n是数组的长度。当数组长度非常大时,比如接近100,这种方法的性能表现会显著下降。对于长度为100的数组,最坏情况下需要进行约5000次比较,这在数据量更大的情况下会导致明显的性能瓶颈,尤其是在对执行时间有严格要求的环境中。因此,对于大数据量的处理,推荐使用时间复杂度更低的算法,如使用哈希表的方法,其时间复杂度可以优化至O(n)。

在当前的算法结构中,提前终止内层循环并不会提供性能优化,因为我们必须检查外层循环指定的每个元素后面的所有元素以寻找相同的值。由于我们的目的是找出所有可能的好数对,每一对可能的组合都需要被考虑。除非我们事先知道数组有某些特定的属性可以利用(例如有序),否则我们无法在没有检查所有可能性的情况下提前终止内层循环。