排序算法总结

由于“代码随想录”没有讲排序算法,这一部分很基础但如果久了不用还是会忘记很多细节,在此总结

排序算法最好情况最坏情况平均情况空间复杂度是否稳定
冒泡排序O(n)O(n²)O(n²)O(1)✅ 是
选择排序O(n²)O(n²)O(n²)O(1)❌ 否
插入排序O(n)O(n²)O(n²)O(1)✅ 是
希尔排序O(n log n)¹O(n²)O(n^(1.3)~n^1.5)²O(1)❌ 否
归并排序O(n log n)O(n log n)O(n log n)O(n)✅ 是
快速排序O(n log n)O(n²)O(n log n)O(log n)³❌ 否
堆排序O(n log n)O(n log n)O(n log n)O(1)❌ 否

冒泡排序 

对于长度为n的array,进行n轮比较(或n-1轮),每轮比较结束时,都会将最大的数冒泡到array尾部
每轮比较都将相邻两个数进行比较
第一轮会比较n-1次
第二轮会比较n-2次(因为此时最大的数已经在队尾,不考虑了)
 

def bubble_sort(arr):
    arr = arr.copy()
    n = len(arr)
    for i in range(n):
        is_swap = False
        for j in range(0, n - i - 1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
                is_swap = True
        if not is_swap:
            break
    return arr

选择排序

对于长度为n的array,每轮选择一个min值与前面的值交换,n轮(或n-1)后完成

def selection_sort(arr):
    arr = arr.copy()
    n = len(arr)
    for i in range(n):
        min_index = i
        for j in range(i+1, n):
            if arr[j] < arr[min_index]:
                min_index = j
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

插入排序

对于长度为n的array,遍历到当前值时,认为之前的值都有序了,往前遍历找到插入位置(同时移动数组)

def insert_sort(arr):
    arr = arr.copy()
    n = len(arr)
    for i in range(1, n):
        key = arr[i]
        j = i - 1
        while j >= 0 and arr[j] > key:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = key
    return arr

希尔排序

将原array看作间隔为gap的多个子array,这样可以快速移动小值到前面,大值到后面

def shell_sort(arr):
    arr = arr.copy()
    n = len(arr)
    gap = n // 2
    while gap > 0:
        for i in range(gap, n):
            key = arr[i]
            j = i - gap
            while j >= 0 and arr[j] > key:
                arr[j+gap] = arr[j]
                j -= gap
            arr[j+gap] = key
        gap = gap // 2
    return arr

归并排序

利用分治,当然这里是后序,在merge之前我需要保证左右子数列有序

def merge_sort(arr):
    def merge(left_arr, right_arr):
        res = []
        i = 0
        j = 0
        while i < len(left_arr) and j < len(right_arr):
            if left_arr[i] < right_arr[j]:
                res.append(left_arr[i])
                i += 1
            else:
                res.append(right_arr[j])
                j += 1
        res.extend(left_arr[i:])
        res.extend(right_arr[j:])
        return res
    def recursion(arr):
        if len(arr) <= 1:
            return arr
        mid = len(arr) // 2
        left_arr = recursion(arr[:mid])
        right_arr = recursion(arr[mid:])
        return merge(left_arr, right_arr)
    arr = arr.copy()
    return recursion(arr)

快速排序

思想是选取一个值pivot,比pivot小的放左边,比pivot大的放右边,递归处理。

快速排序的边界条件很难处理,我采取的区间是【】左闭右闭,选取第一个值为pivot

i永远指向比pivot大的值——i之前的值都<=pivot

j永远指向比pivot小的值——j之后的值都>=pivot

while中i与j的条件都必须带等号,不然i==j这种情况就跳出循环的话,以上两句话就可能不成立
(也就是j可能没正确指向比他小的值,而是指向>=pivot的值时刻,由于i==j跳出了循环)
导致pivot无法移动到正确的位置

def fast_sort(arr):
    def recursion(arr, left, right):
        if left >= right:
            return
        pivot = arr[left]
        i = left + 1
        j = right
        while i <= j:
            while i <= j and arr[i] <= pivot:
                i += 1
            while i <= j and arr[j] >= pivot:
                j -= 1
            #必须有的,没有的话会导致i > j的时候也做交换
            if i < j:
                arr[i], arr[j] = arr[j], arr[i]
        arr[left], arr[j] = arr[j], arr[left]
        recursion(arr, left, j - 1)
        recursion(arr, j + 1, right)
        return arr
    arr = arr.copy()
    return recursion(arr,0, len(arr)-1)

堆排序

堆是用数组实现的——底层数据结构是“完全二叉树”

我们以大顶堆为例,为什么可以用数组来存储这个堆?因为完全二叉树的性质决定了这些节点是“紧凑的”父子节点的坐标关系可以直接用下标计算,无需使用指针(也就不用链表啦)

当前节点i
左孩子left == 2 * i + 1
右孩子right == 2 * i + 2

孩子节点i
父节点 father = (i - 1 )//  2     or    (i + 1) //  2  - 1

就是这点良好的性质,让我们可以很方便地去建堆、维护堆

首先实现heapify堆化函数,逻辑如下:

arr代表处理数组,n为数组长度,i为当前处理节点

首先假定,当前节点,左孩子,右孩子中最大者为当前节点,然后将三者比较,得出最大者的下标,如果i不为最大者则,交换i与其值(注意,此时交换后,可能改变该结点子树的“大顶堆性质”),那我需要递归地向下去做堆化,直到堆底部。)

然后就是建堆、排序的过程


def heap_sort(arr):
    def heapify(arr, n, i):
        largest_index = i
        left = 2 * i + 1
        right = 2 * i + 2
        if left < n and arr[left] > arr[largest_index]:
            largest_index = left
        if right < n and arr[right] > arr[largest_index]:
            largest_index = right
        if largest_index != i:
            arr[largest_index], arr[i] = arr[i], arr[largest_index]
            #递归地向下做堆化
            heapify(arr, n, largest_index)
    arr = arr.copy()
    n = len(arr)
    #数组最后一个节点,也就是完全二叉树的最后一层的最右侧节点index为n-1,那么我们应该从他的父亲开始做堆化,
    #就是(i+1) // 2 - 1
    #也就是n // 2 -1
    start_index = n // 2 - 1
    for i in range(start_index, -1, -1):
        heapify(arr, n, i)
    
    #每次都把堆顶元素arr【0】,与arr【i】交换,也就是将最大元素放到了数列末端
    for i in range(n -1, -1, -1):
        arr[i], arr[0] = arr[0], arr[i]
        #此时堆顶被改变,我们需要去维护堆的性质,那就从堆顶开始heapify
        heapify(arr, i, 0)
    return arr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值