← 返回

LeetCode HOT100 简单题

首发 2026/05/10 阅读 9 评论 0 更新 2026/05/10

LeetCode HOT100 简单题详细学习版

目录

    1. LC 1 两数之和
    1. LC 20 有效的括号
    1. LC 21 合并两个有序链表
    1. LC 70 爬楼梯
    1. LC 94 二叉树的中序遍历
    1. LC 101 对称二叉树
    1. LC 104 二叉树的最大深度
    1. LC 121 买卖股票的最佳时机
    1. LC 136 只出现一次的数字
    1. LC 141 环形链表
    1. LC 160 相交链表
    1. LC 169 多数元素
    1. LC 206 反转链表
    1. LC 226 翻转二叉树
    1. LC 234 回文链表
    1. LC 283 移动零
    1. LC 338 比特位计数
    1. LC 448 找到所有数组中消失的数字
    1. LC 461 汉明距离
    1. LC 543 二叉树的直径
    1. LC 617 合并二叉树

1. LC 1 两数之和

难度:简单
标签:数组、哈希表

原题简述

给定数组和 target,返回两个不同下标,使 nums[i]+nums[j]=target。

知识点介绍

哈希表把“找补数”变成 O(1) 查询。

标准思路

遍历每个数 num,检查 target-num 是否已经出现;没出现就记录 num 的下标。

最优标准解

python
from typing import List

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        seen = {}                             # 记录已经遍历过的数字:数字 -> 下标
        for i, num in enumerate(nums):        # 枚举当前数字,尝试和前面数字配对
            need = target - num               # 当前数字需要 need 这个补数才能凑成 target
            if need in seen:                  # 补数已经在前面出现,当前下标和补数下标就是答案
                return [seen[need], i]        # 返回补数的下标和当前数字的下标
            seen[num] = i                     # 当前数字暂时没有配对成功,存起来给后面的数字用

复杂度

时间 O(n),空间 O(n)。

易错点

先查补数再存当前数,避免同一个元素被用两次。

多解补充

多解 1:暴力枚举

取舍说明: 能做但 O(n²),只适合理解题意。

python
from typing import List

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):            # 枚举当前数字,尝试和前面数字配对
            for j in range(i + 1, len(nums)):
                if nums[i] + nums[j] == target:  # 检查这一对数字是否正好等于 target
                    return [i, j]

快速复习

查配对:哈希表记录已出现数字。


2. LC 20 有效的括号

难度:简单
标签:字符串、栈

原题简述

判断括号字符串是否按正确类型和顺序闭合。

知识点介绍

后打开的左括号必须先被关闭,符合栈的后进先出。

标准思路

左括号入栈;右括号检查栈顶是否是对应左括号;最后栈为空才有效。

最优标准解

python
class Solution:
    def isValid(self, s: str) -> bool:
        stack = []                            # 保存还没有匹配成功的左括号
        match = {')': '(', ']': '[', '}': '{'}  # 记录每种右括号对应的左括号
        for ch in s:                          # 从左到右检查每个括号
            if ch in match:                   # 遇到右括号,需要和栈顶左括号匹配
                if not stack or stack[-1] != match[ch]:  # 栈空或栈顶类型不对,括号无效
                    return False              # 当前情况不符合题意
                stack.pop()                   # 匹配成功,弹出这个左括号
            else:
                stack.append(ch)              # 左括号入栈,等待后续匹配
        return not stack                      # 栈空或栈顶类型不对,括号无效

复杂度

时间 O(n),空间 O(n)。

易错点

右括号出现时栈可能为空;最后要检查栈为空。

多解补充

多解 1:手写分支

取舍说明: 可通过,但重复判断多,不如映射表清晰。

python
class Solution:
    def isValid(self, s: str) -> bool:
        stack = []                            # 保存还没有匹配成功的左括号
        for ch in s:                          # 从左到右检查每个括号
            if ch in '([{':                   # 遇到右括号,需要和栈顶左括号匹配
                stack.append(ch)              # 左括号入栈,等待后续匹配
            else:
                if not stack:                 # 栈空或栈顶类型不对,括号无效
                    return False              # 当前情况不符合题意
                top = stack[-1]               # 栈空或栈顶类型不对,括号无效
                if (ch == ')' and top == '(') or (ch == ']' and top == '[') or (ch == '}' and top == '{'):
                    stack.pop()               # 匹配成功,弹出这个左括号
                else:
                    return False              # 当前情况不符合题意
        return not stack                      # 栈空或栈顶类型不对,括号无效

