日常系列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·排序篇》的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode解题总结排序篇

leetcode 207. 课程表---拓扑排序篇一

算法系列希尔排序篇

算法系列希尔排序篇

算法系列希尔排序篇

算法系列希尔排序篇