python-小学生都可以看懂的排序算法

冒泡排序-时间复杂度O(n**2)

def bubbLe_sort(L):
    Le = len(L)
    if Le <= 1:
        return
    for i in range(Le - 1):
        for j in range(Le - i - 1):
            if L[j] > L[j + 1]:
                L[j], L[j + 1] = L[j + 1], L[j]


if __name__ == '__main__':
    L = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L)
    bubbLe_sort(L)
    print('After: ', L)

冒泡排序优化-时间复杂度最优为O(n),最坏还是O(n**2)

def bubbLe_sort(L):
    n = len(L)
    if n <= 1:
        return
    for i in range(n-1):
        flag = False    # 如果我们在某一次冒泡操作中,发现没有交换任何元素位置,则说明此时列表已经是排序好了,就可以结束之后的循环
        for j in range(n-i-1):
            if L[j] > L[j+1]:
                L[j], L[j+1] = L[j+1], L[j]
                flag = True
        if not flag:  
            return


if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    bubbLe_sort(L1)
    print('After: ', L1)

选择排序-时间复杂度O(n**2)

def selection_sort(L):
    n = len(L)
    if n <= 1:
        return
    for i in range(n - 1):
        min_index = i
        for j in range(i + 1, n):
            if L[min_index] > L[j]:
                min_index = j
        if min_index != i:
            L[min_index], L[i] = L[i], L[min_index]

if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    selection_sort(L1)
    print('After: ', L1)

插入排序-时间复杂度O(n**2)

def insertion_sort(L):
    n = len(L)
    if n <= 1:
        return
    for i in range(1, n): 
        j = i
        while j >= 1 and L[j] < L[j-1]:  
            L[j], L[j-1] = L[j-1], L[j]
            j -= 1

if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    insertion_sort(L1)
    print('After: ', L1)

归并排序-时间复杂度O(nlogn)

def merge_sort(L):
    '''使用 '递归(recursive)' 的方式,实现归并排序算法
    自顶向下(Top Down)1. 递归 '拆分(Splitting)' 成左右两个子序列,首先递归 left = merge_sort(L[:mid])
    不断递归左边,当某一层递归中,左子序列只有一个元素时,向上返回上一层调用,即 left 获取到返回值;
    然后,在上一层调用中,递归 right = merge_sort(L[mid:])
    当右子序列只有一个元素时,向上返回,right 获取到返回值
    2. 当 left 和 right 都被赋值为单个元素的子序列后,再执行 '归并(Merging)' 操作,返回排好序的序列给上一层函数
    即上一层函数的 left 或 right 被赋值为递归函数的返回值
    '''
    n = len(L)
    # 两个作用:
    # 1. 客户端传入原始序列只有一个元素或为空时,不用排序,直接返回
    # 2. 递归的退出条件。如果传入的序列元素个数为1时,不再拆分,返回
    if n <= 1:
        return L

    # 将传入的序列拆分成左右两个子序列,再分别递归
    mid = n // 2
    left = merge_sort(L[:mid])  
    right = merge_sort(L[mid:])


    # 开始执行归并操作,将结果返回给上一层的 merge_sort() 调用栈
    return merge(left, right)


def merge(left, right):
    '''归并操作,使用可移动游标'''
    left_index = 0  # left序列的可移动的下标
    right_index = 0  # right序列的可移动的下标
    merged = []  # 用来存放最终排好序的元素

    while left_index < len(left) and right_index < len(right):  
        if left[left_index] < right[right_index]:
            merged.append(left[left_index])
            left_index += 1  # left序列的下标向右移动一位
        else:
            merged.append(right[right_index])
            right_index += 1  # right序列的下标向右移动一位

    merged = merged + left[left_index:]  
    merged = merged + right[right_index:]  
    return merged


if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    merged = merge_sort(L1)
    print('After: ', merged)

快速排序-时间复杂度O(nlogn),最差O(n**2)

def quick_sort(L, first, last):
    '''使用 '递归(recursive)' 的方式,实现快速排序算法
    first 和 last 都表示序列的下标
    '''
    if first < last:  # 左、右子序列只剩一个元素时,退出递归 (first == last)
        pivot_index = partition(L, first, last)  # 返回 '基准' 值的最终正确位置的下标
        quick_sort(L, first, pivot_index-1)
        quick_sort(L, pivot_index+1, last)


def partition(L, first, last):
    '''一次快速排序的分区过程,将选定的 '基准(pivot)' 放到正确的位置上,小于它的值都在它的左边,大于它的值都在它的右边
    first 和 last 都表示序列的下标
    '''
    pivot_value = L[first]  # 可以选择序列的第1个元素、中间元素或最后1个元素为 '基准' 值,这里选择第1个

    leftmark = first + 1  # leftmark 游标从左向右查找比 '基准' 值大的元素
    rightmark = last  # rightmark 游标从右向左查找比 '基准' 值小的元素

    done = False
    while not done:
        # 当 leftmark 找到比 '基准' 值大的元素后,停下来
        while leftmark <= rightmark and L[leftmark] <= pivot_value:
            leftmark = leftmark + 1
        # 当 rightmark 找到比 '基准' 值小的元素后,停下来
        while L[rightmark] >= pivot_value and rightmark >= leftmark:
            rightmark = rightmark - 1

        if rightmark < leftmark:  # 如果 leftmark 和 rightmark 交错而过了,说明 '基准' 值的正确位置已经找到了:1. 交错而过说明除pivot之外的所有元素都被遍历了 2. rightmark 此时找到的就是最后一个「小于或等于pivot的区间」的元素,那么只需要交换 L[rightmark] 和 pivot 即可
            done = True
        else:  # 如果 leftmark 和 rightmark 还没有交错,则交换它们查找到的元素值
            L[leftmark], L[rightmark] = L[rightmark], L[leftmark]

    # 如果 leftmark 和 rightmark 交错而过了,说明 '基准' 值的正确位置已经找到了,设置 done = True
    # 将 '基准' 值放到正确的位置上,即 rightmark 最后一次所在的位置,交换它们的值即可
    L[first], L[rightmark] = L[rightmark], L[first]

    return rightmark  # 返回 '基准' 值的最终正确的下标,用于划分左右两个子序列


if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    quick_sort(L1, 0, len(L1)-1)
    print('After: ', L1)

希尔排序-最优O(nlogn),最差O(ns)1<s<2

def shell_sort(L):
    '''希尔排序,升序
    步长序列使用 Sedgewick 提出的[1, 5, 19, 41, 109, 209, 505, 929 ...],此时时间复杂度为 O(n (logn)^2)
    '''
    n = len(L)
    if n <= 1:
        return

    gen = sedgewick_gaps(n)  # [1, 5, 19, 41, 109, 209, 505, 929 ...]
    for gap in reversed(list(gen)):  # 将gen生成器对象转换成列表,再倒序: [41, 19, 5, 1]

        # 想像成,以步长 gap 将原始序列划分成 gap 个待排序的序列,对每个序列使用普通的插入排序进行排序
        # 序列1: [L[0], L[gap], L[2*gap]...]
        # 序列2: [L[1], L[1+gap], L[1 + 2*gap]...]
        # 序列3: [L[2], L[2+gap], L[2 + 2*gap]...]
        # 请查看插入排序算法 https://github.com/wangy8961/python3-algorithms/blob/master/4.%20%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%20-%20Insertion%20Sort/3_best_insertion_sort_asc.py
        # 助记:普通的插入排序算法中步长是 1 ,把插入排序中的步长 1 替换为 gap
        for i in range(gap, n):  # "未排序序列" 的第1个元素分别是L[gap], L[1+gap], L[2+gap] ... ,所以变量 i 表示的下标是 gap, 1+gap, 2+gap ...
            temp = L[i]
            j = i
            # j >= gap是因为后续j[j-gap],否则下标越界
            while j >= gap and temp < L[j-gap]:
                L[j] = L[j-gap]
                j -= gap
            L[j] = temp


if __name__ == '__main__':
    L1 = [54, 26, 93, 17, 77, 31, 44, 55, 20]
    print('Before: ', L1)
    shell_sort(L1)
    print('After: ', L1)
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值