【干货总结】排序算法二:快速排序、归并排序、堆排序

本文介绍比较快速的三种主流排序方法,平均时间复杂度均为O(nlogn)。其中,堆排序的介绍中,默认读者已经对堆结构有了一定了解。代码同样实现原地排序,即只改变原数组,不用新数组替代。

快速排序

算法思路:快速排序也是一种分而治之的思路。首先先将数组的第一个元素视为目标元素,我们的第一个任务是将目标元素放到他在数组中“合适”的位置。“合适”的位置含义是这个位置左边的元素均比目标元素小,右边的元素均比目标元素大(但左右不一定有序)。当目标元素找到“合适”的位置后,我们以这个位置为分割点,分别将左右两个子数组重复上述操作。与归并排序类似,最小子数组为单个元素数组。

时间复杂度:

1. 最坏情况   O(n^{2})

2. 平均情况   O(nlogn)

3. 最好情况   O(nlogn)

空间复杂度:最坏 O(n),平均 O(nlogn)

稳定性:不稳定

代码复杂度:较复杂

def QuickSort(arr, l, r):
    if l < r:
        mid = Partition(arr,l,r)  #返回值为切割点坐标,但此函数已将arr进行了调整
        QuickSort(arr, l, mid-1)
        QuickSort(arr, mid+1, r)

def Partition(arr, l, r):
    temp = arr[l]
    while l < r:
        while l < r and arr[r] >= temp:
            r-=1
        arr[l] = arr[r]
        while l < r and arr[l] <= temp:
            l+=1
        arr[r] = arr[l]
    arr[l] = temp
    return l

归并排序

算法思路:一种分而治之(divide-and-conque)的思路,先将数组分为左右两个子数组,将子数组进行排序(子问题),子数组成为有序列后,再将两个有序列合并成为一个有序列。最小子数组为数组里只有单个元素时,单个元素的数组可以视作有序列。

时间复杂度:

1. 最坏情况   O(nlogn)

2. 平均情况   O(nlogn)

3. 最好情况   O(nlogn)

空间复杂度:O(n)

稳定性:稳定

代码复杂度:较复杂

def MergeSort(arr, l, r):
    if l < r:
        mid = (l+r)//2
        MergeSort(arr, l, mid)
        MergeSort(arr, mid+1, r)
        Merge(arr, l, mid, r)

def Merge(arr, l, mid, r):
    L, R = [], []
    for i in range(l, r+1):
        if i < mid+1:
            L.append(arr[i])
        else:
            R.append(arr[i])
    L.append(float("inf"))
    R.append(float("inf"))
    i = j = 0
    for k in range(l, r+1):
        if L[i] < R[j]:
            arr[k] = L[i]
            i+=1
        else:
            arr[k] = R[j]
            j+=1

堆排序

算法思路:首先先将列表化成大根堆,化成堆的过程也用到了分而治之的思想,即将每个子堆依次化成堆,再向上递归。化成堆后,可以保证堆顶的元素一定是整个堆的最大元素,因此我们只需要将堆顶元素与数组最后一个元素位置互换,再将去掉“尾巴”的剩余元素继续化成堆,重复此操作即可。

时间复杂度:

1. 最坏情况   O(nlogn)

2. 平均情况   O(nlogn)

3. 最好情况   O(nlogn)

空间复杂度:O(1)

稳定性:不稳定

代码复杂度:复杂

def HeapSort(arr):
    n = len(arr)
    for i in range((n-2)//2, -1, -1):    # n-1是最后一个叶节点位置,他的父亲位置就是
                                         #(n-1-1)//2,不管是左儿子还是右儿子,父节点都是
                                         #(j-1)//2
        sift(arr, i, n-1)                 # 令high均为n-1,因为high的作用就是不超边界
    #建堆完成
    for i in range(n-1, -1, -1):
        arr[0], arr[i] = arr[i], arr[0]   # 由于arr[0]一定是堆的最大数,因此我们每次将最大数放到最尾部
        sift(arr, 0, i-1)                 # 重新建堆,但是不再考虑最后i个元素,因为后面i个元素已经有序
    
    
def sift(arr,low,high):
    top = arr[low]
    i = low
    j = 2*i + 1
    while j <= high:
        if j+1 < high and arr[j+1] > arr[j]:
            j = j+1
        if arr[j] > top:
            arr[i] = arr[j]
            i = j
            j = 2*i + 1
        else:
            break
    arr[i] = top

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值