• 03 排序算法


    直接移动 ( 交换数据位置 )
    逻辑移动 ( 改变指针,更快 )

    • 冒泡排序法 / 交换排序法
      从第一个元素开始,比较两个相邻元素的大小,若大小顺序有误,则对调后再进行下一次元素比较,确定最后一个元素位于正确的顺序。接着再进行第二次扫描,直到完成所有元素的排序。

        for i in range(len(data)-1, 0, -1):
            for j in range(i):
                if data[j] > data[j+1]:
                    data[j], data[j+1] = data[j+1], data[j]
      
    • 选择排序法( Selection Sort )
      算是枚举法的应用
      反复从未排序的数列中取出最小的元素,加入到另一个数列中。

        for i in range(len(data)-1):
            for j in range(i+1, len(data)):
                if data[i] > data[j]:
                    data[i], data[j] = data[j], data[i]
      
    • 插入排序法( Insert Sort )
      将数组中的元素逐一与 已排好序 的数据进行比较,前两个元素先排好,再将第三个元素插入合适的位置,重复直到完成。

        for i in range(1, len(data)):
            cur = data[i]
            pre = i - 1
            while pre >= 0 and cur < data[pre]:
                data[pre + 1] = data[pre]  # 把数后置一位,给小数腾地儿
                pre -= 1
            # pre = -1,说明该值是目前最小的,把他放在第一个,即 pre = 0
            data[pre + 1] = cur
      
    • 希尔排序法( Shell Sort )
      可以减少插入排序搬移数据的次数。
      将数据区分成 特定间隔 的几个小区快,以插入排序法排完区块内的数据后再渐渐减少隔离的距离。

      • 划分数,不一定是 2,质数最好。间隔为 8/2
        8,7,6,5,4,3,2,1
        (8, 4),(7, 3),(6, 2),(5, 1) # 并不是真的把两个数搬到一起,只是在形式上
        插入排序后
        (4, 8),(3, 7),(2, 6),(1, 5)
        再缩小间隔 (8/2)/2
        (4, 3, 2, 1),(8, 7, 6, 5)
        插入排序后
        (1, 2, 3, 4),(5, 6, 7, 8)
        jmp = len(data) // 2
        while jmp != 0:
            # 插入排序
            for i in range(jmp, len(data)):
                tmp = data[i]
                j = i - jmp
                while j >= 0 and tmp < data[j]:
                    data[j + jmp] = data[j]
                    j -= jmp
                data[j + jmp] = tmp
            jmp = jmp // 2
      
    • 合并排序法( Merge Sort )
      针对以排好序的两个或两个以上的数列(或数据文件),通过合并的方式,将其组合成一个大的且已排好序的数列(或数据文件)。
      2 路( 2-way )合并排序:
      ① 将 N 个长度为 1 的键值,成对的合并成 N/2 个长度为 2 的有序键值组
      ② 将 N/2 个长度为 2 的键值组,成对的合并成 N/4 个长度为 4 的有序键值组
      ③ 不断合并,直到键值组长度为 N,且已排好序

        l3 = []
        idx1 = idx2 = 0
        for i in range(len(l1) + len(l2)-2):
            if l1[idx1] < l2[idx2]:
                l3.append(l1[idx1])
                idx1 += 1
                if idx1 == len(l1):
                    l3.extend(l2[idx2:])
                    break
            else:
                l3.append(l2[idx2])
                idx2 += 1
                if idx2 == len(l2):
                    l3.extend(l1[idx1:])
                    break
      
    • 快速排序法( Quick Sort ) / 分割交换排序法
      目前公认最佳。使用了分而治之( Divide and Conquer )。
      先在数据中找到一个虚拟的中间值,并按此中间值将所有打算排序的数据分为两部分。小于中间值的再左边,大于的再右边。再以同样的方式分别处理左、右两边数据,直到排完序为止。
      假设有 n 项记录 R1,R2,R3,...,Rn,其键值为 k1, k2,k3,...,kn:
      ① 先假设 K 的值为第一个键值
      ② 从左向右找出键值 Ki,使得 Ki > K
      ③ 从右向左找出键值 Kj,是的 Kj < K
      ④ 如果 i < j,那么 Ki 与 Kj 互换,并回到步骤 ②
      ⑤ 若 i ≥ j,则将 K 与 Kj 互换,并以 j 为基准点分割成左右两部分。然后针对左右两部分进行步骤 ① 至 ⑤,直到左半边键值等于右半边键值

        def quick(d, size, lf, rg):
            # 第一项键值为 d[lf]
            # size = len(d)
            if lf < rg:  # 左右两边索引值
                # ②
                lf_idx = lf + 1
                while d[lf_idx] < d[lf]:
                    if lf_idx + 1 == size:
                        break
                    lf_idx += 1
                # ③
                rg_idx = rg
                while d[rg_idx] > d[lf]:
                    rg_idx -= 1
                # ④
                while lf_idx < rg_idx:
                    d[lf_idx], d[rg_idx] = d[rg_idx], d[lf_idx]
                    lf_idx += 1
                    while d[lf_idx] < d[lf]:
                        lf_idx += 1
                    rg_idx -= 1
                    while d[rg_idx] > d[lf]:
                        rg_idx -= 1
                # ⑤
                d[lf], d[rg_idx] = d[rg_idx], d[lf]
                quick(d, size, lf, rg_idx-1)  # 以rg_idx为基准点分成左右两半,以递归方式分别为左右两边进行排序直至完成排序
                quick(d, size, rg_idx+1, rg)
      
    • 基数排序法
      并不需要元素间进行比较,属于一种分配模式排序方式。
      按比较的方向可分为最高位优先 ( Most Significant Digit First , MSD ) 和最低位优先 ( Least Significant Digit First , LSD ) 两种。
      MSD 是从最左边的位数开始比较,LSD 是从最右边的位数开始比较。

        def radix(data):
            """0~999 整数比较"""
            n = 1  # n 为基数,从个位开始排序
            while n <= 100:
                tmp = [[None] * 100 for row in range(10)]  # 设置暂存数组 [[n位为0的所有元素], [?1], [?2], ... , [?9]]。
                for i in range(len(data)):
                    m = (data[i] // n) % 10  # n 位的数值,n=1为个位,n=10为10位
                    tmp[m][i] = data[i]  # 放入棋盘对应位置
                k = 0
                for i in range(10):
                    for j in range(len(data)):
                        if tmp[i][j] is not None:
                            data[k] = tmp[i][j]  # 读出棋盘里的值,重构数组
                            k += 1
                n *= 10
      

      原数组 [3, 5, 32, 74, 169, 2, 111, 0]
      过程:
      比较个位 [0, 111, 32, 2, 3, 74, 5, 169]
      比较十位 [0, 2, 3, 5, 111, 32, 169, 74]
      比较百位 [0, 2, 3, 5, 32, 74, 111, 169]
      结果:
      [0, 2, 3, 5, 32, 74, 111, 169]


    冒泡、选择、插入排序三种数据搬移量最大的是 插入排序。
    合并排序法是稳定的。不会改变两个相同数字的原有顺序。

  • 相关阅读:
    Springboot项目使用Juint单元测试
    ubuntu安装python
    ubuntu安装mysql
    ubuntu系统添加开机自动启动 /home/ubuntu/myscript/startpic.sh 脚本
    ubuntu关闭mysql日志
    ubuntu 系统 pycharm做软连接
    ubuntu系统开机自动爬取数据
    ubuntu系统添加开机自动启动django
    Ubuntu系统设置不休眠
    xshell 打开pycharm 按 ctrl+shift 可取消输入一个字母出现两个字母的现象
  • 原文地址:https://www.cnblogs.com/catyuang/p/11506286.html
Copyright © 2020-2023  润新知