分类: 未分类

  • Predict the Winner,LeetCode,486

    LeetCode 486题 “Predict the Winner” 是一个中等难度的动态规划问题。题目要求预测两个玩家在玩一个游戏时的胜者。游戏规则如下:

    1. 给定一个整数数组 nums
    2. 两个玩家轮流从数组的两端选择一个数字,并将其从数组中移除。
    3. 每个玩家总是尽可能使自己的得分最大化。
    4. 玩家的得分是他们选择的数字的总和。
    5. 预测玩家1(先手)是否能赢得游戏,或者两人是否会打成平手。

    解题思路

    我们可以使用动态规划来解决这个问题。定义一个二维数组 dp,其中 dp[i][j] 表示在子数组 nums[i...j] 上,先手玩家能获得的最大分数与后手玩家能获得的最大分数之差。

    状态转移方程

    • i == j 时,只有一个数字可以选择,dp[i][j] = nums[i]
    • i < j 时,先手玩家有两种选择:
      • 选择 nums[i],则后手玩家在 nums[i+1...j] 上成为先手,此时先手玩家的得分差为 nums[i] - dp[i+1][j]
      • 选择 nums[j],则后手玩家在 nums[i...j-1] 上成为先手,此时先手玩家的得分差为 nums[j] - dp[i][j-1]
  • Max Consecutive Ones,LeetCode,485

    好的,我们来详细讲解一下 LeetCode 上的第 485 题:Max Consecutive Ones(最大连续1的个数)。

    题目描述

    给定一个二进制数组,返回该数组中连续1的最大个数。

    示例 1:

    输入: [1,1,0,1,1,1] 输出: 3 解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.

    解题思路

    这个问题可以通过一次遍历数组来解决。我们可以使用两个变量:

    1. max_count:用来记录最大的连续1的个数。
    2. current_count:用来记录当前连续1的个数。

    遍历数组时,如果遇到1,就将current_count加1;如果遇到0,就将current_count重置为0,并将max_count更新为current_countmax_count中的较大值。

    具体步骤

    1. 初始化max_countcurrent_count为0。
    2. 遍历数组中的每一个元素:
      • 如果元素为1,current_count加1。
      • 如果元素为0,比较current_countmax_count,更新max_count为较大值,然后将current_count重置为0。
    3. 遍历结束后,再次比较current_countmax_count,以确保最后一个连续1的序列被考虑进去。
    4. 返回max_count

    代码实现

    以下是使用Python实现的代码:

    class Solution: def findMaxConsecutiveOnes(self, nums): max_count = 0 current_count = 0

        for num in nums:
            if num == 1:
                current_count += 1
            else:
                max_count = max(max_count, current_count)
                current_count = 0
    
        # 最后一次检查,以防数组以连续1结尾
        max_count = max(max_count, current_count)
    
        return max_count

    复杂度分析

    • 时间复杂度: O(n),其中n是数组的长度。我们只需要遍历一次数组。
    • 空间复杂度: O(1),我们只使用了常数个额外空间。

    测试用例

    sol = Solution() print(sol.findMaxConsecutiveOnes([1,1,0,1,1,1])) # 输出: 3 print(sol.findMaxConsecutiveOnes([1,0,1,1,0,1])) # 输出: 2 print(sol.findMaxConsecutiveOnes([0,0,0,0,0,0])) # 输出: 0 print(sol.findMaxConsecutiveOnes([1,1,1,1,1,1])) # 输出: 6

    通过这些测试用例,我们可以验证代码的正确性。

    希望这个详细的解答对你有帮助!如果有其他问题,欢迎继续提问。

  • Find Permutation,LeetCode,484

    题目描述:

    在 LeetCode 上,第 484 题的标题是 “Find Permutation”,中文可以翻译为“寻找排列”。题目描述如下:

    给定一个由若干个 “I”(表示上升)和 “D”(表示下降)组成的字符串 s,你需要构造一个长度为 n 的排列 p,其中 n 是字符串 s 的长度加 1。排列 p 需要满足以下条件:

    • p 是一个从 1 到 n 的整数排列。
    • 对于每个 i(0 ≤ i < n - 1),如果 s[i] 是 “I”,则 p[i] < p[i+1];如果 s[i] 是 “D”,则 p[i] > p[i+1]

    你需要返回所有符合条件的排列 p

    示例:

    输入: "IDID" 输出: [1,4,3,2,5]

    解题思路:

    1. 初始化: 创建一个长度为 n 的数组 p,初始值为 1 到 n 的顺序排列。
    2. 处理 “D” 区间: 遍历字符串 s,找到连续的 “D” 区间,将这些区间内的数字进行逆序排列,以满足下降的要求。
    3. 返回结果: 经过处理后的数组 p 即为符合条件的排列。

    详细代码实现:

    class Solution: def findPermutation(self, s: str) -> List[int]: n = len(s) + 1 p = list(range(1, n + 1)) # 初始化排列

        i = 0
        while i < len(s):
            if s[i] == 'D':
                start = i
                # 找到连续的 'D' 区间
                while i < len(s) and s[i] == 'D':
                    i += 1
                # 将区间 [start, i] 的数字逆序
                p[start:i+1] = reversed(p[start:i+1])
            else:
                i += 1
    
        return p

    解释:

    1. 初始化排列: p = list(range(1, n + 1)) 创建一个从 1 到 n 的顺序排列。
    2. 遍历字符串 s
      • 如果遇到 “D”,记录起始位置 start,继续遍历直到 “D” 结束的位置 i
      • 使用 reversed(p[start:i+1]) 将区间 [start, i] 的数字逆序,以满足下降的要求。
    3. 返回结果: 经过处理后的 p 即为符合条件的排列。

    复杂度分析:

    • 时间复杂度: O(n),其中 n 是字符串 s 的长度。我们需要遍历字符串 s 并对某些区间进行逆序操作,逆序操作的复杂度为 O(k),k 是区间的长度,总和不超过 n。
    • 空间复杂度: O(n),用于存储排列 p

    这个解法简洁且高效,能够满足题目要求,生成符合条件的排列。希望这个详细的解释对你有所帮助!如果有任何进一步的问题,欢迎继续提问。

  • Smallest Good Base,LeetCode,483

    LeetCode 483题 “Smallest Good Base” 是一个中等难度的数学问题。题目要求找到一个最小的“好基数”(good base),使得给定的正整数 ( N ) 可以表示为以下形式:

    [ N = a^0 + a^1 + a^2 + \ldots + a^d ]

    其中 ( a ) 是基数,( d ) 是指数的最大值,且 ( a ) 和 ( d ) 都是正整数。

    题目描述

    给定一个正整数 ( N ),找到最小的“好基数” ( a )。

    解题思路

    1. 数学转换
      • 首先,将等式转换为数学形式: [ N = \frac{a^{d+1} – 1}{a – 1} ]
      • 这个等式表示 ( N ) 可以被表示为 ( a ) 的等比数列之和。
    2. 确定范围
      • 对于给定的 ( N ),我们需要找到合适的 ( d ) 和 ( a )。
      • ( d ) 的最大值可以通过 ( 2^d \leq N ) 来估算,因为 ( a ) 至少为2。
    3. 二分查找
      • 对于每个可能的 ( d ),使用二分查找来确定合适的 ( a )。
      • 二分查找的范围是 ( [2, N^{1/d}] )。
    4. 验证
      • 对于每个 ( a ),验证等式 ( N = \frac{a^{d+1} – 1}{a – 1} ) 是否成立。

    代码实现

    以下是 Python 的实现代码:

    class Solution: def smallestGoodBase(self, N: int) -> str: import math

        def check(a, d):
            # 检查是否满足等式 N = a^0 + a^1 + ... + a^d
            sum = 0
            for i in range(d + 1):
                sum = sum * a + 1
            return sum == N
    
        def find_a(d):
            # 使用二分查找找到合适的 a
            left, right = 2, int(N**(1/d)) + 1
            while left < right:
                mid = (left + right) // 2
                sum = (mid**(d+1) - 1) // (mid - 1)
                if sum == N:
                    return mid
                elif sum < N:
                    left = mid + 1
                else:
                    right = mid
            return -1
    
        # 从大的 d 开始尝试,找到最小的 a
        for d in range(int(math.log2(N)), 1, -1):
            a = find_a(d)
            if a != -1:
                return str(a)
    
        # 如果没有找到,返回 N-1(根据题意,这种情况不会发生)
        return str(N - 1)

    示例

    sol = Solution() print(sol.smallestGoodBase(13)) # 输出 "3" print(sol.smallestGoodBase(4681)) # 输出 "8"

    解释

    1. check函数
      • 用于验证给定的 ( a ) 和 ( d ) 是否满足等式。
    2. find_a函数
      • 使用二分查找来确定合适的 ( a )。
    3. 主函数
      • 从大的 ( d ) 开始尝试,找到最小的 ( a )。
      • ( d ) 的范围从 ( \log_2(N) ) 开始向下递减,因为 ( d ) 越大,( a ) 越小。

    复杂度分析

    • 时间复杂度:( O(\log^2(N)) ),因为 ( d ) 的范围是 ( O(\log(N)) ),每次二分查找的时间复杂度是 ( O(\log(N)) )。
    • 空间复杂度:( O(1) ),使用了常数空间。

    通过上述步骤和代码实现,可以有效地解决 LeetCode 483题 “Smallest Good Base”。

  • License Key Formatting,LeetCode,482

    题目描述(LeetCode 482):License Key Formatting

    你有一个密钥字符串 S,其中包含字母、数字和短划线 -。你需要将这个字符串重新格式化为一个由字母和数字组成的字符串,并且每 K 个字符之间用短划线分隔。

    具体要求如下:

    1. 字符串 S 中只包含字母(大写和小写)、数字和短划线 -
    2. 你需要将 S 中的短划线去掉,并将剩余的字符重新格式化。
    3. 格式化后的字符串中,每 K 个字符之间用短划线分隔,最后一个分组可能不满 K 个字符。

    示例:

    输入:S = "5F3Z-2e-9-w", K = 4

    输出:"5F3Z-2E9W"

    解释:字符串 S 中的短划线被去掉,剩余的字符按每 4 个一组重新格式化,得到 "5F3Z-2E9W"

    解题思路:

    1. 去除短划线:首先将字符串 S 中的所有短划线去掉。
    2. 反转字符串:为了方便从后往前每 K 个字符分组,可以将字符串反转。
    3. 分组并添加短划线:从反转后的字符串开始,每 K 个字符一组,添加短划线。
    4. 再次反转:将分组后的字符串再次反转,得到最终结果。

    代码实现(Python):

    class Solution: def licenseKeyFormatting(self, S: str, K: int) -> str:

    去除短划线并转换为大写

        S = S.replace('-', '').upper()
    
        # 反转字符串
        S = S[::-1]
    
        # 分组并添加短划线
        result = []
        for i in range(0, len(S), K):
            result.append(S[i:i+K])
    
        # 再次反转并连接成最终结果
        formatted_key = '-'.join(result)[::-1]
    
        return formatted_key

    示例使用

    sol = Solution() print(sol.licenseKeyFormatting("5F3Z-2e-9-w", 4)) # 输出: "5F3Z-2E9W"

    详细解释:

    1. 去除短划线并转换为大写S = S.replace('-', '').upper() 这一步将输入字符串中的所有短划线去掉,并将所有字符转换为大写,以便统一格式。
    2. 反转字符串S = S[::-1] 反转字符串是为了方便从后往前每 K 个字符进行分组。
    3. 分组并添加短划线result = [] for i in range(0, len(S), K): result.append(S[i:i+K]) 通过循环,每 K 个字符一组,将分组后的字符串添加到结果列表中。
    4. 再次反转并连接成最终结果formatted_key = '-'.join(result)[::-1] 将分组后的字符串列表用短划线连接起来,并再次反转,得到最终的格式化后的密钥字符串。

    这种方法的时间复杂度为 O(N),其中 N 是字符串 S 的长度,空间复杂度也为 O(N),主要用于存储中间结果和最终结果。

  • Magical String,LeetCode,481

    题目描述:

    神奇字符串 s 仅由 '1''2' 组成,并需要满足以下条件:

    1. 字符串 s 是神奇的,因为串联字符串 s 中的 '1''2' 组成的字符串等于 s 本身。
    2. 对于每个索引 is[0] 的索引视为 ),s[i] 表示 s 中索引 i 的字符应该出现的次数。

    给你一个整数 n,返回在神奇字符串 s 的前 n 个字符中有多少个 '1'

    示例:

    输入:n = 6 输出:3 解释:神奇字符串 s 的前 6 个字符是 “122112”,其中 “1” 出现了 3 次。

    解题思路:

    1. 初始化字符串: 从一个已知的神奇字符串开始,例如 "122"。这是因为我们可以确定前几个字符的生成规则。
    2. 构建字符串: 使用两个指针 ij,其中 i 指向当前需要生成的字符的位置,j 指向当前用于生成字符的规则的位置。
    3. 生成字符:
      • 根据 s[j] 的值确定接下来要添加多少个字符。
      • 如果 s[j]'1',则添加一个与 s[i-1] 不同的字符。
      • 如果 s[j]'2',则添加两个与 s[i-1] 不同的字符。
    4. 计数 1 的个数: 在生成字符串的过程中,统计前 n 个字符中 '1' 的个数。

    代码实现:

    class Solution: def magicalString(self, n: int) -> int: if n == 0: return 0 if n <= 3: return 1

        # 初始化字符串
        s = [1, 2, 2]
        i, j = 2, 2
        count_1 = 1  # 已经有一个 '1'
    
        while len(s) < n:
            if s[j] == 1:
                # 添加一个与当前不同的字符
                next_char = 3 - s[-1]
                s.append(next_char)
                if next_char == 1:
                    count_1 += 1
                i += 1
            elif s[j] == 2:
                # 添加两个与当前不同的字符
                next_char = 3 - s[-1]
                s.append(next_char)
                s.append(next_char)
                if next_char == 1:
                    count_1 += 2
                i += 2
            j += 1
    
        # 如果生成的字符串长度超过 n,需要修正 count_1
        if len(s) > n and s[n] == 1:
            count_1 -= 1
    
        return count_1

    示例

    sol = Solution() print(sol.magicalString(6)) # 输出:3

    解释:

    1. 初始化: s = [1, 2, 2]i = 2j = 2count_1 = 1
    2. 生成字符串:
      • s[2] = 2,添加两个与 s[-1](即 2)不同的字符,即两个 1s 变为 [1, 2, 2, 1, 1]count_1 变为 3
      • j 增加 1,变为 3。
      • s[3] = 1,添加一个与 s[-1](即 1)不同的字符,即一个 2s 变为 [1, 2, 2, 1, 1, 2]
      • j 增加 1,变为 4。
    3. 终止条件:s 的长度达到 n 时停止生成。

    通过这种方式,我们可以有效地构建神奇字符串并统计前 n 个字符中 '1' 的个数。

  • Sliding Window Median,LeetCode,480

    LeetCode 480题 “Sliding Window Median” 是一个中等难度的题目,要求我们在一个滑动窗口中找到中位数。中位数是指在一个有序数组中位于中间位置的数,如果数组长度是奇数,则中位数是中间那个数;如果数组长度是偶数,则中位数是中间两个数的平均值。

    题目描述

    给定一个数组 nums 和一个整数 k,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

    返回滑动窗口的中位数数组。

    示例

    输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3 输出: [1,-1,-1,3,5,6] 解释:

    滑动窗口的位置 中位数


    [1 3 -1] -3 5 3 6 7 1 1 [3 -1 -3] 5 3 6 7 -1 1 3 [-1 -3 5] 3 6 7 -1 1 3 -1 [-3 5 3] 6 7 3 1 3 -1 -3 [5 3 6] 7 5 1 3 -1 -3 5 [3 6 7] 6

    解题思路

    要解决这个问题,我们可以使用两个数据结构:一个大顶堆(max heap)和一个小顶堆(min heap)。大顶堆用来存储窗口中较小的那一半元素,小顶堆用来存储较大的那一半元素。这样,我们可以保证:

    • 大顶堆中的所有元素都小于等于小顶堆中的所有元素。
    • 两个堆的大小之差不超过1。

    通过这种方式,我们可以快速找到中位数:

    • 如果两个堆的大小相等,中位数是两个堆顶元素的平均值。
    • 如果两个堆的大小不相等,中位数是较大的那个堆的堆顶元素。

    具体步骤

    1. 初始化:创建一个大顶堆 left 和一个小顶堆 right
    2. 填充初始窗口:将前 k 个元素加入堆中,调整堆使其满足上述条件。
    3. 滑动窗口:从第 k 个元素开始,每次移动窗口时:
      • 移除窗口最左边的元素。
      • 添加窗口最右边的元素。
      • 调整堆使其满足上述条件。
      • 计算当前窗口的中位数并记录。

    代码实现

    以下是使用Python实现的代码:

    import heapq

    def medianSlidingWindow(nums, k): def add_num(num): if not left or num <= -left[0]: heapq.heappush(left, -num) else: heapq.heappush(right, num) balance()

    def remove_num(num):
        if num <= -left[0]:
            left.remove(-num)
            heapq.heapify(left)
        else:
            right.remove(num)
            heapq.heapify(right)
        balance()
    
    def balance():
        if len(left) > len(right) + 1:
            heapq.heappush(right, -heapq.heappop(left))
        if len(right) > len(left):
            heapq.heappush(left, -heapq.heappop(right))
    
    def get_median():
        if len(left) == len(right):
            return (-left[0] + right[0]) / 2
        else:
            return -left[0]
    
    left, right = [], []
    result = []
    
    for i in range(len(nums)):
        add_num(nums[i])
        if i >= k:
            remove_num(nums[i - k])
        if i >= k - 1:
            result.append(get_median())
    
    return result

    示例

    nums = [1,3,-1,-3,5,3,6,7] k = 3 print(medianSlidingWindow(nums, k)) # 输出: [1, -1, -1, 3, 5, 6]

    解释

    1. add_num(num):将一个数加入堆中,并根据需要调整堆。
    2. remove_num(num):从堆中移除一个数,并重新调整堆。
    3. balance():确保两个堆的大小之差不超过1。
    4. get_median():根据两个堆的顶元素计算中位数。

    通过这种方式,我们可以在每个窗口移动时高效地计算中位数,从而解决这道题目。

  • Largest Palindrome Product,LeetCode,479

    LeetCode 479题是关于寻找最大的回文数乘积的问题。题目要求我们找到两个n位数相乘得到的最大的回文数。下面我将详细解释题目的要求、解题思路以及具体的代码实现。

    题目描述

    给定一个整数n,返回两个n位数的乘积中的最大回文数。如果没有找到,则返回0。

    解题思路

    1. 定义回文数:回文数是指从左到右和从右到左读都相同的数,例如121、12321等。
    2. 生成n位数:首先需要生成所有可能的n位数。
    3. 寻找最大回文数乘积:通过双重循环遍历所有可能的n位数对,计算它们的乘积,并检查是否为回文数。记录最大的回文数乘积。

    具体步骤

    1. 生成n位数:可以通过计算10^(n-1)到10^n-1之间的所有数来生成n位数。
    2. 检查回文数:编写一个函数来判断一个数是否为回文数。
    3. 遍历并记录最大回文数乘积:使用双重循环遍历所有n位数对,计算乘积并检查是否为回文数,记录最大的回文数乘积。

    代码实现

    以下是Python代码实现:

    class Solution: def largestPalindrome(self, n: int) -> int: if n == 1: return 9

        # 生成n位数的范围
        start = 10**(n-1)
        end = 10**n - 1
    
        # 检查是否为回文数的函数
        def is_palindrome(num):
            return str(num) == str(num)[::-1]
    
        max_palindrome = 0
    
        # 遍历所有可能的n位数对
        for i in range(end, start - 1, -1):
            for j in range(i, start - 1, -1):
                product = i * j
                if is_palindrome(product):
                    max_palindrome = max(max_palindrome, product)
    
        return max_palindrome

    示例使用

    sol = Solution() print(sol.largestPalindrome(2)) # 输出: 9009 (91 * 99)

    优化思路

    上述代码虽然能够解决问题,但时间复杂度较高,为O(n^2 * 10^(2n))。可以通过以下方法进行优化:

    1. 数学性质:利用回文数的数学性质,例如回文数可以表示为a * 10^n + b,其中ba的逆序数。
    2. 减少遍历范围:由于乘积是对称的,可以减少遍历的范围。

    优化后的代码

    class Solution: def largestPalindrome(self, n: int) -> int: if n == 1: return 9

        upper_limit = 10**n - 1
        lower_limit = 10**(n-1)
    
        for i in range(upper_limit, lower_limit - 1, -1):
            # 构造回文数
            palindrome = int(str(i) + str(i)[::-1])
            # 从大到小尝试除以n位数
            for j in range(upper_limit, lower_limit - 1, -1):
                if palindrome // j > upper_limit:
                    break
                if palindrome % j == 0:
                    return palindrome
    
        return 0

    示例使用

    sol = Solution() print(sol.largestPalindrome(2)) # 输出: 9009 (91 * 99)

    总结

    通过上述方法,我们可以有效地找到两个n位数相乘得到的最大回文数。优化后的代码利用了回文数的数学性质,减少了遍历的范围,提高了效率。希望这个详细的解答对你有所帮助!如果有更多问题,欢迎继续提问。

  • Generate Random Point in a Circle,LeetCode,478

    LeetCode 478题是“生成随机点在圆内”(Generate Random Point in a Circle),这是一个中等难度的题目,主要考察的是数学和随机数生成的知识。

    题目描述

    给定圆的半径和圆心位置,要求随机生成圆内的一个点。

    示例:

    输入: ["Solution", "randPoint", "randPoint", "randPoint"] [[1.0, 0.0, 0.0], [], [], []] 输出: [null, [-0.02493, -0.38077], [0.82314, 0.38945], [0.36572, 0.17248]] 解释: Solution solution = new Solution(1.0, 0.0, 0.0); solution.randPoint(); // 返回[-0.02493, -0.38077] solution.randPoint(); // 返回[0.82314, 0.38945] solution.randPoint(); // 返回[0.36572, 0.17248]

    解题思路

    要随机生成圆内的一个点,可以使用极坐标的方法。具体步骤如下:

    1. 生成随机半径:由于圆的面积与半径的平方成正比,直接均匀生成半径会导致点集中在圆心附近。因此,我们需要生成一个与半径平方成正比的随机数。具体来说,可以生成一个[0, 1]之间的随机数,然后取其平方根,再乘以圆的半径。
    2. 生成随机角度:在[0, 2π]之间均匀生成一个随机角度。
    3. 转换为直角坐标:使用极坐标到直角坐标的转换公式:
      • x = r * cos(θ)
      • y = r * sin(θ)
    4. 平移到圆心:将生成的点平移到给定的圆心位置。

    代码实现

    以下是Python的实现代码:

    import random import math

    class Solution: def init(self, radius: float, x_center: float, y_center: float): self.radius = radius self.x_center = x_center self.y_center = y_center

    def randPoint(self):
        # 生成随机半径
        r = self.radius * math.sqrt(random.random())
        # 生成随机角度
        theta = random.uniform(0, 2 * math.pi)
        # 转换为直角坐标
        x = r * math.cos(theta)
        y = r * math.sin(theta)
        # 平移到圆心
        return [self.x_center + x, self.y_center + y]

    示例使用

    solution = Solution(1.0, 0.0, 0.0) print(solution.randPoint()) # 返回一个随机点 print(solution.randPoint()) # 返回一个随机点 print(solution.randPoint()) # 返回一个随机点

    解释

    1. 初始化:在构造函数中,我们存储了圆的半径和圆心的坐标。
    2. 生成随机点
      • math.sqrt(random.random())生成一个[0, 1]之间的随机数的平方根,确保生成的半径符合圆的面积分布。
      • random.uniform(0, 2 * math.pi)生成一个[0, 2π]之间的随机角度。
      • 使用极坐标到直角坐标的转换公式计算出点的坐标。
      • 最后将生成的点平移到圆心位置。

    这种方法能够均匀地在圆内生成随机点,符合题目要求。

    希望这个详细的解答对你有帮助!如果有其他问题,欢迎继续提问。

  • Total Hamming Distance,LeetCode,477

    LeetCode 477题 “Total Hamming Distance” 是一个中等难度的位操作问题。题目要求计算一个数组中所有数对之间的汉明距离的总和。汉明距离是指两个整数对应二进制位不同的位置的数目。

    题目描述

    给定一个非空整数数组 nums,返回数组中所有数对之间的汉明距离的总和。

    示例

    输入: nums = [4, 14, 2] 输出: 6 解释: 在二进制表示中,4表示为 0100,14表示为 1110,2表示为 0010。 所以它们之间的汉明距离分别为: HammingDistance(4, 14) = 2 HammingDistance(4, 2) = 2 HammingDistance(14, 2) = 2 所以总汉明距离为 6。

    解法思路

    直接计算每两个数之间的汉明距离会导致时间复杂度为 O(n^2),我们可以通过位操作优化到 O(n)。

    核心思想

    1. 对于每一个二进制位,统计数组中在该位上为 1 的数的个数。
    2. 对于每一位,汉明距离的贡献为该位上为 1 的数的个数乘以该位上为 0 的数的个数。

    具体步骤

    1. 初始化 total_distance 为 0。
    2. 遍历每一位(从 0 到 31,假设数组中的数不超过 2^31 – 1)。
    3. 对于每一位,统计数组中在该位上为 1 的数的个数 count_1
    4. 该位的汉明距离贡献为 count_1 * (n - count_1),其中 n 是数组的长度。
    5. 将该位的贡献加到 total_distance 中。
    6. 返回 total_distance

    代码实现

    class Solution: def totalHammingDistance(self, nums): total_distance = 0 n = len(nums)

        for i in range(32):  # 遍历每一位
            count_1 = 0
            for num in nums:
                count_1 += (num >> i) & 1  # 统计在第 i 位上为 1 的数的个数
            count_0 = n - count_1
            total_distance += count_1 * count_0  # 计算该位的汉明距离贡献
    
        return total_distance

    解释

    • (num >> i) & 1:将 num 右移 i 位后与 1 进行按位与操作,得到第 i 位的值。
    • count_1 * count_0:在第 i 位上,为 1 的数和为 0 的数之间的汉明距离贡献。

    复杂度分析

    • 时间复杂度:O(n * 32) = O(n),因为我们需要遍历每一位,共 32 位,每次遍历数组。
    • 空间复杂度:O(1),只使用了常数额外空间。

    通过这种方法,我们能够高效地计算出数组中所有数对之间的汉明距离的总和。希望这个解释对你理解这道题有所帮助!如果有更多问题,欢迎继续提问。