Python数据结构(四):Python各排序算法简易理解

Python各排序算法简单理解

一、冒泡排序

基本思路

​ 最基础的排序方法,给定一个数组,从最后一个元素开始,依次与前面相邻的元素进行比较,如果该元素小于前面的元素,则交换元素位置,重复该步骤,直到到达第一个(也可以从前往后)。然后一次循环。

代码实现

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


if __name__ == '__main__':
    li = [1, 2, 4, 7, 54, 8, 5, 3, 5, 9, 5, 3]
    print(bubble_sort(li))

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
冒泡排序 O( n 2 n^2 n2) O(n) O( n 2 n^2 n2) O(1) 稳定

二、选择排序

基本思路

​ 首先,给定一个下标,从0开始,然后从该指针下一个元素开始往后找,如果该元素小于此指针的下标,则交换位置,继续下一个。

代码实现

def choose_sort(alist):
    n = len(alist)
    for i in range(n):
        index = i
        for j in range(i + 1, n):
            if alist[index] > alist[j]:
                alist[index], alist[j] = alist[j], alist[index]
    return alist


if __name__ == '__main__':
    li = [1, 2, 4, 67, 4, 2, 4, 67, 9, 5, 2, 4, 8, 9, 5, 3]
    print(choose_sort(li))

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
选择排序 O( n 2 n^2 n2) O( n 2 n^2 n2) O( n 2 n^2 n2) O(1) 不稳定

三、插入排序

基本思路

​ 类似从第一个元素开始遍历,然后从后往前进行冒泡排序

代码实现

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


if __name__ == '__main__':
    li = [12, 5, 3, 2, 5, 78, 4, 3, 2, 4, 7, 8, 5, 4, 212, 6]
    print(init_sort(li))

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
插入排序 O( n 2 n^2 n2) O(n) O( n 2 n^2 n2) O(1) 稳定

四、快速排序

基本思路

​ 选择中间值,分出左半边和右半边,然后循环该操作,直到传入的值符合结束要求。

​ 细化:对于传入的数组,有开始指针和结束指针,然后开始指针设为中间值,从结束指针开始往前移动,如果该值小于中间值,则把结束指针上的值与开始指针进行位置交换,然后由开始指针往前移动,如果值大于中间值,则与结束指针的值进行交换,重复上述操作,直到开始指针和结束指针的值相等,此时数组分为中间值,中间值左边部分,中间值右边部分。然后把这些部分再次递归调用函数,直到传入的参数的开始指针大于等于结束指针时,返回函数。

代码实现

def fast_sort(alist, first, last):
    # 判断该函数是否结束
    if first >= last:
        return
    mid_val = alist[first]
    low = first
    high = last
    while low < high:
        # 如果开始指针小于结束指针并且结束指针指向的值大于等于中间值
        while low < high and alist[high] >= mid_val:
            # 结束指针往左移动一位
            high -= 1
        # 当上层循环结束时,说明开始指针等于结束指针或者结束指针小于中间值
        # 则需要将结束指针指向的值放到开始指针指向的位置
        alist[low] = alist[high]
        # 接下来由开始指针进行判断,如果开始指针小于结束指针并且开始指针指向的值小于中间值
        while low < high and alist[low] < mid_val:
            # 开始指针往右移动一位
            low += 1
        # 当上层循环结束时,说明开始指针等于结束指针或者开始指针大于中间值
        # 则需要将开始指针指向的值放到结束指针指向的位置
        alist[high] = alist[low]
    # 如果上层循环结束,说明开始指针和结束指针指向同一个位置,则把中间值放到开始指针所指位置(也可以是结束指针)
    alist[low] = mid_val

    # 结束以上的工作,说明该数组在first到end之间,已经分出了三部分(即中间值,中间值的左半部分和中间值的右半部分),接下来即可将左半部分和右半部分放入函数进行重新递归。
    fast_sort(alist, first, low - 1)
    fast_sort(alist, low + 1, last)


if __name__ == '__main__':
    li = [1, 2, 4, 6, 4, 3, 65, 7, 4, 2, 5, 7, 8, 54, 3, 2, 76, 6, 43, 2, 8]
    fast_sort(li, 0, len(li)-1)
    print(li)

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
快速排序 O(nlogn) O(nlogn) O( n 2 n^2 n2) O(logn)~O(n) 不稳定

无、归并排序

基本思路

​ 先递归分解数组,再合并数组。

​ 细化:【1.拆分数组】传入数组,获取该数组的中间位置(向下取整或者整除2),然后重复上诉操作,直至传入函数的数组达到了长度为1。【2-1.创建数组】因为每个传入的数组都被分为两个部分(前半部分和后半部分),给定两个指针,分别指向每个数组的第一个元素,然后创建一个新的数组用来存放这两个数组合并后的数组。【2-2.合并数组】对两个指针的值进行判断,那个值小,就将哪个值放入新数组,该指针往右移动一个位置。重复上述操作直至某个指针达到该指针所在列表的长度。【2-3.压入多余数组】因为会存在一个数组已经全部压入新数组了但是另一个并没有放入,所以要判断哪个数组还有剩余,把这些元素全部压入