快速复习

括号匹配:左入栈,右查栈顶。


3. LC 21 合并两个有序链表

难度:简单
标签:链表、递归

原题简述

合并两个升序链表,返回合并后的升序链表头。

知识点介绍

链表题核心是改 next 指针;dummy 统一处理头节点。

标准思路

比较两个链表当前节点,小的接到结果尾部;某条为空后直接接另一条剩余部分。

最优标准解

python
from typing import Optional

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        dummy = ListNode()                    # 虚拟头节点,方便统一处理链表头部变化
        cur = dummy                           # 虚拟头节点,方便统一处理链表头部变化
        while list1 and list2:                # 两个链表都还有节点时,比较当前值
            if list1.val <= list2.val:        # list1 当前节点更小或相等,先接 list1
                cur.next = list1              # 把 list1 当前节点接到结果链表后面
                list1 = list1.next            # list1 后移到下一个节点
            else:
                cur.next = list2              # 把 list2 当前节点接到结果链表后面
                list2 = list2.next            # list2 后移到下一个节点
            cur = cur.next                    # 结果链表尾指针后移
        cur.next = list1 if list1 else list2  # 把 list1 当前节点接到结果链表后面
        return dummy.next                     # 返回虚拟头节点后面的真实链表头

复杂度

时间 O(m+n),空间 O(1)。

易错点

返回 dummy.next;接完节点后原链表指针要后移。

多解补充

多解 1:递归

取舍说明: 同样优秀,代码短;缺点是递归栈 O(m+n)。

python
from typing import Optional

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        if not list1:
            return list2
        if not list2:
            return list1
        if list1.val <= list2.val:            # list1 当前节点更小或相等,先接 list1
            list1.next = self.mergeTwoLists(list1.next, list2)
            return list1
        else:
            list2.next = self.mergeTwoLists(list1, list2.next)
            return list2

快速复习

有序链表合并:dummy + 每次接小的。


4. LC 70 爬楼梯

难度:简单
标签:动态规划

原题简述

每次爬 1 或 2 阶,求到第 n 阶的方法数。

知识点介绍

到第 i 阶只可能来自 i-1 或 i-2。

标准思路

f(i)=f(i-1)+f(i-2),只保留前两项即可。

最优标准解

python
class Solution:
    def climbStairs(self, n: int) -> int:
        if n <= 2:
            return n
        prev2, prev1 = 1, 2
        for _ in range(3, n + 1):
            cur = prev1 + prev2
            prev2, prev1 = prev1, cur
        return prev1

复杂度

时间 O(n),空间 O(1)。

易错点

n=2 的答案是 2;普通递归会超时。

多解补充

多解 1:DP 数组

取舍说明: 更直观但空间 O(n)。

python
class Solution:
    def climbStairs(self, n: int) -> int:
        if n <= 2:
            return n
        dp = [0] * (n + 1)                    # 创建 DP 表,保存子问题答案
        dp[1], dp[2] = 1, 2                   # 创建 DP 表,保存子问题答案
        for i in range(3, n + 1):
            dp[i] = dp[i - 1] + dp[i - 2]     # 创建 DP 表,保存子问题答案
        return dp[n]

快速复习

爬楼梯就是斐波那契。


5. LC 94 二叉树的中序遍历

难度:简单
标签:树、DFS、栈

原题简述

返回二叉树中序遍历结果。

知识点介绍

中序顺序固定为左、根、右。

标准思路

递归处理左子树,记录当前值,再递归右子树。

最优标准解

python
from typing import List, Optional

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans = []
        def dfs(node):
            if not node:                      # 空节点是递归边界
                return                        # 结束当前递归分支
            dfs(node.left)                    # 递归处理左子树
            ans.append(node.val)              # 中序位置访问当前节点
            dfs(node.right)                   # 递归处理右子树
        dfs(root)
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(h)。

