美文网首页
【LeetCode】排序算法专题

【LeetCode】排序算法专题

作者: 不可能打工 | 来源:发表于2021-06-17 19:08 被阅读0次

    1. 三种低级排序

    冒泡排序

    每一趟选出一个最大值,排在最后一个
    时间复杂度:o(n2)

    def bubble_sort(alist):
        n = len(alist)
        for i in range(n-1,0,-1):
            count = 0
            for j in range(i):
                if alist[j] > alist[j+1]:
                    alist[j],alist[j+1] = alist[j+1],alist[j]
                    count += 1
            if 0 == count:
                break
        return alist
    

    选择排序

    每一趟选出一个最小值,放到前面
    时间复杂度:o(n2)

    def select_sort(alist):
        n = len(alist)
        for i in range(n-1):
            min_index = i
            for j in range(i+1,n):
                if alist[j] < alist[min_index]:
                    min_index = j
            alist[i],alist[min_index] = alist[min_index], alist[i]
        return alist
    

    插入排序

    不断地从后面选一个数,然后插入到前面已经有序的序列里
    时间复杂度:o(n2)

    # 插入排序
    def insert_sort(alist):
       n = len(alist)
       for i in range(1,n):
           while (i>0):
               if alist[i] < alist[i-1]:
                   alist[i], alist[i-1] = alist[i-1], alist[i]
                   i -= 1
               else:
                   break
       return alist
    

    希尔排序

    是一种分组插入排序算法
    时间复杂度:o(nlogn) ~ o(n2)

    # 希尔排序
    def shell_sort(alist):
        n = len(alist)
        gap = n // 2
        while gap > 0:
            for i in range(gap,n):
                j = i
                while j > 0:
                    if alist[j] > alist[j-gap]:
                        alist[j], alist[j-gap] = alist[j-gap], alist[j]
                        j -= gap
                    else:
                        break
            gap //= 2
        return alist
    

    2. 三种高级排序

    快速排序

    指定第一个数为mid_value,排序使得mid_value左边的数比mid_value小,右边的数比mid_value大,然后分别对左边和右边进行递归排序。

    partition模板

    partition,它是 “分而治之” 思想当中 “分” 的那一步。经过 partition 操作以后,每一次都能排定一个元素,并且这个元素左边的数都不大于它,这个元素右边的数都不小于它,并且我们还能知道排定以后的元素的索引。

    def partition(nums, left, right):
        pivot = nums[left]#初始化一个待比较数据
        i,j = left, right
        while(i < j):
            while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数
                j-=1
            nums[i] = nums[j] #将更小的数放入左边
            while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数
                i+=1
            nums[j] = nums[i] #将更大的数放入右边
        #循环结束,i与j相等
        nums[i] = pivot #待比较数据放入最终位置 
        return i #返回待比较数据最终位置
    

    快排示例

    # 时间复杂度:o(nlogn)
    #快速排序
    def quicksort(nums, left, right):
        if left < right:
            index = partition(nums, left, right)
            quicksort(nums, left, index-1)
            quicksort(nums, index+1, right)
    
    arr = [1,3,2,2,0]
    quicksort(arr, 0, len(arr)-1)
    print(arr) 
    

    归并排序

    拆分到单个元素,然后两个两个往上进行递归合并。设置left 和right两个游标,进行合并。
    时间复杂度:o(nlogn)

    # 归并排序
    def merge_sort(alist):
        n = len(alist)
        if n <= 1:
            return alist
        mid = n//2
        left = merge_sort(alist[:mid])
        right = merge_sort(alist[mid:])
    
        left_point,right_point = 0,0
        result = []
        while left_point < len(left) and right_point < len(right):
            if left[left_point] <= right[right_point]:
                result.append(left[left_point])
                left_point += 1
            else:
                result.append(right[right_point])
                right_point += 1
    
        result += left[right_point:]
        result += right[left_point:]
        return result
    

    最变态的堆排序来了😈

    堆排序

    构造堆:从小堆到大堆,先看最后一个非叶子节点,从下往上
    时间复杂度 : o(nlogn)

    # 堆排序
    
    # 向下调整函数的实现, 此处建立大根堆,可实现数组升序排列
    def sift(alist, low, high):
        # 假设只有根节点需要调整,两棵子树都是堆
        i = low
        j = i *2 +1 #j指向i的左子树
        tmp = alist[i]
        while j <=high:
            if j+1<= high and alist[j] < alist[j+1] #右子树比较大,则指向右子树
                j = j+1
            if alist[j] > tmp:  # 若子树的值比较大,则根节点换成子树,然后向下看一层
                alist[i] = alist[j]
                i = j
                j = i *2 +1
            else:
                alist[i] = tmp # 子树的值小于根节点,则根节点就放在这一层
                break
        else:
            alist[i] = tmp # j越界跳出循环,则把根节点放在叶子节点
    
    
    def heap_sort(alist):
        # 1、建堆
        # 先找到最后一个不是叶子节点的根节点,为(n-2)//2 (若叶子节点为i,则他的父节点为(i-1)//2 )
        # 再向上循环根节点,从小到大
        n = len(alist)
        for i in range((n-2)//2, -1, -1):
            sift(alist,i,n-1)
    
        # 2、挨个出数,按升序排列
        for i in range(n-1, -1, -1):
            alist[0], alist[i] = alist[i], alist[0]
            sift(alist, 0, i-1)
    
    if __name__ == '__main__':
        li = [54, 26, 93, 17, 77, 31, 44, 55, 20, 13]
        heap_sort(li)
        print(li)
    

    3.topk问题

    leetcode 215.数组中的第K个最大元素
    在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

    堆排序的思想

    可以先建立k个元素的小根堆,则堆顶就是最小值。若k之后的元素大于堆顶,则应该插入该堆,从k开始,一直循环遍历完整个数组,则堆顶就是第k大的数。

    # 堆排序的思想
    
    #可借助python中的heapq模块实现堆的功能, 注意建立的是小根堆
    class Solution1:
        def findKthLargest(self, nums: List[int], k: int) -> int:
            import heapq as hq
            heap = []
            for i in nums:
                hq.heappush(heap, i)
    
                if len(heap) > k:
                    hq.heappop(heap)
            return heap[0]
    
    # 自己实现堆
    class Solution2:
        def findKthLargest(self, nums: List[int], k: int) -> int:
            n = len(nums)
            heap = nums[:k]
            # 建立含k个元素的小根堆
            for i in range((k-2)//2, -1, -1):
                self.sift(heap, i, k-1)
    
            #若k之后的元素大于根节点,则将该元素与根节点替换,然后做一次调整
            for j in range(k,n):
                if nums[j] > heap[0]: #找前k大的数
                    heap[0] = nums[j]
                    self.sift(heap, 0 , k-1)
            # print(heap)
            return heap[0] #堆顶就是第k大的数了
    
        # 注意这里要建立小根堆
        def sift(self, alist, low, high):
            # 假设只有根节点需要调整,两棵子树都是堆
            i = low
            j = i *2 +1 #j指向i的左子树
            tmp = alist[i]
            while j <=high:
                if j+1<= high and alist[j] > alist[j+1]: #右子树比较小,则指向右子树
                    j = j+1
                if alist[j] < tmp:  # 若子树的值比较小,则根节点换成子树,然后向下看一层
                    alist[i] = alist[j]
                    i = j
                    j = i *2 +1
                else:
                    alist[i] = tmp # 子树的值大于根节点,则根节点就放在这一层
                    break
            else:
                alist[i] = tmp # j越界跳出循环,则把根节点放在叶子节点
    

    快排思想

    快排的思想是,每次把第一个值作为mid_value放到数组的中间,使得mid_value左边的数都比它小,右边的数都比它大。那么,现在可以返回mid_value的下标p,比较k(k = len(nums) - k)和p的大小,若k<p,则要找的这个数在nums[:p-1]里,若k>p, 则要找的这个数在nums[p+1:]里。

    # 快排思想
    # 时间复杂度相比堆排序慢
    
    class Solution:
        def findKthLargest(self, nums, k) -> int:
            # 第k个最大的元素,即升序排列后,index为len(nums)-k
            k = len(nums) - k
            low = 0
            high = len(nums) - 1
            while low <= high:
                p = self.patition(nums, low, high)
                if k < p:
                    high = p-1
                elif k > p:
                    low = p+1
                else:
                    return nums[p]
            return -1
    
    
        def patition(self, alist, low, high):
            mid_value = alist[low]
            while low <high:
                while low < high and alist[high] >= mid_value:
                    high -= 1
                alist[low] = alist[high]
    
                while low < high and alist[low] <= mid_value:
                    low += 1
                alist[high] = alist[low]
            alist[low] = mid_value
            return low
    
    image.png

    (图片来自知乎)

    相关文章

      网友评论

          本文标题:【LeetCode】排序算法专题

          本文链接:https://www.haomeiwen.com/subject/ifscyltx.html