《算法导论》第8章 线性时间排序(计数排序、桶排序、基数排序,以及比较排序算法的下界)

2023-05-16

关于比较排序算法的下界:

        显而易见,对于一个含有n个数字的数组,他们的排列方式有n*(n-1)*(n-2)*(n-3)……=n!种。对于其中仅存的一种正确的排序方式,我们需要做的事情是将剩下的所有合理的状态调整成这一种正确的排序方式。也就是说我们完成的是一个从n!定位到1的过程。

        如果我们逆向地思考这一过程:从1如果能达到所有合理的n!个状态,也就说明了采用的方法可以成功地将数列排序。然而比较算法的基本原理决定了我们采用的方式是比较数的大小,而比较数的大小,我们走向两个分支:【大于或者小于等于】、【大于等于或者小于】(一般讨论的时候很少将等于单独拿出来讨论,毕竟相等的情况在排序的时候是一样的),也就是可以看成一棵二叉树,二叉树的层数,表征着我们比较出结果需要用的比较的次数,也就是需要的时间。

        对于二叉树的知识告诉我们,显然这棵二叉树是完全二叉树的时候层数是最小的,也就是最快的算法,而我们熟知的那些诸如插入排序一定有一些比较是无效的,也就是说整个二叉树显得比较高比较瘦,而矮矮胖胖的二叉树才是较快的算法,接下来我们针对一棵完全二叉树进行讨论:

        对于一棵有l个叶节点的决策树,应该有 n! <= l <= 2^h(k是二叉树的层数),那么显然:

        h >= lg(n!)        从而        h = O(nlgn)

        这也就意味着,一个比较排序,无论如何优化,他的时间复杂度最多被优化到nlgn级别,这是由它本身的决策类型以及节点数量决定的。

计数排序:

        计数排序不是比较排序中的一种,是通过记录集合中出现的次数实现的,简而言之例如现在有n个1-100之间的数(1和100也是排序检测数组最大最小值得到的) ,那就开一个有100个下标的数组,然后遍历整个数组,出现一次就在对应的位置加一,将整个数组内每一个元素出现的个数给记录下来,之后再按照顺序和次数输出就可以。思路非常简单,python源代码如下:

def jishu(arr):
    """
    计数排序算法
    :param arr: 等待排序的列表
    :return: 排序完毕的列表
    """
    # 确定数组的最大值和最小值
    min = arr[0]
    max = arr[0]
    for i in arr:
        if i > max:
            max = i
        if i < min:
            min = i
    jishu_list = []
    ans = []
    for i in range(min, max+1):      # 开辟新的存储空间存出现次数
        jishu_list.append(0)
    for i in arr:      # 统计出现次数
        jishu_list[i-min] += 1
    for i in range(0, len(jishu_list)):  #生成答案数组
        for j in range(0, jishu_list[i]):
            ans.append(i+min)
    return ans


if __name__ == '__main__':
    # a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    a = [16, 14, 10, 9, 8, 7, 4, 3, 2, 1, 16, 14, 10, 9, 8, 7, 4, 3, 2, 1]
    print(jishu(a))

        计数排序存在的问题是,如果上界和下界差很大的时候需要非常巨大但是没怎么利用的存储空间,特别是数据量不是很多的时候就更加浪费存储空间,适合范围相对固定,重复数据很多的样本。

桶排序:

        为了连贯性,稍稍变更一下书里面的顺序,先记录一下和计数排序有很多相似之处的桶排序。桶排序是为了节省一部分的存储空间,但是又不至于回到比较排序的nlgn时间的折中方案,基本思想是把最大值和最小值之间的所有空间分成n个平均大小的空间(也就是“桶”),然后遍历整个数组,扔到桶里面的同时,对桶里的所有元素进行排序,最后按照桶的顺序相应输出就可以。

        我们针对一个桶进行讨论:显然桶内会有很多数据进出,而且会有插入操作,那么用链表数据结构是比较合适的,python的话我们可以直接使用简单的列表以及它的insert方法实现。