易错点

中序是左根右;空树返回 []。

多解补充

多解 1:迭代栈

取舍说明: 同样优秀;不用递归时写这个。

python
from typing import List, Optional

class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        ans, stack = [], []
        cur = root
        while cur or stack:
            while cur:
                stack.append(cur)             # 加入当前路径、节点或中间结果
                cur = cur.left
            cur = stack.pop()
            ans.append(cur.val)
            cur = cur.right
        return ans                            # 返回最终答案

快速复习

中序遍历:左、根、右。


6. LC 101 对称二叉树

难度:简单
标签:树、DFS、BFS

原题简述

判断二叉树是否轴对称。

知识点介绍

对称比较镜像位置:左左对右右,左右对右左。

标准思路

递归比较两个节点是否互为镜像:都空真,一个空假,值不同假,然后比较外侧和内侧。

最优标准解

python
from typing import Optional

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        def check(a, b):
            if not a and not b:
                return True                   # 当前检查通过
            if not a or not b or a.val != b.val:
                return False                  # 当前情况不符合题意
            return check(a.left, b.right) and check(a.right, b.left)
        return True if not root else check(root.left, root.right)  # 交换当前节点左右孩子

复杂度

时间 O(n),空间 O(h)。

易错点

比较镜像位置,不是同方向位置。

多解补充

多解 1:BFS 队列

取舍说明: 同样优秀,一对一对比较镜像节点。

python
from collections import deque
from typing import Optional

class Solution:
    def isSymmetric(self, root: Optional[TreeNode]) -> bool:
        if not root:
            return True                       # 当前检查通过
        q = deque([(root.left, root.right)])  # 交换当前节点左右孩子
        while q:
            a, b = q.popleft()
            if not a and not b:
                continue
            if not a or not b or a.val != b.val:
                return False                  # 当前情况不符合题意
            q.append((a.left, b.right))       # 加入当前路径、节点或中间结果
            q.append((a.right, b.left))       # 加入当前路径、节点或中间结果
        return True                           # 当前检查通过

快速复习

对称树:外侧对外侧,内侧对内侧。


7. LC 104 二叉树的最大深度

难度:简单
标签:树、DFS、BFS

原题简述

返回根节点到最远叶子节点路径上的节点数。

知识点介绍

树深度递归定义:空树为 0,非空为左右深度最大值加 1。

标准思路

递归求左右深度,返回 max(left,right)+1。

最优标准解

python
from typing import Optional

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

复杂度

时间 O(n),空间 O(h)。

易错点

深度按节点数算;空树是 0。

多解补充

多解 1:BFS 层序

取舍说明: 同样优秀,一层一层数。

python
from collections import deque
from typing import Optional

class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        q = deque([root])                     # 队列用于按层遍历二叉树
        depth = 0
        while q:
            depth += 1
            for _ in range(len(q)):
                node = q.popleft()
                if node.left:
                    q.append(node.left)       # 加入当前路径、节点或中间结果
                if node.right:
                    q.append(node.right)      # 加入当前路径、节点或中间结果
        return depth

快速复习

最大深度:左右最大深度 + 1。


8. LC 121 买卖股票的最佳时机

难度:简单
标签:数组、贪心、动态规划

原题简述

只允许买卖一次股票,求最大利润。

知识点介绍

如果今天卖出,最佳买入价一定是今天之前的最低价。

标准思路

遍历价格,维护历史最低价和最大利润。

最优标准解

python
from typing import List

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        min_price = prices[0]                 # 记录到当前为止最低买入价
        ans = 0
        for price in prices[1:]:
            ans = max(ans, price - min_price)  # 记录到当前为止最低买入价
            min_price = min(min_price, price)  # 记录到当前为止最低买入价
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(1)。

易错点

必须先买后卖;一路下跌时返回 0。

多解补充

多解 1:DP 持有/不持有

取舍说明: 可解释为 DP,但会被压缩成两个变量。

