快速排序、归并排序、基数排序

news2024/11/19 18:36:01

快速排序

算法思想

图 1-1

即确定一个基准值(一般为数组中间位置的元素,或者自定义),让待排序数组中所有比基准值小的元素放到基准值左边的位置,所有比基准值大的元素放到基准值右边的位置,这样一趟排序下来,基准值左边的元素都比基准值小,基准值右边的元素都会被基准值大,然后在分别对基准值左右两边的数据进行上述操作,最后得到一个有序数组。

快速排序示意图

图 1-2

代码实现

def quick_sort(arr: list, left: int, right: int):
    l = left  # 数组左边的下标
    r = right  # 数组右边的下标
    # 基准值,此处取数组中间位置的元素
    pivot = arr[(l + r) // 2]

    while l < r:
        # 从数组最左边开始,寻找比基准值大或等于基准值的元素
        # 找到则退出循环,此时 l 位置的元素就是比基准值大的元素
        while arr[l] < pivot:
            l += 1

        # 从数组最右边开始,寻找比基准值小或等于基准值的元素
        # 找到则退出循环,此时 r 位置的元素就是比基准值小的元素
        while arr[r] > pivot:
            r -= 1

        # 说明数组中的元素已经以基准值为分界点划分为两部分:比基准值大的在一边,小的在另一边
        # 退出循环,在下一次循环时对基准值左边/右边的数组再以新的基准值进行划分
        # 直到整个数组有序
        if l >= r:
            break

        # 把上述找到的两个元素交换
        # 即把比基准值大的元素放到基准值右边,比基准值小的放在基准值左边
        t = arr[l]
        arr[l] = arr[r]
        arr[r] = t

    # 如果 l == r ,要分别加1 和 减1,否则会在 while l < r 处进入死循环
    if l == r:
        l += 1
        r -= 1

    if left < r:  # 对基准值左边的数据进行递归,重复上述步骤
        quick_sort(arr, left, r)
    if l < right:  # 对基准值右边的数据进行递归,重复上述步骤
        quick_sort(arr, l, right)


arr = [-9, 78, 0, 23, -567, 70]
quick_sort(arr, 0, len(arr) - 1)
print(arr)  # [-567, -9, 0, 23, 70, 78]

归并排序

算法思想

图 2-1

图 2-2

数据的每一次合并过程都是将两个已经有序的数组合并成一个有序数组,遵从相关算法,图2-2的最后一轮合并(第七次合并)如下图:

图 2-3

代码实现

def merge(arr: list, left: int, mid: int, right: int):
    """
    归并排序的合并部分,本质就是将两个有序数组合并为一个有序数组
    现假设两个有序数组分别为数组A和数组B,这两个有序数组都放在数组arr中,以mid位置为分割,mid是数组A的最后一个元素的位置
    数组A是图2-3中蓝绿色部分,数组B就是图2-3的粉色部分
    :param arr: 待排序数组,包含两个待合并的有序数组,两个有序数组以mid为分割点,数组A是arr的左边部分,数组B是arr右边部分,mid也是数组A的最后一个元素的位置
    :param left: 数组A的第一个元素的下标
    :param mid: 数组A和数组B起始都是待排序数组中的一部分,mid表示数组A和数组B的中间位置,也就是数组A的最后一个元素
    :param right: 数组B的最后一个元素的下标
    :return:
    如待排序数组为:[8,4,5,7,1,3,6,2],最后将他们分割为八个数组:8,4,5,7,1,3,6,2
    1、然后将这八个数组两两合并:8和4,5和7,1和3,6和2,得到四个数组
    第一次合并:合并8和4,所以 arr=[8,4,5,7,1,3,6,2], left=0, mid=0, right=1,
                数组A=[8], 数组B=[4], 将A和B元素按大小放入temp数组, temp最终结果为:[4,8]
                将temp复制到arr中,arr最终为:[4,8,5,7,1,3,6,2]
    同理:
    第二次合并:合并5和7,所以 arr=[4,8,5,7,1,3,6,2], left=2, mid=2, right=3,
                数组A=[5], 数组B=[7], 将A和B元素按大小放入temp数组, temp最终结果为:[5,7]
                将temp复制到arr中,arr最终为:[4,8,5,7,1,3,6,2]
    第三次合并:合并1和3,所以 arr=[4,8,5,7,1,3,6,2], left=4, mid=4, right=5,
                数组A=[1], 数组B=[3], 将A和B元素按大小放入temp数组, temp最终结果为:[1,3]
                将temp复制到arr中,arr最终为:[4,8,5,7,1,3,6,2]
    第四次合并:合并6和2,所以 arr=[4,8,5,7,1,3,6,2], left=6, mid=6, right=7,
                数组A=[6], 数组B=[2], 将A和B元素按大小放入temp数组, temp最终结果为:[2,6]
                将temp复制到arr中,arr最终为:[4,8,5,7,1,3,2,6]
    2、接下来再把四个数组:[4,8]和[5,7], [1,3]和[2,6]再两两合并,得到两个数组
    第五次合并:合并[4,8]和[5,7],所以 arr=[4,8,5,7,1,3,2,6], left=0, mid=1, right=3,
                数组A=[4,8], 数组B=[5,7], 将A和B元素按大小放入temp数组, temp最终结果为:[4,5,7,8]
                将temp复制到arr中,arr最终为:[4,5,7,8,1,3,2,6]
    第六次合并:合并[1,3]和[2,6],所以 arr=[4,5,7,8,1,3,2,6], left=4, mid=5, right=7,
                数组A=[1,3], 数组B=[2,6], 将A和B元素按大小放入temp数组, temp最终结果为:[1,2,3,6]
                将temp复制到arr中,arr最终为:[4,5,7,8,1,2,3,6]
    3、最后把两个数组:[4,5,7,8]和[1,2,3,6]合并为一个数组,得到归并排序的最终结果
    第七次合并:合并[4,5,7,8]和[1,2,3,6],所以 arr=[4,5,7,8,1,2,3,6], left=0, mid=3, right=7,
                数组A=[4,5,7,8], 数组B=[1,2,3,6], 将A和B元素按大小放入temp数组, temp最终结果为:[1,2,3,4,5,6,7,8]
                将temp复制到arr中,arr最终为:[1,2,3,4,5,6,7,8]
    """
    i = left  # 指向数组A的第一个元素位置
    j = mid + 1  # 指向数组B的第一个元素位置
    temp = []  # 临时保存元素的中转数组
    # t = 0  # 指向中转数组最后一个元素的位置(中转数组初始为空)

    # 依次遍历数组A和数组B,将两个数组中较小的元素依次放入中转数组temp(此处按从小到大排序)
    # 直到两个数组中有一个遍历完毕,则退出循环
    while (i <= mid and j <= right):
        if arr[i] <= arr[j]:  # 数组A中 i 位置的元素比数组B中 j 位置的元素要小,所以小的放入中转数组
            temp.append(arr[i])
            i += 1
        else:  # 反之,数组B中 j 位置的元素比数组A中 i 位置的元素要小,所以小的放入中转数组
            temp.append(arr[j])
            j += 1

    # 退出循环说明有一个数组遍历结束,将另一个没遍历结束的数组的剩余元素全部添加到中转数组temp中
    while i <= mid:  # 说明先结束的是数组B,将数组A剩余元素添加到中转数组temp
        temp.append(arr[i])
        i += 1
    while j <= right:  # 说明先结束的是数组A,将数组B剩余元素添加到中转数组temp
        temp.append(arr[j])
        j += 1

    # 将中转数组temp的元素拷贝到数组arr
    t = 0
    left_temp = left
    while left_temp <= right:
        arr[left_temp] = temp[t]
        left_temp += 1
        t += 1


def merge_sort(arr: list, left: int, right: int):
    """
    归并排序的分组及合并部分
    :param arr: 待排序的数组
    :param left: 已拆分数组的起始位置
    :param right: 已拆分数组的最后位置
    :return:
    """
    if left < right:
        mid = (left + right) // 2
        merge_sort(arr, left, mid)  # 向左递归
        merge_sort(arr, mid + 1, right)  # 向右递归
        merge(arr,left, mid, right)  # 合并


sort_arr = [8, 4, 5, 7, 1, 3, 6, 2]
merge_sort(sort_arr, 0, len(sort_arr) - 1)
print(sort_arr)

基数排序

图 3-1

图 3-2

算法思想

图 3-3

代码实现

详细步骤代码
def radix_sort_detail(arr: list):
    # 假设 arr=[53,3,542,748,14,214]
    # 定义一个二维数组bucket[][],二维数组中的第一维bucket[]表示一个桶,即每个桶是一个一维数组
    # 因为有0-9十个数字,所以有十个桶,即二维数组的长度为 10
    # 每个桶从0开始编号,即名字依次为 0-9
    # 其中每一个桶用来存储待排序的元素
    # 因为极端情况下可能每个待排序的元素的位数数字一样(如所有待排序元素的个位数都是0),
    # 所以每个桶的空间都需要和待排序数组长度一样
    # 由此可见基数排序是典型的以空间换时间
    bucket = []
    # 构造一个具有10行的二维数组
    for i in range(10):
        bucket.append([])

    # 假设 arr=[53,3,542,748,14,214]
    ############### 第一轮排序:根据待排序数组元素的个位数进行 ###############
    # 遍历待排序数组,得到每个元素的个位数,根据个位数对应的数字将该元素放入对应的桶中
    # 如某个元素的个位数是0,则将该元素放入0号桶,如果是2,则放入2号桶
    for item in arr:
        digit = item % 10  # 获取元素的个位数
        # 根据个位数的值,将元素放入到对应的桶中
        bucket[digit].append(item)

    # 将所有元素放入到对应的桶中后
    # 从第0个桶开始,依次获取每个桶中的所有元素,将元素放入到arr中
    idx = 0
    for i in range(len(bucket)):  # i为:0-9
        if len(bucket[i]) != 0:  # 如果桶中有元素,则遍历该桶获取桶中的每一个元素
            for item in bucket[i]:
                arr[idx] = item
                idx += 1
            # 每一轮排序过后,把桶清空
            bucket[i].clear()
    print('第一轮:', arr)

    ############### 第二轮排序:根据待排序数组元素的十位数进行 ###############
    # 遍历待排序数组,得到每个元素的十位数,根据十位数对应的数字将该元素放入对应的桶中
    # 如某个元素的十位数是0,则将该元素放入0号桶,如果是2,则放入2号桶
    for item in arr:
        digit = item // 10 % 10  # 获取元素的十位数
        # 根据百位数的值,将元素放入到对应的桶中
        bucket[digit].append(item)

    # 将所有元素放入到对应的桶中后
    # 从第0个桶开始,依次获取每个桶中的所有元素,将元素放入到arr中
    idx = 0
    for i in range(len(bucket)):  # i为:0-9
        if len(bucket[i]) != 0:  # 如果桶中有元素,则遍历该桶获取桶中的每一个元素
            for item in bucket[i]:
                arr[idx] = item
                idx += 1
            # 每一轮排序过后,把桶清空
            bucket[i].clear()
    print('第二轮:', arr)

    ############### 第三轮排序:根据待排序数组元素的百位数进行 ###############
    # 遍历待排序数组,得到每个元素的百位数,根据百位数对应的数字将该元素放入对应的桶中
    # 如某个元素的百位数是0,则将该元素放入0号桶,如果是2,则放入2号桶
    for item in arr:
        digit = item // 100 % 10  # 获取元素的百位数
        # 根据百位数的值,将元素放入到对应的桶中
        bucket[digit].append(item)

    # 将所有元素放入到对应的桶中后
    # 从第0个桶开始,依次获取每个桶中的所有元素,将元素放入到arr中
    idx = 0
    for i in range(len(bucket)):  # i为:0-9
        if len(bucket[i]) != 0:  # 如果桶中有元素,则遍历该桶获取桶中的每一个元素
            for item in bucket[i]:
                arr[idx] = item
                idx += 1
            # 每一轮排序过后,把桶清空
            bucket[i].clear()
    print('第三轮:', arr)


arr = [53, 3, 542, 748, 14, 214]
radix_sort_detail(arr)
print(arr)
完整代码
def radix_sort(arr: list):
    # 假设 arr=[53,3,542,748,14,214]
    # 定义一个二维数组bucket[][],二维数组中的第一维bucket[]表示一个桶,即每个桶是一个一维数组
    # 因为有0-9十个数字,所以有十个桶,即二维数组的长度为 10
    # 每个桶从0开始编号,即名字依次为 0-9
    # 其中每一个桶用来存储待排序的元素
    # 因为极端情况下可能每个待排序的元素的位数数字一样(如所有待排序元素的个位数都是0),
    # 所以每个桶的空间都需要和待排序数组长度一样
    # 由此可见基数排序是典型的以空间换时间
    bucket = []
    # 构造一个具有10行的二维数组
    for i in range(10):
        bucket.append([])

    # 获取待排序数组中的最大的数,然后获取最大数是几位数
    # 几位数决定了需要排序多少趟
    max = 0
    for item in arr:
        if max < item:
            max = item
    # 获取一个数是几位数的技巧:将数转换成字符串然后求其长度
    count = len(str(max))

    num = 0
    n = 1  # 表示正在获取的位数(个位数、十位数、百位数...)
    while num < count:
        # 遍历待排序数组,得到每个元素的每一位数digit,根据digit对应的数字将该元素放入对应的桶中
        # 如digit=0,则将该元素放入0号桶,如果是2,则放入2号桶
        for item in arr:
            digit = item // n % 10  # 依次获取获取元素的个位数、十位数、百位数...
            # 根据digit的值,将元素放入到对应的桶中
            bucket[digit].append(item)

        # 将所有元素放入到对应的桶中后
        # 从第0个桶开始,依次获取每个桶中的所有元素,将元素放入到arr中
        idx = 0
        for i in range(len(bucket)):  # i为:0-9
            if len(bucket[i]) != 0:  # 如果桶中有元素,则遍历该桶获取桶中的每一个元素
                for item in bucket[i]:
                    arr[idx] = item
                    idx += 1
                # 读取桶中的所有元素后,把桶清空
                bucket[i].clear()

        # 从个位数开始,依次向更高位取值
        num += 1
        n *= 10
        

arr = [53, 3, 542,2014, 748, 1451, 214, 100]
radix_sort(arr)
print(arr)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1095021.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rxjava3 全新详解及常用操作符

简介 RxJava 是一个基于 Java 的响应式编程库&#xff0c;用于处理异步事件流和数据流。它是由 Netflix 开发并开源&#xff0c;现在广泛用于 Android 和 Java 后端开发。RxJava 提供了一种用于组合和处理异步数据的丰富工具集&#xff0c;它的核心思想是将数据流视为一系列事…

Hadoop3教程(十):MapReduce中的InputFormat

文章目录 &#xff08;87&#xff09;切片机制与MapTask并行度决定机制&#xff08;90&#xff09; 切片源码总结&#xff08;91&#xff09;FileInputFormat切片机制&#xff08;92&#xff09;TextInputFormat及其他实现类一览&#xff08;93&#xff09; CombineTextInputFo…

深入解析C语言中的strstr函数

目录 一&#xff0c;strstr函数简介 二&#xff0c;strstr函数实现原理 三&#xff0c;strstr函数的用法 四&#xff0c;strstr函数的注意事项 五&#xff0c;strstr函数的模拟实现 一&#xff0c;strstr函数简介 strstr函数是在一个字符串中查找另一个字符串的第一次出现&…

QT6集成CEF3--01 准备工作

QT6集成CEF3--01 准备工作 一、所有使用到的工具软件清单:二、准备工作三、cefclient示例程序四、特别注意 一、所有使用到的工具软件清单: CEF 二进制发行包 cef_binary_117.2.5gda4c36achromium-117.0.5938.152_windows64.tar.bz2 CMake 编译工具 cmake-3.22.6-windows-x86_…

实践笔记-docker安装及配置镜像源

docker安装及配置镜像源 1.卸载旧版本docker2. 安装yum工具3. 设置镜像仓库4.安装docker5. 启动docker服务6.docker镜像源配置 当前环境linux为centos7 1.卸载旧版本docker 如果以前安装过旧版本可以先执行卸载&#xff0c;下面的截图是没有安装过。 yum remove docker \ do…

DID赛道前列的生物识别技术,开启Web3时代的大门—MXT

互联网发展的十字路口 互联网从上世纪90年代初发展至今&#xff0c;历经30年&#xff0c;她改变了整个人类的生活方式、沟通形式以及社会发展模式&#xff0c;她的影响早已渗透到了世界的各个角落。而如今&#xff0c;我们似乎正站在一个新的十字路口&#xff0c;一个互联网将…

端口被谁占用如何解决?

## 如何查看端口被谁占用了&#xff1f;你好,我是Hasity 今天分享的内容是&#xff1a;Windows/Linux端口被占用如何解决? 问题出现 Description:Web server failed to start. Port 8082 was already in use.Action:Identify and stop the process thats listening on port 8…

mp4文件怎样提取mp3音频文件

Mp4视频文件怎样提取mp3等音频文件呢&#xff1f; 推荐一个免费网址&#xff0c;且不用任何注册&#xff0c;直接可以使用 https://audio-extractor.net/cn/ 上传视频&#xff0c;选择转换的音频格式&#xff0c; 更多格式&#xff0c;包括如下&#xff1a; 转换文件完成后…

【JavaEE】初识网络

网络初识 文章目录 网络初识网络发展史独立模式网络互连局域网LAN广域网 网络通信基础IP地址端口号格式 协议五元组协议分层OSI七层模型TCP/IP五&#xff08;四&#xff09;层协议1.物理层2.数据链路层3.网络层4.传输层5.应用程序 网络设备所在的分层封装和分用例子发送方接收方…

PyTorch入门教学——dir()函数和help()函数的应用

1、简介 已知PyTorch是一个工具包&#xff0c;其中包含许多功能函数。dir()函数和help()函数是学习PyTorch包的重要法宝。 dir()&#xff1a;能让我们知道工具包以及工具包中的分隔区有什么东西。help()&#xff1a;能让我们知道每个工具是如何使用的&#xff0c;即工具的使用…

Redis数据序列化器

Redis序列化 Redis 数据序列化器用于将数据在存储到 Redis 中时进行序列化&#xff08;编码&#xff09;和反序列化&#xff08;解码&#xff09;。 RedisTemplate的两种序列化实践方案&#xff1a; 方案一&#xff1a; 自定义RedisTemplate 修改RedisTemplate的序列化器为…

从一到无穷大 #18 时序数据库运营SLI思考

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。 本作品 (李兆龙 博文, 由 李兆龙 创作)&#xff0c;由 李兆龙 确认&#xff0c;转载请注明版权。 公有云时序数据库SLA 运营商产品每服务周期服务可用率不低于99.9%衡量服务不可用数据指标从采…

SpringAOP是什么?为什么要有SpringAOP?

SpringAOP是什么&#xff1f;为什么要有SpringAOP&#xff1f; 原文&#xff1a;SpringAOP是什么&#xff1f;为什么要有SpringAOP&#xff1f; 一、有SpringAOP之前 简单的开发场景&#xff0c;只需要写简单的业务逻辑&#xff0c;比如CRUD 但是在执行具体的逻辑之前&…

【论文精读】NMP: End-to-end Interpretable Neural Motion Planner

toc 1 背景信息 团队&#xff1a;Uber&#xff0c;多伦大大学 年份&#xff1a;2019 论文链接&#xff1a;https://arxiv.org/abs/2101.06679 2 Motivation 深度学习方案受限于累积误差suffers from the compounding error&#xff0c;而且可解释性差interpretability is d…

如何设计一个ToC的弹窗

本文主要分享了如何设计一个具有高可扩展性的弹窗功能。 本设计参考了优惠券功能的设计思路&#xff0c;有兴趣的朋友可以看看优惠券的分享&#xff1a;如何设计一个可扩展的优惠券功能_java优惠券系统设计-CSDN博客 一、需求介绍 假如你的项目需要实现以下弹窗&#xff0c;…

排序算法-合并排序法(MergeSort)

排序算法-合并排序法&#xff08;MergeSort&#xff09; 1、说明 合并排序法&#xff08;MergeSort&#xff09;是针对已排序好的两个或两个以上的数列&#xff08;或数据文件&#xff09;&#xff0c;通过合并的方式将其组合成一个大的且已排好序的数列&#xff08;或数据文…

debian9换源存在的问题

今天租借了云服务器搭建了debian9&#xff0c;给apt-get换源过程中出现了很多问题&#xff0c;主要有两个问题&#xff1a; 问题1&#xff1a; sudo apt-get update 0% [Working]问题2&#xff1a; W: The repository http://mirrors.aliyun.com/debian stretch Release doe…

软考网工选择题易错总结(2020下~2022下)

目录 2020下半年 2021上半年 2021年下半年 2022上半年 2022下半年 2020下半年 15、在卫星通信中&#xff0c;通常采用的差错控制机制为&#xff08;15&#xff09;。 A.停等 ARQ B.后退 N 帧 ARQ C.选择重发 ARQ D.最大限额 ARQ 答案&#xff1…

Golang基础学习笔记

Golang基础学习笔记 1、下载安装 1.1、下载 Golang下载地址&#xff1a;https://golang.google.cn/dl/ 1.2、安装 1.3、环境变量 # GOPATH D:\GolandProjects# GOPROXY https://mirrors.aliyun.com/goproxy# 启用Go模块支持 go env -w GO111MODULEon1.5、验证安装/配置 1.…

Premiere pro

操作 1.音乐淡入淡出 窗口 效果 音频过度 交叉淡化 指数淡化 拖动到首尾音频上2.手机视频序列设置 3.视频转场淡入淡出 选中视频 在视频连接处 交叉溶解