Leetcode 技巧 1

题号直达: 136 只出现一次的数字 | 169 多数元素 | 75 颜色分类 | 31 下一个排列

LeetCode 136. 只出现一次的数字 ↑

题目

给定一个非空整数数组 nums,除某个元素仅出现一次外,其余每个元素均出现两次。找出那个只出现了一次的元素。

要求:算法应具线性时间复杂度;且仅使用常数额外空间。

测试样例

  • 示例 1: nums = [2, 2, 1]
    输出:1
  • 示例 2: nums = [4, 1, 2, 1, 2]
    输出:4
  • 示例 3: nums = [1]
    输出:1

提示

  • 1 <= nums.length <= 3 * 10^4
  • -3 * 10^4 <= nums[i] <= 3 * 10^4
  • 除一个元素只出现一次外,其余均恰出现两次

代码(Python)

class Solution(object):
    def singleNumber(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        s = nums[0]
        for i in range(1, len(nums)):
            s = s ^ nums[i]
        return s

思路解析

异或(^):a ^ a = 0,a ^ 0 = a,且满足交换律、结合律。数组里成对出现的数在全程异或中会互相抵消,最后剩下唯一的那个数。

从 nums[0] 起依次异或即可;时间 O(n),额外空间 O(1)。

LeetCode 169. 多数元素 ↑

题目

给定大小为 n 的数组 nums,返回其中的多数元素。

多数元素:在数组中出现次数严格大于 ⌊ n/2 ⌋ 的元素。

题目保证数组非空且多数元素一定存在。

测试样例

  • 示例 1: nums = [3, 2, 3]
    输出:3
  • 示例 2: nums = [2, 2, 1, 1, 1, 2, 2]
    输出:2

提示

  • n == nums.length
  • 1 <= n <= 5 * 10^4
  • -10^9 <= nums[i] <= 10^9

进阶:尝试设计 O(n) 时间且 O(1) 空间(如摩尔投票);下面「栈」写法时间仍为 O(n),栈深度最坏 O(n),额外空间为栈规模。

代码(Python)

class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        stack = []
        for i in range(len(nums)):
            if not stack:
                stack.append(nums[i])
            elif nums[i] == stack[-1]:
                stack.append(nums[i])
            else:
                stack.pop()
        return stack[0]

思路解析

用栈做「消消乐」:依次扫数组,栈空则压入当前值;若与栈顶相同则再压入;若不同则弹栈一次(相当于扔掉一对互不相同的数)。众数出现次数超过一半,与其它数两两消去后,最后不可能被消光,栈底(本题中最后 stack[0])即为多数元素。

  • 时间复杂度:O(n)。
  • 空间复杂度:栈最坏 O(n);若需 O(1) 空间可改写为摩尔投票(只记「当前候选」与计数,不相等时计数减一而非用栈存元素)。

LeetCode 75. 颜色分类 ↑

题目

给定长度为 n 的数组 nums,其中元素只取 0、1、2,分别表示红、白、蓝。请原地对 nums 排序,使同色相邻,且顺序为红 → 白 → 蓝(即 0 在前、1 其次、2 在后)。

要求:不要用内置排序;进阶可做到单次扫描且仅常数额外空间。

测试样例

  • 示例 1: nums = [2,0,2,1,1,0]
    输出:[0,0,1,1,2,2](原地修改后的 nums)
  • 示例 2: nums = [2,0,1]
    输出:[0,1,2]

提示

  • n == nums.length
  • 1 <= n <= 300
  • nums[i] 为 0、1 或 2

代码(Python)

class Solution(object):
    def sortColors(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        p0 = 0
        p1 = 0
        for i in range(len(nums)):
            if nums[i] == 0:
                nums[i], nums[p0] = nums[p0], nums[i]
                if p1 > p0:
                    nums[i], nums[p1] = nums[p1], nums[i]
                p0 += 1
                p1 += 1

            elif nums[i] == 1:
                nums[i], nums[p1] = nums[p1], nums[i]
                p1 += 1

思路解析

双指针:p0 表示「下一段 0 应落入的位置」p1 表示「下一段 0/1 中 1 应落入的位置」([0, p0) 全 0,[p0, p1) 全 1,其后为待定的 2)。

从左向右扫 i:

  • 遇 0:先与 p0 交换,把 0 放到左端;若 p1 > p0,说明 p0 处原来已是 1,被 Swap 到 i 后需再与 p1 交换,把 1 放回 1 区。然后 p0、p1 各右移一格。
  • 遇 1:与 p1 交换后 p1 += 1。
  • 遇 2:不交换(自然留在右侧)。

一趟遍历、两个下标,满足进阶的 O(n) 时间与 O(1) 额外空间。

LeetCode 31. 下一个排列 ↑

题目

排列:整数数组的一种线性摆放方式。

下一个排列:在所有排列按字典序从小到大排好时,当前排列的下一个更大排列;若当前已是最大(整体严格递减),则重排为最小字典序(升序)。

要求:原地修改 nums;仅使用常数级额外空间。

测试样例

  • 示例 1: nums = [1, 2, 3]
    输出:[1, 3, 2]
  • 示例 2: nums = [3, 2, 1]
    输出:[1, 2, 3]
  • 示例 3: nums = [1, 1, 5]
    输出:[1, 5, 1]

提示

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100
下一个排列:字典序调整示意

代码(Python)

class Solution(object):
    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: None Do not return anything, modify nums in-place instead.
        """
        if len(nums) == 1:
            return
        s = nums[-1]
        for i in range(len(nums) - 2, -2, -1):
            if i == -1:
                break
            if nums[i] < s:
                break
            else:
                s = nums[i]

        for j in range(len(nums) - 1, -1, -1):
            if nums[j] > nums[i]:
                break

        if i >= 0:
            nums[i], nums[j] = nums[j], nums[i]

        def invert(s):
            for k in range(len(s) // 2):
                s[k], s[len(s) - 1 - k] = s[len(s) - 1 - k], s[k]
            return s

        nums[i + 1 :] = invert(nums[i + 1 :])

思路解析

经典三步:① 从右往左找第一个 nums[i] < nums[i+1] 的位置(代码里用变量 s 维护右侧「扫描过的最小后缀」实现同一判断);若不存在则 i == -1,说明整体递减,直接将整段反转成升序。
② 否则在右侧找最小的大于 nums[i] 的 nums[j],与 nums[i] 交换。
③ 将 i 之后的后缀反转,使其变为升序,得到恰好大一点的排列。

  • 时间复杂度:O(n)。
  • 空间复杂度:O(1)(invert 仅对后缀原地交换;若避免辅助函数也可内联)。
posted @ 2026-10-12 00:36  Phier  阅读(1)  评论(0)    收藏  举报