python
from typing import List

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        cash = 0                              # cash 表示当天结束后手里没股票的最大利润
        hold = -prices[0]                     # hold 表示当天结束后手里持有股票的最大利润
        for price in prices[1:]:
            cash = max(cash, hold + price)    # cash 表示当天结束后手里没股票的最大利润
            hold = max(hold, -price)          # hold 表示当天结束后手里持有股票的最大利润
        return cash                           # cash 表示当天结束后手里没股票的最大利润

快速复习

股票一次交易:维护最低价。


9. LC 136 只出现一次的数字

难度:简单
标签:数组、位运算

原题简述

除一个数出现一次外,其余数都出现两次,找出唯一数。

知识点介绍

异或性质:x^x=0,x^0=x。

标准思路

把所有数异或,成对数字抵消,剩下唯一数。

最优标准解

python
from typing import List

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        ans = 0                               # 异或初始值为 0,因为 x ^ 0 = x
        for num in nums:
            ans ^= num                        # 成对出现的数字会被异或抵消
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(1)。

易错点

异或符号是 ^;适用前提是其他数都出现两次。

多解补充

多解 1:哈希计数

取舍说明: 直观但空间 O(n)。

python
from typing import List

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        count = {}
        for x in nums:
            count[x] = count.get(x, 0) + 1
        for x, c in count.items():
            if c == 1:
                return x

快速复习

成对抵消:异或。


10. LC 141 环形链表

难度:简单
标签:链表、双指针

原题简述

判断链表是否有环。

知识点介绍

快慢指针:有环时快指针会追上慢指针。

标准思路

slow 每次一步,fast 每次两步;相遇则有环,fast 到空则无环。

最优标准解

python
from typing import Optional

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        slow = fast = head                    # slow 找中点或入环点,fast 用来制造速度差
        while fast and fast.next:             # fast 还能走两步时,继续推进快慢指针
            slow = slow.next                  # slow 每次走一步
            fast = fast.next.next             # fast 每次走两步
            if slow is fast:                  # 快慢指针相遇,说明存在环
                return True                   # 当前检查通过
        return False                          # 当前情况不符合题意

复杂度

时间 O(n),空间 O(1)。

易错点

循环条件是 fast and fast.next;节点比较用 is。

多解补充

多解 1:集合记录节点

取舍说明: 直观但空间 O(n)。

python
from typing import Optional

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        seen = set()
        while head:
            if head in seen:
                return True                   # 当前检查通过
            seen.add(head)
            head = head.next
        return False                          # 当前情况不符合题意

快速复习

链表判环:快慢指针。


11. LC 160 相交链表

难度:简单
标签:链表、双指针

原题简述

返回两个链表相交的起始节点,不相交返回 None。

知识点介绍

相交是节点对象相同;双指针换头让两指针走过相同总长度。

标准思路

pA 走 A+B,pB 走 B+A;有交点相遇,无交点同时 None。

最优标准解

python
from typing import Optional

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        pA, pB = headA, headB                 # pA 从链表 A 出发
        while pA is not pB:                   # 两个指针没走到同一节点时继续同步前进
            pA = pA.next if pA else headB     # pA 从链表 A 出发
            pB = pB.next if pB else headA     # pB 从链表 B 出发
        return pA

复杂度

时间 O(m+n),空间 O(1)。

易错点

比较节点对象;不能修改链表。

多解补充

多解 1:哈希集合

取舍说明: 容易理解但空间 O(m)。

python
from typing import Optional

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        seen = set()
        while headA:                          # B 走完后切到 A,抵消长度差
            seen.add(headA)                   # B 走完后切到 A,抵消长度差
            headA = headA.next                # B 走完后切到 A,抵消长度差
        while headB:                          # A 走完后切到 B,抵消长度差
            if headB in seen:                 # A 走完后切到 B,抵消长度差
                return headB                  # A 走完后切到 B,抵消长度差
            headB = headB.next                # A 走完后切到 B,抵消长度差
        return None                           # 没有可返回的节点或结果

快速复习

相交链表:双指针换头。


12. LC 169 多数元素

难度:简单
标签:数组、摩尔投票

原题简述

