————技巧点:快排思想
1、颜色分类(75)
题目描述:
【中等题】
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
思路分析:
要求:对仅含有数字0,1,2的数组排序。
特点:数字重复率极高
题解一:计数排序
1、遍历数组,统计0,1,2分别出现的总个数
2、遍历数组,依次将0,1,2填充进去。
【python代码实现】
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
#统计0,1,2的次数
zero=nums.count(0)
one=nums.count(1)
two=nums.count(2)
index=0 #填充移动指针
for i in range(zero):
nums[index]=0
index+=1
for i in range(one):
nums[index]=1
index+=1
for i in range(two):
nums[index]=2
index+=1
-
时间复杂度: O ( n ) O(n) O(n),遍历两遍数组
-
空间复杂度: O ( 1 ) O(1) O(1)
此方法遍历了两遍数组,能不能就遍历一遍数组呢?当然能,并且考虑数组元素重复率较高,我们可以使用三路快排的方法。
题解二:三路快排
具体原理、思想见上一篇博文快排(单路、双路、三路)详解
1、该数组只有0,1,2三种元素,因此我们可以以1为基准元素,所有等于2的元素放在右边,等于0的元素放在左边。
2、设置分界指针zero,two
未完待续
【python代码实现】
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
'''
all in [0, zero) = 0
all in [zero, i) = 1
all in [two, len(nums) - 1] = 2
'''
zero=-1 # nums[0...zero]=0,0与1的分解指针初始化为-1,保证其刚开始是无效数组
two=len(nums) # nums[two...len(nums)-1]=2,1与2的分界指针初始化为长度,保证其刚开始是无效数组
i=0
while i<two:
if nums[i]==1:
i+=1
elif nums[i]==0:
zero+=1
nums[i],nums[zero]=nums[zero],nums[i]
i+=1
else: # nums[i]==2
two-=1
nums[i],nums[two]=nums[two],nums[i]
2、合并两个有序数组(88)
题目描述:
【简单题】
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。
说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。
思路分析:
要求:两个有序数组合并到其中一个数组内。
题解一:合并后排序
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
nums1[:]=sorted(nums1[:m]+nums2)#注意:nums1取前m个元素
- 时间复杂度: O ( ( m + n ) l o g ( m + n ) ) O((m+n)log(m+n)) O((m+n)log(m+n))
- 空间复杂度: O ( 1 ) O(1) O(1)
缺点:时间复杂度大,没有利用两个数组本身就有序这一点
题解二:双指针—从前往后
\quad \quad
其实,有序
,合并
两次可以想到用归并排序的归并操作来解答此题。
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
p1=0
p2=0
nums1_copy=nums1[:m]#额外的空间将nums1中的元素腾出来
nums1[:]=[]
while p1<m and p2<n:#两数组都不为空时
if nums1_copy[p1]<nums2[p2]:#谁小先放谁
nums1.append(nums1_copy[p1])
p1+=1
else:
nums1.append(nums2[p2])
p2 += 1
if p1<m:#nums2数组已处理完
nums1[p1+p2:]=nums1_copy[p1:]
if p2<n:#nums1数组已处理完
nums1[p1+p2:]=nums2[p2:]
- 时间复杂度: O ( n + m ) O(n+m) O(n+m)
- 空间复杂度: O ( m ) O(m) O(m),由于nums1是用于输出的数组,需要将nums1中的前m个元素放在其他地方,另两个指针
题解三:双指针—从后往前
\quad \quad
为了解决省略上一题解所需的额外空间,可以考虑从后往前走,因为nums1
的空间都在后面,所以从后向前处理排序的数据会更好,节省空间,一边遍历一边将值填充进去。
-
设置指针
p1
和p2
分别指向nums1
和nums2
的有数字尾部即p1=m-1
、p2=n-1
-
设置指针
k
指向nums1的最末尾,(每次遍历比较两数组值大小后,则进行填充)即k=m+n-1
-
当p1>0且p2>0:
比较两数组相对应位置大小,大的填充在nums1[k]处且相应的指针-1,k-1 -
当
p1<0
:此时nums2中数据未拷贝完,
将nums2中数据直接拷贝到nums1的前面 -
否则即
p2<0
:此时nums1中数据未拷贝完,但是因为其在nums1中,故什么都不用做
class Solution:
def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
"""
Do not return anything, modify nums1 in-place instead.
"""
p1=m-1 #p1指向nums1有数字末尾处
p2=n-1 #p1指向nums2末尾处
k=m+n-1 #k指向nums1末尾处
while p1>=0 and p2>=0:
if nums1[p1]>nums2[p2]:
nums1[k]=nums1[p1]
p1-=1
k-=1
else:
nums1[k]=nums2[p2]
p2-=1
k-=1
if p1<0: #nums2中还有书数据没有拷贝完
nums1[:p2+1]=nums2[:p2+1]
3、数组中的第k个最大元素(215)
题目描述:
【中等题】
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
题目链接
思路分析:
要求:求排序后的第k大元素
题解一:暴力法
python函数排序,再求k索引
1、基于python排序函数对数组升序
2、返回**nums[-k]**即为排序后的第k大元素
class Solution:
def findKthLargest(self, nums: List[int], k: int) -> int:
nums.sort()
return nums[-k]
- 时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
- 空间复杂度:O(1),这里是原地排序,没有借助额外的辅助空间。
\quad \quad python里的sort排序是一种名为Timsort的排序方法,其时间复杂度为 O ( n l o g n ) O(nlogn) O(nlogn),而且这是一种快速的稳定的排序方法。它的发明者是Tim Peters在2001年为Python创造的一种排序算法。
题解二: