• 冒泡排序、选择排序、插入排序


    在开始这三种算法的学习之前,我们要先来补给几个知识:

    时间复杂度

    时间复杂度:用来评估算法运行效率的一个式子

    时间复杂度-小结:

    快速地判断算法复杂度:

    简单情况:

    • 确定问题规模n
    • 循环减半过程 -- logn
    • k层关于n的循环 n的k次方

    复杂情况:根据算法执行过程判断

    稳定性:

      相同值的情况下,排序完两个值的相对位置不会发生变化。

    1、冒泡排序(Bubble Sort)

    算法描述

    比较相邻的两个元素,如果第一个比第二个大,就交换他们两个的位置。(每一趟都会冒出一个有序区的数。)

    动图

    代码实现

    
    
    def bubble_sort(li):

    # 第一层循环几趟
    # len - 1,因为冒泡排序最后一趟的数字已经是最大的了,无需要再走一趟。
    for i in range(len(li) - 1):

    # 第二层循环,
    # - i:在无序区中比较两个数的大小,每一趟结束之后会产生一个有序数字
    # - 1:最后一个数字无须遍历,因为在第j次(倒数第二个数字)已经与j+1(倒数第一个数字)次进行了比较
    exchange = False
    for j in range(len(li) - i - 1):
    if li[j+1] < li[j]: # 升序
    li[j+1], li[j] = li[j], li[j+1]
    exchange = True

    # 如果标志位没有变化,则说明这一趟中数字位置没有发生变化(即已经有序了),后面的趟没必要比较了
    if not exchange:
    return


    li = [1, 2, 5, 3, 4, 6, 9, 8, 7]
    bubble_sort(li)
    print(li)

    ##[1, 2, 3, 4, 5, 6, 7, 8, 9]

    小结:

    • 选择排序时间复杂度:O的二次方
    • 原地排序
    • 比较时都是相邻的数进行比较(埃个比较),具有稳定性

    2、选择排序(Selection Sort)

    算法描述

    每一趟选择一个数放在第一位,然后后面每个数都与这个数进行比较,如果比第一位数小的话就替换掉第一个数,然后后面的数再和新的最小值比较,一趟完成过后有序区产生一个有序数字,然后每一趟都循环无序区中的数字。

    动图演示

    代码演示

    def select_sort(li):
    # 第一层循环, 循环多少趟
       # 每一趟拿出无序区中的第一个元素出来与后面无序的元素进行比较, 所以趟数为无序区中去掉第一个元素的长度
    for i in range(1,len(li)):

    min_sol = i
    # 选择出第一个数,使后面每个数不断比较不断更新最小值,等走完一趟之后确定这趟的最小值,进行替换
    for j in range(i+1, len(li)):
    if li[j] < li[min_sol]:
    min_sol = j

    if min_sol != i:
    li[min_sol], li[i] = li[i], li[min_sol]

    li = [1, 2, 5, 3, 4, 6, 9, 8, 7]
    select_sort(li)
    print(li)

    小结:

    • 时间复杂度也是n方
    • 原地排序
    • 排序时发现一个数比第一个数小,直接隔离很多数换去第一个位置(跳着换),不具有稳定性。比如:[2,3,2,1,4], 1和第一个2换了,两个2的相对位置发生了变化
    • 和冒泡排序的区别,稳定性和不稳定性,冒泡排序的无序区在每一趟后面,选择排序的无序区在每一趟前面。

    3、插入排序(Insertion Sort)

    算法描述

    它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置插入。

    实现思路:

    1. 从第一个元素开始,该元素可以认为已经被排序;
    2. 取出下一个元素,在已经排序的元素序列从后向前扫描
    3. 如果该元素大于新元素,则该元素移动到下一位置。

    动图演示

    代码实现

    def insert_sort(li):
    
        for i in range(1, len(li)):
            j = i - 1   # j 为手里最大的牌
         tmp = li[i]
            while j >= 0 and li[j] > tmp:     # 只要手里最大的牌比摸回来的牌大就一致往右移动
                # j >= 0 ,说明所有数都比摸到的牌要大,直接退出循环
                li[j+1] = li[j]     # 右移
                j -= 1
    
            li[j+1] = tmp     # 确定要插入的时候,是必须插到j的前一个位置

     小结:

    • 时间复杂度:n方
    • 原地排序
    • 没有跳着(跳着无序区的数字)插入,所以具有稳定性。
  • 相关阅读:
    博客样式备份
    2018年终总结
    技术博客的太监
    LeetCode 日常填坑
    互联网之父
    TotoiseSVN的使用方法
    常用CMD命令
    量化策略
    浏览器加载js的阻塞与非阻塞
    Vue核心之数据劫持
  • 原文地址:https://www.cnblogs.com/Xuuuuuu/p/10804830.html
Copyright © 2020-2023  润新知