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)不稳定
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值