题目描述
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
示例 1:
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
示例 2:
输入:nums = [2,0,1]
输出:[0,1,2]
示例 3:
输入:nums = [0]
输出:[0]
示例 4:
输入:nums = [1]
输出:[1]
提示:
n == nums.length
1 <= n <= 300
nums[i] 为 0、1 或 2
- 进阶:
你可以不使用代码库中的排序函数来解决这道题吗?
你能想出一个仅使用常数空间的一趟扫描算法吗?
一、填充排序
1.基本思想
统计出每个数字出现的次数,依次往列表中填充。
例如:
2.代码
class Solution:
def sortColors(self, nums) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
"""填充排序"""
# 统计每个数字出现的次数
num_count = [0] * 3
for num in nums:
num_count[num] = num_count[num] + 1
# 记录原始列表当前的填充位置
curr_loc = 0
# 遍历每个数字
for i in range(len(num_count)):
# 记录当前数字的填充次数
curr_count = num_count[i]
# 依次填充
while curr_count > 0:
nums[curr_loc] = i
curr_count -= 1
curr_loc += 1
时间复杂度 O(N)
进行了两次扫描, 2 * O(N) ≈ O(N)
空间复杂度 O(1)
二、两次分区算法
1. 分区算法模板
while left <= right:
while left <= right and nums[left]应该在左侧:
left += 1
while left <= right and nums[right]应该在右侧:
right -= 1
if left <= right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
2.代码
class Solution2:
def sortColors(self, nums) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
"""两次分区算法"""
self.partition_array(nums, 1)
self.partition_array(nums, 2)
def partition_array(self, nums, target):
left, right = 0, len(nums) - 1
while left <= right:
while left <= right and nums[left] < target:
left += 1
while left <= right and nums[right] >= target:
right -= 1
if left <= right:
nums[left], nums[right] = nums[right], nums[left]
left += 1
right -= 1
时间复杂度 O(N)
进行了两次分区,即两次扫描, 2 * O(N) ≈ O(N)
空间复杂度 O(1)
三指针(一次遍历)
1. 代码
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
"""三指针一次遍历"""
left, cur, right = 0, 0, len(nums) - 1
while cur <= right:
# 当前指针值>1时(即当前值为2),交换当前指针与右指针值,并只将右指针向左移动一位
# 不移动当前指针,因为不知道上一步右指针交换来的值的大小,所以,当前位置的值还得判断一遍
while cur <= right and nums[cur] > 1:
nums[cur], nums[right] = nums[right], nums[cur]
right -= 1
# 当前指针值<1时(即当前值为0),交换当前指针与左指针值,并只将当前与左指针都向右移动一位
while cur <= right and nums[cur] < 1:
nums[cur], nums[left] = nums[left], nums[cur]
cur += 1
left += 1
# 当前指针值==1时(即当前值为1),将当前向右移动一位
while cur <= right and nums[cur] == 1:
cur += 1
时间复杂度 O(N)
只进行了一次扫描 O(N)
空间复杂度 O(1)