找出现次数大于 n/2 的元素,题目保证存在。

知识点介绍

摩尔投票:不同元素互相抵消,多数元素最后一定留下。

标准思路

count 为 0 时换候选人,相同加一,不同减一。

最优标准解

python
from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        candidate, count = None, 0
        for num in nums:
            if count == 0:
                candidate = num
            count += 1 if num == candidate else -1
        return candidate

复杂度

时间 O(n),空间 O(1)。

易错点

题目保证多数元素存在;否则最后要验证。

多解补充

多解 1:哈希计数

取舍说明: 直观但空间 O(n)。

python
from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        count = {}
        for x in nums:
            count[x] = count.get(x, 0) + 1
            if count[x] > len(nums) // 2:
                return x

快速复习

多数元素:摩尔投票。


13. LC 206 反转链表

难度:简单
标签:链表、递归

原题简述

反转单链表并返回新头。

知识点介绍

核心是改变每个节点的 next 指向,先保存 next 防止断链。

标准思路

prev 表示反转后前一个节点,cur 表示当前节点。

最优标准解

python
from typing import Optional

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev, cur = None, head                # prev 是反转后当前节点要指向的前一个节点
        while cur:
            nxt = cur.next                    # 先保存原来的下一个节点,避免断链
            cur.next = prev                   # prev 是反转后当前节点要指向的前一个节点
            prev = cur                        # prev 是反转后当前节点要指向的前一个节点
            cur = nxt                         # 先保存原来的下一个节点,避免断链
        return prev

复杂度

时间 O(n),空间 O(1)。

易错点

先保存 nxt;最后返回 prev。

多解补充

多解 1:递归

取舍说明: 同样经典,但空间 O(n)。

python
from typing import Optional

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:
            return head
        new_head = self.reverseList(head.next)
        head.next.next = head
        head.next = None
        return new_head

快速复习

反转链表四步:存 next、改 next、移 prev、移 cur。


14. LC 226 翻转二叉树

难度:简单
标签:树、DFS、BFS

原题简述

交换二叉树中每个节点的左右孩子。

知识点介绍

每个节点做相同操作,递归天然适合。

标准思路

当前节点交换左右孩子,然后递归处理左右子树。

最优标准解

python
from typing import Optional

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root:
            return None                       # 没有可返回的节点或结果
        root.left, root.right = root.right, root.left  # 交换当前节点左右孩子
        self.invertTree(root.left)
        self.invertTree(root.right)
        return root                           # 返回处理后的根节点

复杂度

时间 O(n),空间 O(h)。

易错点

不是只换根节点,要换所有节点。

多解补充

多解 1:BFS

取舍说明: 同样优秀,逐层交换。

python
from collections import deque
from typing import Optional

class Solution:
    def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root:
            return None                       # 没有可返回的节点或结果
        q = deque([root])                     # 队列用于按层遍历二叉树
        while q:
            node = q.popleft()
            node.left, node.right = node.right, node.left
            if node.left:
                q.append(node.left)           # 加入当前路径、节点或中间结果
            if node.right:
                q.append(node.right)          # 加入当前路径、节点或中间结果
        return root                           # 返回处理后的根节点

快速复习

翻转树:每个节点交换左右。


15. LC 234 回文链表

难度:简单
标签:链表、双指针

原题简述

判断链表是否正读反读相同。

知识点介绍

快慢指针找中点,反转后半段后即可从两端比较。

标准思路

slow 到中点,反转 slow 开始的后半段,前半段和反转后半段逐个比较。

最优标准解

python
from typing import Optional

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        slow = fast = head                    # slow 找中点或入环点,fast 用来制造速度差
        while fast and fast.next:             # fast 还能走两步时,继续推进快慢指针
            slow = slow.next                  # slow 每次走一步
            fast = fast.next.next             # fast 每次走两步
        prev, cur = None, slow                # prev 是反转后当前节点要指向的前一个节点
        while cur:
            nxt = cur.next                    # 先保存原来的下一个节点,避免断链
            cur.next = prev                   # prev 是反转后当前节点要指向的前一个节点
            prev = cur                        # prev 是反转后当前节点要指向的前一个节点
            cur = nxt                         # 先保存原来的下一个节点,避免断链
        p1, p2 = head, prev                   # prev 是反转后当前节点要指向的前一个节点
        while p2:
            if p1.val != p2.val:              # 前半段和反转后半段对应值不同,不是回文
                return False                  # 当前情况不符合题意
            p1, p2 = p1.next, p2.next
        return True                           # 当前检查通过

