作者: admin2025

  • Repeated Substring Pattern,LeetCode,459

    题目描述:

    给定一个非空的字符串 s,检查它是否可以通过重复某个子串多次构成。你可以假设字符串 s 仅由小写英文字母组成,并且它的长度不会超过 10000。

    示例:

    输入: "abab" 输出: True 解释: 可由子串 "ab" 重复两次构成。

    输入: "aba" 输出: False

    输入: "abcabcabcabc" 输出: True 解释: 可由子串 "abc" 重复四次构成。 (或者子串 "abcabc" 重复两次构成。)

    解题思路:

    1. 字符串拼接法:
      • 将字符串 s 拼接一遍,即形成 s + s
      • 去掉拼接后的字符串的第一个字符和最后一个字符,形成新的字符串 new_s
      • 检查原字符串 s 是否在 new_s 中出现。
      • 如果出现,则说明 s 可以通过重复某个子串多次构成。
    2. 数学法:
      • 设字符串 s 的长度为 n
      • 遍历所有可能的子串长度 i(从 1 到 n/2),检查 n 是否能被 i 整除。
      • 如果 n 能被 i 整除,则检查长度为 i 的子串是否能够通过重复构成原字符串 s
      • 如果找到这样的子串,则返回 True;否则返回 False

    代码实现(字符串拼接法):

    class Solution: def repeatedSubstringPattern(self, s: str) -> bool: if not s: return False

        new_s = (s + s)[1:-1]
        return s in new_s

    代码实现(数学法):

    class Solution: def repeatedSubstringPattern(self, s: str) -> bool: n = len(s) for i in range(1, n // 2 + 1): if n % i == 0: substring = s[:i] if substring * (n // i) == s: return True return False

    解释:

    1. 字符串拼接法:
      • new_s = (s + s)[1:-1] 的目的是去掉拼接后的字符串的首尾字符,避免直接包含原字符串。
      • s in new_s 检查原字符串是否在新字符串中出现,如果出现则说明可以通过重复某个子串构成。
    2. 数学法:
      • n % i == 0 检查 n 是否能被 i 整除,确保子串长度 in 的因数。
      • substring = s[:i] 提取长度为 i 的子串。
      • substring * (n // i) == s 检查通过重复子串 substring 是否能构成原字符串 s

    复杂度分析:

    • 字符串拼接法:
      • 时间复杂度:O(n),其中 n 是字符串的长度。
      • 空间复杂度:O(n),因为生成了新的字符串 new_s
    • 数学法:
      • 时间复杂度:O(n * sqrt(n)),最坏情况下需要遍历所有可能的子串长度,并检查子串的重复构成。
      • 空间复杂度:O(1),只使用了常数额外空间。

    这两种方法各有优缺点,字符串拼接法实现简单且效率较高,而数学法更直观地展示了问题的本质。根据实际情况选择合适的方法即可。

  • Circular Array Loop,LeetCode,457

    LeetCode 457题 “Circular Array Loop” 是一个中等难度的算法题。题目要求我们检测一个数组中是否存在一个循环,该循环满足以下条件:

    1. 循环中的元素值必须一致(即循环中的所有元素值要么都是正数,要么都是负数)。
    2. 循环的长度必须大于1。

    数组的每个元素都指向数组中的一个位置,即 nums[i] 表示从位置 i 跳到位置 (i + nums[i]) % n(其中 n 是数组的长度)。

    题目描述

    给定一个含有正整数和负整数的数组 nums,如果数组中存在一个循环,则返回 true,否则返回 false

    示例

    输入: [2,-1,1,2,2] 输出: true 解释: 有一个循环,从索引 0 -> 2 -> 3 -> 0。

    输入: [-1,2] 输出: false 解释: 没有循环。

    解题思路

    我们可以使用快慢指针(Floyd’s Tortoise and Hare)算法来检测循环。具体步骤如下:

    1. 初始化快慢指针:慢指针 slow 和快指针 fast 都从当前索引 i 开始。
    2. 移动指针
      • 慢指针每次移动一步。
      • 快指针每次移动两步。
    3. 检查循环
      • 如果快指针或慢指针指向的值与当前值符号不同,则不存在循环。
      • 如果快慢指针相遇,检查循环长度是否大于1。
    4. 避免重复检查:使用一个集合或标记数组来记录已经检查过的索引。

    代码实现

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

    def circularArrayLoop(nums): n = len(nums)

    def next_index(i):
        return (i + nums[i]) % n
    
    for i in range(n):
        if nums[i] == 0:
            continue
    
        slow, fast = i, i
        while True:
            slow = next_index(slow)
            fast = next_index(next_index(fast))
    
            if slow == fast:
                break
    
            if nums[slow] * nums[i] < 0 or nums[fast] * nums[i] < 0:
                break
    
        if slow == fast:
            # 检查循环长度是否大于1
            loop_length = 1
            fast = next_index(slow)
            while fast != slow:
                fast = next_index(fast)
                loop_length += 1
    
            if loop_length > 1:
                return True
    
    return False

    示例

    print(circularArrayLoop([2, -1, 1, 2, 2])) # 输出: True print(circularArrayLoop([-1, 2])) # 输出: False

    解释

    1. next_index函数:计算下一个索引位置。
    2. 外层循环:遍历每个元素作为起点。
    3. 内层循环:使用快慢指针检测循环。
    4. 循环长度检查:如果快慢指针相遇,检查循环长度是否大于1。

    复杂度分析

    • 时间复杂度:O(n),每个元素最多被访问两次。
    • 空间复杂度:O(1),使用了常数额外空间。

    通过上述方法,我们可以有效地检测数组中是否存在满足条件的循环。希望这个解释对你理解这道题目有所帮助!如果有更多问题,欢迎继续提问。

  • 4Sum II,LeetCode,454

    题目描述:

    给定四个包含整数的数组 nums1, nums2, nums3, nums4,计算有多少个元组 (i, j, k, l) 使得 nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

    示例:

    输入: nums1 = [1, 2] nums2 = [-2, -1] nums3 = [-1, 2] nums4 = [0, 2]

    输出: 2

    解释: 两个元组如下:

    1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
    2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0

    解题思路:

    这道题可以使用哈希表来优化求解过程。具体步骤如下:

    1. 构建哈希表:
      • 创建一个哈希表 sum_map,用于存储 nums1nums2 中所有可能的两数之和及其出现的次数。
    2. 填充哈希表:
      • 遍历 nums1nums2,计算每一对数的和 sum,并将该和及其出现的次数存储在 sum_map 中。
    3. 查找匹配的元组:
      • 遍历 nums3nums4,计算每一对数的和的相反数 -sum
      • sum_map 中查找是否存在该相反数,如果存在,则将其出现的次数累加到结果中。

    代码实现:

    def fourSumCount(nums1, nums2, nums3, nums4): sum_map = {} result = 0

    # 构建哈希表,存储 nums1 和 nums2 的所有两数之和及其出现的次数
    for num1 in nums1:
        for num2 in nums2:
            sum_ = num1 + num2
            if sum_ in sum_map:
                sum_map[sum_] += 1
            else:
                sum_map[sum_] = 1
    
    # 遍历 nums3 和 nums4,查找匹配的元组
    for num3 in nums3:
        for num4 in nums4:
            target = -(num3 + num4)
            if target in sum_map:
                result += sum_map[target]
    
    return result

    示例

    nums1 = [1, 2] nums2 = [-2, -1] nums3 = [-1, 2] nums4 = [0, 2] print(fourSumCount(nums1, nums2, nums3, nums4)) # 输出: 2

    复杂度分析:

    • 时间复杂度: O(n^2),其中 n 是每个数组的长度。我们需要遍历 nums1nums2 来构建哈希表,再遍历 nums3nums4 来查找匹配的元组。
    • 空间复杂度: O(n^2),哈希表的大小取决于 nums1nums2 中所有可能的两数之和的数量。

    总结:

    这道题通过巧妙地使用哈希表,将四数之和的问题转化为两数之和的问题,大大减少了计算复杂度。这种方法在处理多数组求和问题时非常有效,值得借鉴。

  • Delete Node in a BST,LeetCode,450

    好的,我们来详细讨论一下 LeetCode 上的第 450 题:删除二叉搜索树(BST)中的节点。

    题目描述

    给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回删除后的二叉搜索树的根节点。

    解题思路

    删除二叉搜索树中的节点可以分为以下几种情况:

    1. 节点不存在:如果 root 为空或者找不到 key,直接返回 root
    2. 节点是叶子节点:直接删除该节点,返回 null
    3. 节点只有一个子节点:删除该节点,并用其子节点替代。
    4. 节点有两个子节点
      • 找到该节点的右子树中的最小节点(即后继节点)。
      • 将该节点的值替换为后继节点的值。
      • 删除后继节点。

    代码实现

    以下是使用递归实现的代码:

    class TreeNode: def init(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right

    class Solution: def deleteNode(self, root: TreeNode, key: int) -> TreeNode: if not root: return root

        if key < root.val:
            root.left = self.deleteNode(root.left, key)
        elif key > root.val:
            root.right = self.deleteNode(root.right, key)
        else:
            # 找到了要删除的节点
            if not root.left:
                return root.right
            elif not root.right:
                return root.left
            else:
                # 找到右子树中的最小节点
                min_node = self.findMin(root.right)
                root.val = min_node.val
                root.right = self.deleteNode(root.right, min_node.val)
    
        return root
    
    def findMin(self, node: TreeNode) -> TreeNode:
        while node.left:
            node = node.left
        return node

    详细解释

    1. 基本情况
      • 如果 root 为空,直接返回 null
    2. 递归查找
      • 如果 key 小于当前节点的值,递归地在左子树中查找并删除。
      • 如果 key 大于当前节点的值,递归地在右子树中查找并删除。
    3. 删除操作
      • 当找到要删除的节点时,根据其子节点的数量进行不同的处理:
        • 如果没有左子节点,返回右子节点。
        • 如果没有右子节点,返回左子节点。
        • 如果有两个子节点,找到右子树中的最小节点(后继节点),将其值赋给当前节点,并递归地删除后继节点。
    4. 辅助函数 findMin
      • 用于找到某个节点的右子树中的最小节点。

    复杂度分析

    • 时间复杂度:O(h),其中 h 是树的高度。在最坏情况下,树可能是一条链,时间复杂度为 O(n)。
    • 空间复杂度:O(h),递归调用栈的深度。

    总结

    删除二叉搜索树中的节点是一个经典问题,需要处理多种情况。通过递归的方式,我们可以简洁地实现这一功能。关键在于正确处理有两个子节点的节点,通过找到后继节点并进行替换,保证二叉搜索树的性质不变。

    希望这个详细的解答对你有所帮助!如果有任何进一步的问题,欢迎继续提问。

  • Serialize and Deserialize BST,LeetCode,449

    LeetCode 449题是关于二叉搜索树(BST)的序列化和反序列化问题。序列化是将一个数据结构或对象状态转换为可存储或传输的格式的过程,以便在需要时可以恢复该数据结构或对象状态。反序列化是序列化的逆过程。

    题目描述

    你需要设计一个算法来实现二叉搜索树的序列化和反序列化。序列化是将一棵二叉搜索树转换为一个字符串,而反序列化是将一个字符串转换回一棵二叉搜索树。

    要求

    • 序列化和反序列化的算法应该是高效的,即时间复杂度和空间复杂度尽可能低。
    • 序列化的结果应该是一个字符串,反序列化的输入也应该是一个字符串。

    解决思路

    由于二叉搜索树具有有序性,可以利用前序遍历和中序遍历的特性来进行序列化和反序列化。但更常见的方法是使用前序遍历结合null标记来进行序列化,这样可以简化反序列化的过程。

    1. 序列化

    使用前序遍历(根-左-右)来遍历树,并将每个节点的值转换为字符串,如果遇到null节点,则用特殊字符(如”#”)来表示。

    2. 反序列化

    将序列化后的字符串按前序遍历的顺序重新构建二叉搜索树。使用一个队列来存储节点值,逐个构建节点并按前序遍历的顺序连接左右子树。

    代码实现(Python)

    class TreeNode: def init(self, x): self.val = x self.left = None self.right = None

    class Codec: def serialize(self, root): """Encodes a tree to a single string.""" if not root: return ""

        def preorder(node):
            if not node:
                return "# "
            return str(node.val) + " " + preorder(node.left) + preorder(node.right)
    
        return preorder(root)
    
    def deserialize(self, data):
        """Decodes your encoded data to tree."""
        if not data:
            return None
    
        def build_tree(values):
            val = values.pop(0)
            if val == "#":
                return None
            node = TreeNode(int(val))
            node.left = build_tree(values)
            node.right = build_tree(values)
            return node
    
        values = data.split()
        return build_tree(values)

    Your Codec object will be instantiated and called as such:

    ser = Codec()

    deser = Codec()

    tree = deser.deserialize(ser.serialize(root))

    详细解释

    1. 序列化(serialize):
      • 使用递归函数preorder进行前序遍历。
      • 如果当前节点为null,返回”#”和一个空格。
      • 否则,返回当前节点的值和一个空格,然后递归处理左子树和右子树。
    2. 反序列化(deserialize):
      • 将输入的字符串按空格分割成列表values
      • 使用递归函数build_tree来构建树:
        • values中取出第一个元素,如果是”#”,则返回None。
        • 否则,创建一个新节点,递归构建其左子树和右子树。

    复杂度分析

    • 时间复杂度:序列化和反序列化均为O(n),其中n是树中节点的数量。
    • 空间复杂度:序列化使用O(h)的递归栈空间,反序列化使用O(n)的空间来存储节点值。

    这种方法利用了二叉搜索树的性质和前序遍历的特性,实现了高效的序列化和反序列化。

  • Arranging Coins,LeetCode,441

    LeetCode 441题 “Arranging Coins” 是一个中等难度的数学问题。题目描述如下:

    你总共有 n 枚硬币,并希望将它们排列成一个阶梯形状,第 k 行必须正好有 k 枚硬币。

    给你一个整数 n,返回你可以完整排列的行数。

    示例 1:

    输入: n = 5 输出: 2 解释: 因为第三行不完整,所以返回 2.

    示例 2:

    输入: n = 8 输出: 3 解释: 因为第四行不完整,所以返回 3.

    解题思路

    这个问题可以通过数学公式和二分查找来解决。

    方法一:数学公式

    我们知道,前 k 行需要的硬币总数是: [ S = \frac{k(k + 1)}{2} ]

    我们需要找到一个最大的 k,使得: [ \frac{k(k + 1)}{2} \leq n ]

    这可以通过解以下不等式得到: [ k^2 + k – 2n \leq 0 ]

    使用求根公式: [ k = \frac{-b \pm \sqrt{b^2 – 4ac}}{2a} ] 其中,a = 1b = 1c = -2n

    所以: [ k = \frac{-1 \pm \sqrt{1 + 8n}}{2} ]

    我们取正根: [ k = \frac{-1 + \sqrt{1 + 8n}}{2} ]

    最后取整即可。

    方法二:二分查找

    我们可以使用二分查找来找到最大的 k,使得: [ \frac{k(k + 1)}{2} \leq n ]

    初始化 left = 1right = n,然后进行二分查找。

    代码实现

    方法一:数学公式

    import math

    def arrangeCoins(n: int) -> int: return int((-1 + math.sqrt(1 + 8 * n)) // 2)

    示例

    print(arrangeCoins(5)) # 输出: 2 print(arrangeCoins(8)) # 输出: 3

    方法二:二分查找

    def arrangeCoins(n: int) -> int: left, right = 1, n while left <= right: mid = (left + right) // 2 current = mid * (mid + 1) // 2 if current == n: return mid elif current < n: left = mid + 1 else: right = mid - 1 return right

    示例

    print(arrangeCoins(5)) # 输出: 2 print(arrangeCoins(8)) # 输出: 3

    总结

    • 数学公式法:通过求解一元二次方程来直接计算结果,时间复杂度为 (O(1))。
    • 二分查找法:通过二分查找逐步逼近结果,时间复杂度为 (O(\log n))。

    两种方法各有优劣,数学公式法更直观且速度快,但可能存在浮点数精度问题;二分查找法更通用且精度高,但稍微复杂一些。

    希望这些详细的解释和代码示例能帮助你理解和解决这个题目!如果有更多问题,欢迎继续提问。

  • K-th Smallest in Lexicographical Order,LeetCode,440

    LeetCode 440题 “K-th Smallest in Lexicographical Order” 是一个中等难度的题目,主要考察的是字典序(lexicographical order)和数字的排列组合。

    题目描述

    给定整数 nk,找到从 1n 的所有整数按字典序排列后的第 k 个数。

    字典序排列是指将数字视为字符串,按照字典顺序排列。例如,对于 n = 13,字典序排列为 [1, 10, 11, 12, 13, 2, 3, 4, 5, 6, 7, 8, 9]

    解法思路

    这个问题可以通过递归和数学的方法来解决。核心思想是利用字典树的性质,逐步缩小范围,找到第 k 个数。

    步骤:

    1. 初始化:从根节点 1 开始。
    2. 计算子节点数量:对于当前节点 curr,计算从 currcurr + 1 之间的所有子节点的数量。
    3. 比较 k 和子节点数量
      • 如果 k 小于等于子节点数量,说明第 k 个数在当前节点的子树中,更新 currcurr * 10 并继续搜索。
      • 如果 k 大于子节点数量,说明第 k 个数不在当前节点的子树中,更新 currcurr + 1 并减去当前子树节点的数量,继续搜索。

    关键函数:

    • countSteps(curr, n):计算从 currcurr + 1 之间的所有子节点的数量。

    代码实现

    class Solution: def findKthNumber(self, n, k): curr = 1 k -= 1 # 因为从1开始计数,所以k减1

        while k > 0:
            steps = self.countSteps(curr, n)
            if steps <= k:
                # 第k个数不在当前子树中
                curr += 1
                k -= steps
            else:
                # 第k个数在当前子树中
                curr *= 10
                k -= 1
    
        return curr
    
    def countSteps(self, curr, n):
        steps = 0
        first = curr
        last = curr
    
        while first <= n:
            steps += min(n + 1, last + 1) - first
            first *= 10
            last = last * 10 + 9
    
        return steps

    示例

    sol = Solution() print(sol.findKthNumber(13, 2)) # 输出: 10 print(sol.findKthNumber(13, 5)) # 输出: 13

    解释

    1. 初始化curr1 开始,k1 是因为从 1 开始计数。
    2. 循环:当 k 大于 时,计算从 currcurr + 1 之间的子节点数量。
    3. 子节点数量计算
      • firstlast 分别表示当前层的起始和结束节点。
      • 通过循环扩展 firstlast 到下一层,累加每层的节点数量。
    4. 更新 currk
      • 如果 k 大于子节点数量,说明第 k 个数不在当前子树中,curr 增加 1k 减去当前子树节点数量。
      • 如果 k 小于等于子节点数量,说明第 k 个数在当前子树中,curr 扩展到下一层(curr * 10),k 减去 1(因为进入下一层)。

    通过这种方法,逐步缩小范围,最终找到第 k 个数。

    希望这个详细的解释和代码实现能帮助你理解并解决这个题目!如果有任何进一步的问题,欢迎继续提问。

  • Combination Sum IV,LeetCode,377

    题目描述:

    LeetCode 377题 “Combination Sum IV” 是一个关于组合数学的动态规划问题。题目要求给定一个由正整数组成的数组 nums 和一个目标值 target,找出所有可能的组合,使得这些组合中的数字之和等于 target。每个数字在组合中可以无限次使用,但组合中的顺序不同的序列被认为是不同的组合。

    示例:

    输入: nums = [1, 2, 3], target = 4 输出: 7 解释: 所有可能的组合为: (1, 1, 1, 1) (1, 1, 2) (1, 2, 1) (1, 3) (2, 1, 1) (2, 2) (3, 1)

    解题思路:

    这个问题可以通过动态规划来解决。我们定义一个数组 dp,其中 dp[i] 表示和为 i 的组合数。初始时,dp[0] = 1,因为和为0的组合只有一种,即什么也不选。

    接下来,我们遍历从1到target的每一个数,对于每一个数i,我们再遍历nums数组中的每一个数num,如果i >= num,则dp[i] += dp[i - num]。这是因为如果我们可以通过某种方式组合出i - num,那么再加上num就可以组合出i

    代码实现:

    def combinationSum4(nums, target):

    初始化dp数组,长度为target + 1,初始值全为0

    dp = [0] * (target + 1)
    # 和为0的组合只有一种,即什么也不选
    dp[0] = 1
    
    # 遍历从1到target的每一个数
    for i in range(1, target + 1):
        # 遍历nums数组中的每一个数
        for num in nums:
            # 如果当前数i大于等于num,则更新dp[i]
            if i >= num:
                dp[i] += dp[i - num]
    
    # 返回和为target的组合数
    return dp[target]

    示例

    nums = [1, 2, 3] target = 4 print(combinationSum4(nums, target)) # 输出: 7

    详细解释:

    1. 初始化dp数组:
      • dp数组的大小为target + 1,因为我们需要计算从0到target的所有可能的组合数。
      • dp[0] = 1是因为和为0的组合只有一种,即什么也不选。
    2. 外层循环:
      • 遍历从1到target的每一个数i,表示我们当前要计算和为i的组合数。
    3. 内层循环:
      • 遍历nums数组中的每一个数num,表示我们尝试使用num来组合出和为i的情况。
      • 如果i >= num,说明我们可以使用num来组合出和为i的情况,此时dp[i]应该加上dp[i - num],因为dp[i - num]表示和为i - num的组合数,加上num后就能组合出和为i的情况。
    4. 返回结果:
      • 最终dp[target]就是我们要求的和为target的组合数。

    时间复杂度:

    • 时间复杂度为O(target * n),其中nnums数组的长度。这是因为我们需要遍历从1到target的每一个数,并且对于每一个数,我们还需要遍历nums数组中的每一个数。

    空间复杂度:

    • 空间复杂度为O(target),因为我们使用了一个长度为target + 1的数组来存储中间结果。

    通过上述分析和代码实现,我们可以有效地解决LeetCode 377题 “Combination Sum IV”。

  • Wiggle Subsequence,LeetCode,376

    题目描述:

    LeetCode 376题 Wiggle Subsequence(摆动序列)要求我们找到一个序列的最长摆动子序列的长度。摆动序列的定义是:序列中的元素在相邻元素之间交替上升和下降。

    题目示例:

    输入: [1,7,4,9,2,5] 输出: 6 解释: 整个序列就是一个摆动序列。

    输入: [1,17,5,10,13,15,10,5,16,8] 输出: 7 解释: 序列中的最长摆动子序列是 [1,17,10,13,10,16,8]。

    解题思路:

    我们可以使用贪心算法来解决这个问题。具体思路如下:

    1. 初始化: 定义两个变量 updown,分别表示当前序列中上升和下降的最长摆动子序列的长度。初始时都设为1,因为任何一个单独的元素都可以看作是一个摆动序列。
    2. 遍历序列: 从第二个元素开始遍历整个序列,对于每一个元素,根据它与前一个元素的关系来更新 updown
      • 如果当前元素大于前一个元素,说明这是一个上升的过程,此时 up 应该更新为 down + 1,因为上升序列可以接在之前的下降序列后面。
      • 如果当前元素小于前一个元素,说明这是一个下降的过程,此时 down 应该更新为 up + 1,因为下降序列可以接在之前的上升序列后面。
    3. 结果: 遍历完成后,updown 中的最大值就是最长摆动子序列的长度。

    代码实现(Python):

    def wiggleMaxLength(nums): if not nums: return 0 up = down = 1 for i in range(1, len(nums)): if nums[i] > nums[i - 1]: up = down + 1 elif nums[i] < nums[i - 1]: down = up + 1 return max(up, down)

    示例

    print(wiggleMaxLength([1,7,4,9,2,5])) # 输出: 6 print(wiggleMaxLength([1,17,5,10,13,15,10,5,16,8])) # 输出: 7

    复杂度分析:

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

    总结:

    这个题目通过贪心算法可以高效解决,关键在于正确理解和维护上升和下降序列的长度。通过简单的遍历和更新操作,我们能够在线性时间内找到最长摆动子序列的长度。这种方法不仅简洁,而且效率高,适合处理大规模数据。

  • Sum of Two Integers,LeetCode,371

    LeetCode 371题是“两整数之和”(Sum of Two Integers),这道题要求在不使用加号(+)和减号(-)的情况下,计算两个整数的和。

    题目描述

    给定两个整数 ab,求它们的和。

    注意

    • 你不能使用加号(+)和减号(-)。
    • 你可以使用位运算。

    解题思路

    这道题可以通过位运算来解决。位运算主要包括以下几种:

    • 与运算(&):用于找出两个数的二进制表示中同时为1的位。
    • 异或运算(^):用于找出两个数的二进制表示中不同时为1的位。
    • 左移运算(<<):用于将数的二进制表示向左移动指定的位数。

    具体步骤如下:

    1. 异或运算a ^ b 可以得到两个数相加但不考虑进位的结果。
    2. 与运算和左移(a & b) << 1 可以得到两个数相加时的进位结果。
    3. 重复上述两个步骤,直到进位结果为0。

    代码实现

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

    class Solution: def getSum(self, a: int, b: int) -> int:

    32位整数的最大值和最小值

        MAX = 0x7FFFFFFF
        MIN = 0x80000000
        # 掩码,用于获取32位整数
        mask = 0xFFFFFFFF
    
        while b != 0:
            # 计算不进位的和
            a, b = (a ^ b) & mask, ((a & b) << 1) & mask
    
        # 如果a是负数,将其转换成补码形式
        return a if a <= MAX else ~(a ^ mask)

    代码解释

    1. 掩码(mask):用于确保计算结果始终在32位整数的范围内。
    2. 循环:当 b 不为0时,继续计算不进位的和和进位。
    3. 返回结果:如果 a 是负数,需要将其转换成补码形式。

    示例

    假设 a = 1b = 2

    1. 第一次循环:
      • a = 1 ^ 2 = 3
      • b = (1 & 2) << 1 = 0
    2. 由于 b 为0,循环结束,返回 a = 3

    复杂度分析

    • 时间复杂度:O(1),因为每次循环都会减少进位的位数,最坏情况下循环的次数是32(整数的位数)。
    • 空间复杂度:O(1),只使用了常数空间。

    通过这种方法,我们可以在不使用加号和减号的情况下,计算出两个整数的和。希望这个解释对你有帮助!如果有更多问题,欢迎继续提问。