def tong(arr, tong_num):
    """
    桶排序算法
    :param arr: 等待排序的列表
    :param tong_num: 桶子个数
    :return: 排序完毕的列表
    """
    # 确定数组的最大值和最小值
    min = arr[0]
    max = arr[0]
    size = len(arr) // tong_num  # 每个桶的大小
    for i in arr:
        if i > max:
            max = i
        if i < min:
            min = i

    tong_lists = []  # 初始化桶
    for i in range(0, size-1):
        tong_lists.append([])

    for num in arr:
        tong_id = (num - min) // size
        if not tong_lists[tong_id]:  # 如果桶是空的直接扔进去
            tong_lists[tong_id].append(num)
        else:
            for i in range(0, len(tong_lists[tong_id])):
                if tong_lists[tong_id][i] >= num:  # 新插入的数依次比较,找到第一个大于等于它的就插它前面
                    tong_lists[tong_id].insert(i, num)
                    break

    print(tong_lists)
    ans = []  # 处理成能输出的答案
    for tong_list in tong_lists:
        for num in tong_list:
            ans.append(num)
    return ans

if __name__ == '__main__':
    # a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    a = [16, 14, 10, 9, 8, 7, 4, 3, 2, 1, 16, 14, 10, 9, 8, 7, 4, 3, 2, 1]
    print(tong(a, 4))

 基数排序:​​​​​​​

        基数排序的思想在于,先假设将所有的数码用0补齐成为和最大的数一样长,然后从低位到高位依次排序,由于这个排序过程类似于分类(按照正在排序的位置的数字分类)+排序的过程,因此可以用类似桶排序的方式做10个并列的小列表来存储。然而列表是有序的,可以之前排序过的内部顺序也会随着这个操作保留下来,依次排序到最高位的数字就可以看到整个数组按照顺序排序了。 