复杂度

时间 O(n),空间 O(1)。

易错点

比较时以后半段为准;奇数长度也适用。

多解补充

多解 1:数组法

取舍说明: 最简单但空间 O(n)。

python
from typing import Optional

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        vals = []
        while head:
            vals.append(head.val)             # 加入当前路径、节点或中间结果
            head = head.next
        return vals == vals[::-1]

快速复习

回文链表:找中点 + 反转后半段。


16. LC 283 移动零

难度:简单
标签:数组、双指针

原题简述

把所有 0 移到末尾,保持非零元素相对顺序,原地修改。

知识点介绍

slow 指向下一个非零元素应该写入的位置。

标准思路

遍历数组,遇到非零就放到 slow 位置。

最优标准解

python
from typing import List

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        slow = 0                              # slow 指向下一个非零元素应该放的位置
        for i in range(len(nums)):
            if nums[i] != 0:                  # 非零元素需要保留到数组前面
                nums[slow], nums[i] = nums[i], nums[slow]  # slow 指向下一个非零元素应该放的位置
                slow += 1                     # slow 指向下一个非零元素应该放的位置

复杂度

时间 O(n),空间 O(1)。

易错点

函数不返回数组;要保持相对顺序。

多解补充

多解 1:覆盖后补零

取舍说明: 同样优秀,逻辑更直观。

python
from typing import List

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        slow = 0                              # slow 指向下一个非零元素应该放的位置
        for x in nums:
            if x != 0:
                nums[slow] = x                # slow 指向下一个非零元素应该放的位置
                slow += 1                     # slow 指向下一个非零元素应该放的位置
        for i in range(slow, len(nums)):
            nums[i] = 0                       # 非零元素写完后,剩余位置补 0

快速复习

移动零:slow 管非零区。


17. LC 338 比特位计数

难度:简单
标签:位运算、动态规划

原题简述

返回 0 到 n 每个数二进制中 1 的个数。

知识点介绍

i>>1 去掉最低位,i&1 判断最低位是否为 1。

标准思路

ans[i]=ans[i>>1]+(i&1)。

最优标准解

python
from typing import List

class Solution:
    def countBits(self, n: int) -> List[int]:
        ans = [0] * (n + 1)
        for i in range(1, n + 1):
            ans[i] = ans[i >> 1] + (i & 1)
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(n)。

易错点

返回 n+1 个数;包含 0。

多解补充

多解 1:消掉最低位 1

取舍说明: 同样优秀。

python
from typing import List

class Solution:
    def countBits(self, n: int) -> List[int]:
        ans = [0] * (n + 1)
        for i in range(1, n + 1):
            ans[i] = ans[i & (i - 1)] + 1
        return ans                            # 返回最终答案

快速复习

比特 DP:右移 + 最低位。


18. LC 448 找到所有数组中消失的数字

难度:简单
标签:数组、原地哈希

原题简述

数组长度 n,值在 1..n,找没出现的数字。

知识点介绍

数字 x 对应下标 x-1,出现过就把对应位置标负。

标准思路

第一遍标记,第二遍找仍为正的位置。

最优标准解

python
from typing import List

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        for x in nums:
            idx = abs(x) - 1
            if nums[idx] > 0:
                nums[idx] = -nums[idx]        # 把对应位置标负,表示这个数字出现过
        ans = []
        for i, x in enumerate(nums):
            if x > 0:
                ans.append(i + 1)
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(1) 不算返回结果。

易错点

用 abs(x) 取原值;重复数字不要反复变号。

多解补充

多解 1:哈希集合

取舍说明: 直观但空间 O(n)。

python
from typing import List