代码实现

def merge_sort(alist):
    # 判断传入的数组长度是否小于1,如果是返回该数组
    if len(alist) < 2:
        return alist

    mid = len(alist) // 2  # 获取中间值

    # left_li,right_li:采用归并排序后形成的新的有序的列表
    left_li = merge_sort(alist[:mid])
    right_li = merge_sort(alist[mid:])

    # 下面是将两个数组合并成一个新的数组
    left_pointer = 0
    right_pointer = 0
    result = []
    # 如果左指针小于左列表长度并且右指针小于右列表的长度
    while left_pointer < len(left_li) and right_pointer < len(right_li):
        # 如果左列表下的左指针所指向的值小于右边的,则把该值放入新列表
        if left_li[left_pointer] < right_li[right_pointer]:
            result.append(left_li[left_pointer])
            left_pointer += 1
        else:
            # 不然就把右边的值放入新列表指针加一
            result.append(right_li[right_pointer])
            right_pointer += 1
    # 当结束上层循环说明,某个指针已经达到了该列表的长度,另一个列表还有多余的值需要全部传入新数组
    if left_pointer < len(left_li):
        # 也可以使用extend方法而不能使用append。
        # extend是将传入的列表挨个值放入列表,但是append是将整个放入
        result += left_li[left_pointer:]
    if right_pointer < len(right_li):
        result += right_li[right_pointer:]
    return result


if __name__ == '__main__':
    li = [1, 3, 65, 6, 4, 23, 65, 87, 5, 3, 3, 7, 7, 4, 3, 237, 9, 9865, 3, 2, 57]
    print(merge_sort(li))

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定

六、希尔排序

基本思路

​ 给定一个间隔,然后在列表上根据间隔的个数先排列,然后循环直到该间隔全部排完。等该间隔完了以后缩小间隔继续循环

​ 细化:假设初始间隔为4,该指针从该间隔开始到数组末尾进行判断,如果该指针所指向的值小于该指针的大小减去间隔*n的值,则将两个位置进行交换。重复上诉操做,然后间隔减一变成3,继续上面的操作,直到间隔为0完成排序

代码实现

# -*- coding: utf-8 -*-


def shell_sort(alist):
    n = len(alist)
    gap = 4
    while gap > 0:
        for i in range(gap, n):
            tmp = alist[i]
            j = i
            while j >= gap and alist[j - gap] > tmp:
                alist[j] = alist[j - gap]
                j -= gap
            alist[j] = tmp
        gap -= 1


# 新的思路,即不指定gap的大小,而是由n决定,根据1,4,13来一次一次的改变,
def Knuth_shell_sort(alist):
    # h=1 h=3*h+1 ...
    n = len(alist)
    h = 1
    while h < n/3:
        h = 3*h+1
    gap = h
    while gap > 0:
        for i in range(gap, n):
            temp = alist[i]
            j = i
            while j >= gap and alist[j - gap] > temp:
                alist[j] = alist[j - gap]
                j -= gap
            alist[j] = temp
        gap = int((gap-1)/3)


if __name__ == '__main__':
    li = [1, 3, 6, 5, 4, 43, 7, 9, 7, 54, 3, 6, 9, 7, 5, 3, 6, 9, 65, 3, 43, 7, 4, 3, 5, 87, 54, 4, 7, 96]
    shell_sort(li)
    print("普通希尔排序:")
    print(li)
    li = [1, 3, 6, 5, 4, 43, 7, 9, 7, 54, 3, 6, 9, 7, 5, 3, 6, 9, 65, 3, 43, 7, 4, 3, 5, 87, 54, 4, 7, 96]
    Knuth_shell_sort(li)
    print("Knuth希尔排序:")
    print(li)

复杂度

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
归并排序 O(nlogn)~O( n 2 n^2 n2) O( n 1.3 n^{1.3} n1.3) O( n 2 n^2 n2) O(1) 不稳定

七、总结

排序名称 平均时间复杂度 最好情况下 最坏情况下 空间复杂度 是否稳定
冒泡排序 O( n 2 n^2 n2) O(n) O( n 2 n^2 n2) O(1) 稳定
选择排序 O( n 2 n^2 n2) O( n 2 n^2 n2) O( n 2 n^2 n2) O(1) 不稳定
插入排序 O( n 2 n^2 n2) O(n) O( n 2 n^2 n2) O(1) 稳定
快速排序 O(nlogn) O(nlogn) O( n 2 n^2 n2) O(logn)~O(n) 不稳定
归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定
归并排序 O(nlogn)~O( n 2 n^2 n2) O( n 1.3 n^{1.3} n1.3) O( n 2 n^2 n2) O(1) 不稳定
上一篇:python基础练习


下一篇:python记录汇总