日常系列LeetCode《7·排序篇》
Posted 常某某的好奇心
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列LeetCode《7·排序篇》相关的知识,希望对你有一定的参考价值。
数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
总结
1.回顾常用排序算法
2.各排序算法的应用(归并、快排)
小规模数据,选择使用O(n^2)的排序算法
大规模数据,选择使用O(nlogn)的排序算法
1)如何写一个通用的排序算法
2)排序元素比较
3)分治算法思想
3.自定义排序的逻辑
4.有的时候,先对数据进行排序,可以降低复杂度
lc 912 :https://leetcode.cn/problems/sort-an-array/
提示:
1 <= nums.length <= 5 * 10^4
-5 * 10^4 <= nums[i] <= 5 * 10^4
#方案一:归并
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
#o(n)
self.sort_merge(nums,0,len(nums)-1,[0]*len(nums))
return nums
#o(nlogn):分治
def sort_merge(self, nums,lo,hi,tmp) -> None:
#递归终止条件:最小子问题,无法继续分解
if lo>=hi:return
#
mid=lo+(hi-lo)//2
self.sort_merge(nums,lo,mid,tmp)
self.sort_merge(nums,mid+1,hi,tmp)
self.merge(nums,lo,mid,hi,tmp)
#归并
def merge(self, nums,lo,mid,hi,tmp) -> None:
#
for i in range(lo,hi+1):
tmp[i]=nums[i]
#
i=lo
j=mid+1
for k in range(lo,hi+1):
if i==mid+1: #key
nums[k]=tmp[j]
j+=1
elif j==hi+1: #key
nums[k]=tmp[i]
i+=1
elif tmp[i]<=tmp[j]:
nums[k]=tmp[i]
i+=1
else:
nums[k]=tmp[j]
j+=1
#方案二:二路快排:o(nlogn),o(logn)
import random
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
self.quick_sort(nums,0,len(nums)-1)
return nums
#o(nlogn):分治
def quick_sort(self,nums,lo,hi) -> None:
#
if lo>=hi:return
#
index=self.pivot(nums,lo,hi)
self.quick_sort(nums,lo,index-1)
self.quick_sort(nums,index+1,hi)
#快排:o(nlogn)
def pivot(self, nums,lo,hi) -> None:
#随机选取pivot
i = random.randint(lo, hi)
nums[i],nums[hi]=nums[hi],nums[i] #换至最后,方便分区
pivot=nums[hi]
#分区
less=great=lo
while great<=hi-1:
if nums[great]<pivot:
nums[less],nums[great]=nums[great],nums[less]
less+=1
great+=1
nums[less],nums[great]=nums[great],nums[less]
#
return less
#三路快排:o(nlogn),o(logn)
import random
class Solution:
def sortArray(self, nums: List[int]) -> List[int]:
self.quick_sort(nums,0,len(nums)-1)
return nums
#o(nlogn):分治
def quick_sort(self,nums,lo,hi) -> None:
#
if lo>=hi:return
#
less,great=self.pivot(nums,lo,hi)
self.quick_sort(nums,lo,less-1)
self.quick_sort(nums,great+1,hi)
#三路快排:o(nlogn)
def pivot(self, nums,lo,hi) -> None:
#随机选取pivot
j = random.randint(lo, hi)
nums[j],nums[hi]=nums[hi],nums[j] #换至最后,方便分区
pivot=nums[hi]
#分区:未处理区[great,hi],其他为已处理区
i=lo
less=lo
great=hi
while i<=great:
if nums[i]<pivot:
nums[less],nums[i]=nums[i],nums[less]
less+=1
i+=1#注意:nums[i]与nums[less]已经得到处理
elif nums[i]>pivot:
nums[great],nums[i]=nums[i],nums[great]
great-=1
#注意无i+=1:此时nums[i]为待处理元素,因为之前nums[great]不明情况
else:i+=1
#
return less,great
阿里面试题 - 快速查找第二大数
#冒泡:时o(n),空o(1)
class Solution:
def getsecondmax(self, nums: List[int]) -> List[int]:
#
first=-2**31
second=-2**31
#
for num in nums:
if num>first:
second=first
first=num
else:num>second:
second=num
#
return second
lc 628 :https://leetcode.cn/problems/maximum-product-of-three-numbers/
提示:
3 <= nums.length <= 10^4
-1000 <= nums[i] <= 1000
#冒泡
class Solution:
def maximumProduct(self, nums: List[int]) -> int:
#o(1)
#第一小,第二小
min1=min2=float('inf')
#第一大,第二大,第三大
max1=max2=max3=float('-inf')
#o(n)
for num in nums:
min1,min2,_=sorted([min1,min2,num])
_,max3,max2,max1=sorted([max1,max2,max3,num])
#
return max(max1*max2*max3,min1*min2*max1)
lc 88:https://leetcode.cn/problems/merge-sorted-array/
提示:
nums1.length == m + n
nums2.length == n
0 <= m, n <= 200
1 <= m + n <= 200
-10^9 <= nums1[i], nums2[j] <= 10^9
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.
"""
#o(1)
i=m-1
j=n-1
k=n+m-1
#o(n):nums1,nums2已经有序
while j>=0:
if i>=0 and nums1[i]>nums2[j]:
nums1[k]=nums1[i]
i-=1
else:
nums1[k]=nums2[j]
j-=1
k-=1
#
return nums1
【剑指 51】:https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/
限制:
0 <= 数组长度 <= 50000
#逆序对:数组中的两个元素,前面的元素大于后面的元素
#方案一:暴力(超时)
class Solution:
def reversePairs(self, nums: List[int]) -> int:
#
if nums==None or len(nums)<2:return 0
#o(n^2)
count=0
for i in range(len(nums)):
for j in range(i+1,len(nums)):
if nums[i]>nums[j]:
count+=1
#
return count
#方案二:归并(阶段排序):整序的过程是消逆序对(有序程度)的过程
#分治算法:类比栈(先进后出)与 DFS(深度优先遍历)
#https://www.bilibili.com/video/BV1Qk4y1r7u5?spm_id_from=333.337.search-card.all.click
class Solution:
def reversePairs(self, nums: List[int]) -> int:
#
if len(nums)<2:return 0
#o(n),o(nlogn)
copy=[0]*len(nums) #不改变原数组
for i in range(len(nums)):
copy[i]=nums[i]
count=self.sort_merge_count(copy,0,len(nums)-1,[0]*len(nums))
return count
def sort_merge_count(self,copy,lo,hi,tmp) -> int:
#
if lo==hi:return 0 #只有一个元素,一定有序,一定非逆序数
#
mid=lo+(hi-lo)//2 #解决整形溢出问题
n1=self.sort_merge_count(copy,lo,mid,tmp)
n2=self.sort_merge_count(copy,mid+1,hi,tmp)
#优化-已经有序
if copy[mid]<=copy[mid+1]:
return n1+n2
n3=self.merge(copy,lo,mid,hi,tmp)
#
return n1+n2+n3
#copy[left,mid],copy[mid+1,right]已经排好序
def merge(self,copy,lo,mid,hi,tmp) -> int:
#
for i in range(lo,hi+1):
tmp[i]=copy[i]
#
i=k=lo
j=mid+1
count=0
while 0<=k<=hi: #key
if i==mid+1:
copy[k]=tmp[j]
j+=1
elif j==hi+1:
copy[k]=tmp[i]
i+=1
elif tmp[i]<=tmp[j]: #=:保证稳定排序
copy[k]=tmp[i]
i+=1
else:
copy[k]=tmp[j]
j+=1
count+=mid-i+1 #key:利用部分数组的有序性
k+=1
return count
lc 315:https://leetcode.cn/problems/count-of-smaller-numbers-after-self/
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
class Solution:
def countSmaller(self, nums: List[int]) -> List[int]:
#nums->index tmp->tmpindex
n=len(nums)
self.index=[i for i in range(n)]
self.tmp=[0]*n
self.tmpindex=[0]*n #key:绑定元素和对应的索引
#o(n)
self.res=[0]*n
self.sort_merge(nums,0,n-1)
return self.res
#o(nlogn):分治
def sort_merge(self, nums,lo,hi) -> None:
#递归终止条件:最小子问题,无法继续分解
if lo==hi:return
#
mid=lo+(hi-lo)//2
self.sort_merge(nums,lo,mid)
self.sort_merge(nums,mid+1,hi)
self.merge(nums,lo,mid,hi)
#归并
def merge(self, nums,lo,mid,hi) -> None:
#
for i in range(lo,hi+1):
self.tmp[i]=nums[i]
self.tmpindex[i]=self.index[i]
#
i=lo
j=mid+1
for k in range(lo,hi+1):
if i==mid+1: #key
nums[k]=self.tmp[j]
self.index[k]=self.tmpindex[j]
j+=1
elif j==hi+1: #key
nums[k]=self.tmp[i]
self.index[k]=self.tmpindex[i]
self.res[self.tmpindex[i]]+=(j-mid-1)
i+=1
elif self.tmp[i]<=self.tmp[j]:
nums[k]=self.tmp[i]
self.index[k]=self.tmpindex[i]
self.res[self.tmpindex[i]]+=(j-mid-1) #key优化:i-mid-1:比当前元素小的后面元素的个数
i+=1
else:
nums[k]=self.tmp[j]
self.index[k]=self.tmpindex[j]
j+=1
lc 327:https://leetcode.cn/problems/count-of-range-sum/
提示:
1 <= nums.length <= 10^5
-2^31 <= nums[i] <= 2^31 - 1
-10^5 <= lower <= upper <= 10^5
题目数据保证答案是一个 32 位 的整数
#问题转化(前缀和):查找[i,j]对使得: prefixSum[j] - prefixSum[i] ∈ [lower,upper]找到所有符合条件的[i,j]对,其中i!=j
#排序不影响结果:组间是独立的,不影响的
class Solution:
def countRangeSum(self, nums: List[int], lower: int, upper: int) -> int:
#前缀和
n=len(nums)
prefixsum=[0]*(n+1)
for i in range(1,n+1):
prefixsum[i]=prefixsum[i-1]+nums[i-1]
#
self.lower=lower
self.upper=upper
self.tmp=[0]*(n+1)
return self.sort_merge(prefixsum,0,n)
#o(nlogn):分治
def sort_merge(self, nums,lo,hi) -> int:
#递归终止条件:最小子问题,无法继续分解
if lo==hi:return 0
#
mid=lo+(hi-lo)//2
n1=self.sort_merge(nums,lo,mid)
n2=self.sort_merge(nums,mid+1,hi)
#nums[left,mid],nums[mid+1,right]已排序
count=0
i=lo
l=r=mid+1
while i<=mid:
while l<=hi and (nums[l]以上是关于日常系列LeetCode《7·排序篇》的主要内容,如果未能解决你的问题,请参考以下文章