class Solution:
    def findDisappearedNumbers(self, nums: List[int]) -> List[int]:
        seen = set(nums)
        return [x for x in range(1, len(nums) + 1) if x not in seen]

快速复习

原地哈希:x 标记 x-1。


19. LC 461 汉明距离

难度:简单
标签:位运算

原题简述

返回两个整数二进制不同位数量。

知识点介绍

异或后 1 的位置就是不同位。

标准思路

先 x^y,再统计 1 的个数。

最优标准解

python
class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        num = x ^ y                           # 异或后为 1 的位,就是两个数不同的位
        ans = 0
        while num:
            num &= num - 1                    # 每次消掉 num 最右边的一个 1
            ans += 1
        return ans                            # 返回最终答案

复杂度

时间 O(k),空间 O(1)。

易错点

先异或再数 1。

多解补充

多解 1:逐位右移

取舍说明: 更直观。

python
class Solution:
    def hammingDistance(self, x: int, y: int) -> int:
        num = x ^ y                           # 异或后为 1 的位,就是两个数不同的位
        ans = 0
        while num:
            ans += num & 1                    # 检查当前最低位是否为 1
            num >>= 1
        return ans                            # 返回最终答案

快速复习

汉明距离:异或后数 1。


20. LC 543 二叉树的直径

难度:简单
标签:树、DFS

原题简述

返回二叉树任意两节点最长路径的边数。

知识点介绍

经过某节点的最长路径是左深度+右深度。

标准思路

后序 DFS 返回深度,同时更新全局直径。

最优标准解

python
from typing import Optional

class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        ans = 0
        def depth(node):
            nonlocal ans
            if not node:                      # 空节点是递归边界
                return 0
            left = depth(node.left)           # 求左子树向下的最大深度
            right = depth(node.right)         # 求右子树向下的最大深度
            ans = max(ans, left + right)      # 经过当前节点的最长路径等于左右深度之和
            return max(left, right) + 1
        depth(root)
        return ans                            # 返回最终答案

复杂度

时间 O(n),空间 O(h)。

易错点

直径按边数;返回给父节点的是深度。

多解补充

多解 1:重复求深度

取舍说明: 能做但 O(n²),不推荐。

python
from typing import Optional

class Solution:
    def diameterOfBinaryTree(self, root: Optional[TreeNode]) -> int:
        def dep(node):
            return 0 if not node else max(dep(node.left), dep(node.right)) + 1  # 空节点是递归边界
        def dfs(node):
            if not node:                      # 空节点是递归边界
                return 0
            return max(dep(node.left) + dep(node.right), dfs(node.left), dfs(node.right))  # 递归处理左子树
        return dfs(root)

快速复习

直径:每个节点算 left+right。


21. LC 617 合并二叉树

难度:简单
标签:树、DFS、BFS

原题简述

合并两棵二叉树,重叠节点值相加,不重叠节点保留。

知识点介绍

双树递归重点处理一空一不空的情况。

标准思路

如果某棵树为空返回另一棵;都不空则值相加并递归合并左右子树。

最优标准解

python
from typing import Optional

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1                      # 返回合并后的第一棵树根节点
        root1.val += root2.val                # 两个节点重叠时,把值相加到 root1 上
        root1.left = self.mergeTrees(root1.left, root2.left)  # 递归合并左子树,并接回 root1.left
        root1.right = self.mergeTrees(root1.right, root2.right)  # 递归合并右子树,并接回 root1.right
        return root1                          # 返回合并后的第一棵树根节点

复杂度

时间 O(m+n) 级别,空间 O(h)。

易错点

递归返回值要接回左右孩子;原地合并会修改 root1。

多解补充

多解 1:创建新树

取舍说明: 不修改原树,但会创建新节点。

python
from typing import Optional

class Solution:
    def mergeTrees(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
        if not root1:
            return root2
        if not root2:
            return root1                      # 返回合并后的第一棵树根节点
        node = TreeNode(root1.val + root2.val)
        node.left = self.mergeTrees(root1.left, root2.left)
        node.right = self.mergeTrees(root1.right, root2.right)
        return node                           # 返回当前构造出的节点

快速复习

合并树:空返另一棵,都有则相加。

相关文章