力扣hot100—系列1

1. 只出现一次的数字 (Single Number)

难度:简单

🎯 核心技巧:位运算(异或 XOR)

💡 解题思路:

题目要求线性时间复杂度且不使用额外空间。

利用异或运算(^)的性质:

  1. 归零律 : a ⊕ a = 0 a \oplus a = 0 a⊕a=0(任何数和自己异或归零)
  2. 恒等律 : a ⊕ 0 = a a \oplus 0 = a a⊕0=a(任何数和 0 异或不变)
  3. 交换律和结合律 : a ⊕ b ⊕ a = ( a ⊕ a ) ⊕ b = 0 ⊕ b = b a \oplus b \oplus a = (a \oplus a) \oplus b = 0 \oplus b = b a⊕b⊕a=(a⊕a)⊕b=0⊕b=b

解法:将数组中所有数字进行异或,成对出现的数字会互相抵消变成 0,最终剩下的结果就是那个只出现一次的数字。

💻 代码:

python 复制代码
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        res = 0
        for num in nums:
            res ^= num
        return res

2. 多数元素 (Majority Element)

难度:简单

🎯 核心技巧:摩尔投票法 (Boyer-Moore Voting Algorithm)

💡 解题思路:

题目定义多数元素是指出现次数大于 ⌊ n/2 ⌋ 的元素。

可以将这个问题看作"诸侯争霸":

  1. 维护一个 candidate(候选人)和一个 count(票数)。
  2. 遍历数组:
    • 如果 count 为 0,假设当前数字为新的 candidate,并将 count 设为 1。
    • 如果当前数字等于 candidate,票数 count + 1
    • 如果当前数字不等于 candidate,票数 count - 1(互相抵消)。
  3. 因为多数元素的数量超过一半,经过抵消后,最终剩下的 candidate 一定是多数元素。

💻 代码:

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

3. 颜色分类 (Sort Colors)

难度:中等

🎯 核心技巧:三指针 (双指针的变种,荷兰国旗问题)

💡 解题思路:

要求原地排序且仅扫描一遍。我们需要将 0 放在左边,2 放在右边,1 自动就在中间了。

定义三个指针:

  • p0:指向 0 的右边界(下一个放 0 的位置)。
  • p2:指向 2 的左边界(下一个放 2 的位置)。
  • curr:当前遍历的指针。

逻辑

  1. 如果 nums[curr] == 0:与 p0 交换,p0 右移,curr 右移。
  2. 如果 nums[curr] == 2:与 p2 交换,p2 左移。注意:此时 curr 不移动,因为交换过来的数字可能是 0 或 2,需要再次判断。
  3. 如果 nums[curr] == 1curr 直接右移(不管它)。
  4. curr > p2 时停止。

💻 代码:

python 复制代码
class Solution:
    def sortColors(self, nums: List[int]) -> None:
        p0 = 0
        curr = 0
        p2 = len(nums) - 1
        
        while curr <= p2:
            if nums[curr] == 0:
                nums[curr], nums[p0] = nums[p0], nums[curr]
                p0 += 1
                curr += 1
            elif nums[curr] == 2:
                nums[curr], nums[p2] = nums[p2], nums[curr]
                p2 -= 1
                # 这里 curr 不自增,因为从后面换过来的数还需要判断
            else:
                curr += 1

4. 下一个排列 (Next Permutation)

难度:中等

🎯 核心技巧:两遍扫描(找"小数" -> 找"大数" -> 交换 -> 翻转)

💡 解题思路:

我们要找到一个比当前排列稍大一点点的排列。

  1. 从后往前 找第一个升序对 ( i , i + 1 ) (i, i+1) (i,i+1),即满足 nums[i] < nums[i+1]。此时 nums[i] 就是需要被替换的"较小数"。
  2. 如果找不到(整个数组是降序),说明这是最大的排列,直接翻转整个数组即可。
  3. 如果在步骤 1 找到了 i,再从后往前 找第一个比 nums[i] 大的数 nums[j]("较大数")。
  4. 交换 nums[i]nums[j]
  5. 此时 i 后面的部分是降序的,将其翻转为升序,以保证变大的幅度最小。

💻 代码:

python 复制代码
class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        n = len(nums)
        i = n - 2
        
        # 1. 从后往前找第一个升序对 (i, i+1)
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
            
        if i >= 0:
            # 2. 从后往前找第一个比 nums[i] 大的数
            j = n - 1
            while j >= 0 and nums[j] <= nums[i]:
                j -= 1
            # 3. 交换
            nums[i], nums[j] = nums[j], nums[i]
        
        # 4. 翻转 i 之后的所有元素
        # (如果 i < 0,即没找到升序对,这里会翻转整个数组,符合题意)
        left, right = i + 1, n - 1
        while left < right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1

5. 寻找重复数 (Find the Duplicate Number)

难度:中等

🎯 核心技巧:快慢指针 (Floyd 判圈算法 / 链表成环)

💡 解题思路:

题目限制:不能修改数组(不能排序)、 O ( 1 ) O(1) O(1) 空间(不能用哈希表)。

这就转化为了链表找环入口的问题。

  • 映射关系 :将数组下标 i i i 视为节点, n u m s [ i ] nums[i] nums[i] 视为 next 指针指向的下一个节点索引。
  • 因为有重复数,意味着有多个下标指向同一个值,这在图论中就形成了一个环。

步骤

  1. 快慢指针相遇slow 走一步,fast 走两步,直到它们相遇。
  2. 找环入口 :保持 slow 在相遇点,将 fast(或新建一个指针)重置回起点 0
  3. 同步前进:两个指针每次都走一步,再次相遇的点就是环的入口,也就是重复的数字。

💻 代码:

python 复制代码
class Solution:
    def findDuplicate(self, nums: List[int]) -> int:
        # 第一阶段:快慢指针找相遇点
        slow = nums[0]
        fast = nums[nums[0]]
        
        while slow != fast:
            slow = nums[slow]
            fast = nums[nums[fast]]
            
        # 第二阶段:找环入口
        ptr1 = 0
        ptr2 = slow
        
        while ptr1 != ptr2:
            ptr1 = nums[ptr1]
            ptr2 = nums[ptr2]
            
        return ptr1
相关推荐
老鼠只爱大米2 小时前
LeetCode经典算法面试题 #236:二叉树的最近公共祖先(RMQ转化、Tarjan离线算法等五种实现方案详细解析)
算法·leetcode·二叉树·lca·并查集·最近公共祖先·rmq
问好眼2 小时前
【信息学奥赛一本通】1296:开餐馆
c++·算法·动态规划·信息学奥赛
yxm26336690812 小时前
【洛谷压缩技术续集题解】
java·开发语言·算法
张人玉2 小时前
VisionPro Blob、条码识别、OCR 结构化速记版
人工智能·算法·机器学习·vsionpro
愚者游世2 小时前
力扣解决二进制&题型常用知识点梳理
c++·程序人生·算法·leetcode·职场和发展·改行学it
圣保罗的大教堂2 小时前
leetcode 3640. 三段式数组 II 困难
leetcode
Geoking.2 小时前
前缀和算法:从一道 LeetCode 题看区间求和优化思想
算法·leetcode·职场和发展
爱吃rabbit的mq2 小时前
第7章 逻辑回归:二分类的基础
算法·分类·逻辑回归
DFT计算杂谈2 小时前
VASP+Wannier90 计算位移电流和二次谐波SHG
java·服务器·前端·python·算法