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.length1 <= 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.length1 <= n <= 300nums[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 <= 1000 <= 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仅对后缀原地交换;若避免辅助函数也可内联)。

浙公网安备 33010602011771号