def jishupaixu(arr):
    """
    基数排序算法:
    :param arr: 输入的无序数组
    :return: 返回的数组
    """
    sorting = 1
    max_num = max(arr)
    max_pos = len(str(max_num))
    while sorting <= max_pos:
        lists = [[] for i in range(0, 10)]
        for num in arr:
            lists[num % (10 ** sorting) // (10 ** (sorting - 1))].append(num)
        arr = []
        for num_list in lists:
            for num in num_list:
                arr.append(num)
        sorting += 1
    return arr


if __name__ == '__main__':
    # a = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
    a = [16, 14, 10, 9, 8, 7, 4, 3, 2, 1, 16, 14, 10, 9, 8, 7, 4, 3, 2, 1]
    print(jishupaixu(a))

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

《算法导论》第8章 线性时间排序(计数排序、桶排序、基数排序,以及比较排序算法的下界) 的相关文章

  • 操作系统 记录型信号量实现生产者消费者问题(完整代码)

    问题描述 用信号量模拟生产者 消费者问题的过程 生产者和消费者两个线程共享同一个缓冲区 xff0c 生产者不断向缓冲区中添加产品 xff0c 消费者从缓冲区中消费产品 要保证缓冲区满了之后生产者不能再继续添加产品 xff0c 需要等消费者至
  • 制版经验分享—使用AD18

    文章目录 前言一 封装二 走线三 注意细节四 制版流程五 制版细节总结 前言 在做一些培训题目时 xff0c 由于时间有限制 xff0c 在外面开板会花费好几天的制作和快递时间 xff0c 所以有时候就需要自己制版 xff0c 在这里我记录
  • Java打印九九乘法表

    1 使用双重for循环打印九九乘法表 Java源代码如下 xff1a for int i 61 0 i lt 61 9 i 43 43 for int j 61 1 j lt 61 i j 43 43 System out print i
  • 解决selenium打开Chrome浏览器自动退出的问题

    好不容易安装好selenium和对应的浏览器驱动器后终于可以运行程序了 xff0c 结果发现一运行程序后浏览器打开就自动退出了 xff0c 但是我在Python代码中并没有写driver quit 方法 xff0c 上网查了查发现原来是我的
  • 在Java应用中嵌入sshd服务

    这个应用需要依赖apache mina的子项目sshd xff0c 项目主页http mina apache org sshd project index html xff0c 当前版本号为0 8 0 这里的sshd和Linux下的sshd
  • openssl开发库安装时的踩坑指南

    序 前几天用linux编译一个提权脚本的时候报错 openssl opensslv h 没有那个文件或目录 的问题 无论如何也解决不了 xff0c 这下我记录一个踩坑指南防止下一个人掉进坑里 操作 总体介绍 首先介绍一下 xff0c 这个报
  • 性能测试脚本用例【模板】

    产品名称Product name 密级Confidentiality level 秘密 产品版本Product version Total 12pages 共12页 性能测试脚本用例 仅供内部使用 拟制 日期 xff1a 审核 日期 xff
  • Java常见的集合类

    我们常见的Java集合类有List Set Map List 1 接口可以被继承 2 接口可以被多次实现 3 List和ArrayList package List import java util ArrayList import jav
  • WIN7我的电脑右键管理打不开

    问题现象 xff1a 我的电脑右键点击管理无法正常打开 xff0c 会弹出下面的报错信息 首先打开注册表 xff0c 打开运行 xff0c 输入regedit 选择路径 xff1a HKEY LOCAL MACHINE SOFTWARE C
  • LIKE的用法

    我们来谈谈关于like运算符的理解 xff1a 下面是like的语法 xff0c 以后使用到like运算符的都必须根据这个语法使用 LIKE 运算符是用来匹配通配符指定模式的文本值 如果搜索表达式与模式表达式匹配 xff0c LIKE 运算
  • 从0开始详细安装archlinux(UEFI启动)

    隔了一周没更新 xff0c 前阵子把电脑windows卸了装了个archlinux xff0c 不得不说arch是真的香 xff0c 但是坑也是真的多 xff0c 刚踩完所有的坑 xff0c 滚回来写blog了 注 xff1a 本贴为UEF
  • archlinux开机无法联网问题,以及安装archlinuxcn和yay管理器

    前一篇已经安装完了archlinux系统 xff0c 不过真正难的其实并不是安装 xff0c 你的路现在才开始 xff0c 哈哈 添加用户 建议 xff0c 不然使用登录管理器的时候不支持root用户 span class token fu
  • archlinux安装kde桌面和sddm登录管理器

    前几篇已经配置好了archlinuxcn软件仓库 xff0c 网络和nvidia驱动 xff0c 现在来给你的archlinux安装一个kde桌面 xff08 kde玩法有很多 xff0c 可以自己去搜一搜美化教程 xff09 xff0c
  • Spring框架学习

    目录 目录 学习内容 xff1a IoC java中创建对象有哪些方式 xff1a ioc的体现 xff1a DI 是ioc的技术实现 spring的第一个核心功能 ioc Spring 八大模块 Spring的特点 xff1a Sprin
  • CenOs6.7不能使用yum命令

    因为官方不维护了所以 先用更换源 wget O etc yum repos d CentOS Base repo https mirrors aliyun com repo Centos vault 6 10 repo https deve
  • PowerMock注解PowerMockIgnore的使用方法

    故事要从一个异常开始 xff0c 某天我在开发一个加密 解密特性 xff0c 算法使用的是3DES xff0c 样例代码如下 package org jackie study powermock import java io Unsuppo
  • 如何写一棵AVL树

    二叉查找树 二叉查找树有一个缺陷就是查询效率跟树的高度有关 在极端情况下 xff0c 查询效率为n 如何解决二叉查找树效率低问题 xff1f 要增加查询效率 xff0c 高效的方案是在插入的时候对树进行一下平衡操作 xff0c 降低树的高度
  • 点击超链接下载.pdf文件

    上代码 span class token keyword package span span class token class name Servlet span span class token punctuation span res
  • SpringTemplate增删改查及其事务控制基本使用

    一 JdbcCRUD操作 1 导入坐标包 span class token generics span class token punctuation lt span dependency span class token punctuat
  • C语言从键盘读入一个正整数num,计算0 ~ num(包括num)范围内所有奇数之和

    include span class token generics span class token punctuation lt span stdio span class token punctuation span h span cl

随机推荐