排序算法4:【快速排序】、查看每趟归并后的结果,定义一个全局变量,用来计数作为总趟数

news2024/12/23 4:46:33

一、快速排序——时间复杂度:O(n\log n)、 最坏的情况O(n^{2})

1、原理:

        快速排序是通过多次比较和交换来实现排序,首先,先从数列中,任意选择一个数作为基准(或叫分界值),比如,第一个数,然后将数列中比基准小的数都放到基准左边,比基准大的数都放到基准右边,这样一趟下来之后呢,这个数列就被基准分成了左右两个独立的部分,(也就是说,此时,基准就处于数列的中间位置)。然后像刚才一样,分别继续递归这两部分,最终使得数列变得有序。

具体步骤:

1、首先,先从数列中,任意选择一个数作为基准,(比如,第一个数),通过这个基准将数据分成左右两个部分。

2、将数列中比基准小的数都放到基准左边,比基准大或者等于的数放到基准右边,这样就完成了第一趟排序,此时,基准就处于数列的中间位置,左边的数都小于这个基准,而右边的数,都大于或等于基准。

3、然后,再把左右两边像刚才一样,再分别找个基准再进行划分为两个部分,(也就是递归),直到左右两边都只有一个数时为止,此时列表已排好序。

        总的来说就是,先任意取一个元素,比如a ,使得a归位,所谓归位就是说,列表被这个元素a 分成了两部分,左边都是比a小,右边都是比a大,然后再对左右两边递归完成排序。

2、举例:第一趟排序效果图:

注意:

1、如果是left指向坑,看right左移,找小】用right所指的数,与基准进行比较如果这个数>=基准,那right-1,把它向左移动一个位置,继续与基准进行比较,直到找到比基准小的这个数,然后放到left所指的坑里。——填坑   

2、如果是right指向坑,看left,右移,找大】,如果left所指的数小于等于基准,那就把left+1,把它向右移动一个位置,继续与基准进行比较,直到找到比基准大的数然后把它放到right所指的坑里

3、快速排序代码

def partition(li, left, right):  # partition:分区
    tmp = li[left]
    while left < right:  # 只要left和right没有重合(或叫碰头),说明还有数据没有跟基准进行比较
        while left < right and li[right] >= tmp:  # 坑指向left,看right,找大
            right -= 1  # 只要这个数大于tmp,那right就往左移一步,继续找
        li[left] = li[right]  # 把右边的值写到左边空位上

        while left < right and li[left] <= tmp: # 坑指向right,看left,找大
            left += 1
        li[right] = li[left]

    li[left] = tmp  # 或写li[right] = tmp,都一样,因为跳出循环时候,left=right
    return left  # 返回mid的值,也可以写return right


def quick_sort(li, left, right):
    if left < right:  # 至少有两个元素的时候,才递归
        mid = partition(li, left, right)  # mid出来以后说明第一个元素已经归位了,或者叫已完成第一趟排序,然后开始递归左右两部分
        quick_sort(li, left, mid - 1)  # 递归左边
        quick_sort(li, mid + 1, right)  # 递归右边


l1 = [5, 1, 6, 7, 2, 4, 8, 5]
print(l1)
quick_sort(l1, 0, len(l1) - 1)
print(l1)

# 结果:
[5, 1, 6, 7, 2, 4, 8, 5]
[1, 2, 4, 5, 5, 6, 7, 8]

4、查看每趟归并后的结果

如果想看每趟归并后的结果怎么办?定义一个全局变量,用来记录趟数,代码如下:

i = 0  # 定义i用来计数
def partition(li, left, right):
    global i  # !!!!
    tmp = li[left]
    while left < right:  
        while left < right and li[right] >= tmp:  # 填left所指的坑
            right -= 1
        li[left] = li[right]
        # print(li, "right")   # 每一次填left坑的结果

        while left < right and li[left] <= tmp:  # 填right所指的坑
            left += 1
        li[right] = li[left]
        # print(li, "left")  # 每一次填right坑的结果
    i += 1   # !!!!
    li[left] = tmp  # 或写li[right] = tmp,因为跳出循环时候,left=right
    print("第%s趟归位后的结果:" % i, li)  # 每一趟归并后的结果   # !!!!
    return left  # 或写成return left


def quick_sort(li, left, right):
    if left < right:
        mid = partition(li, left, right)  # mid出来以后说明第一个元素已经归位了,或者叫已完成第一趟排序
        quick_sort(li, left, mid - 1)
        quick_sort(li, mid + 1, right)


l1 = [5, 1, 6, 7, 2, 4, 8, 5]
quick_sort(l1, 0, len(l1) - 1)
print(l1)

# 结果:
第1趟归位后的结果: [4, 1, 2, 5, 7, 6, 8, 5]
第2趟归位后的结果: [2, 1, 4, 5, 7, 6, 8, 5]
第3趟归位后的结果: [1, 2, 4, 5, 7, 6, 8, 5]
第4趟归位后的结果: [1, 2, 4, 5, 5, 6, 7, 8]
第5趟归位后的结果: [1, 2, 4, 5, 5, 6, 7, 8]
[1, 2, 4, 5, 5, 6, 7, 8]

5、快速排序所存在的问题

1、快速排序最坏情况

        比如,当列表是个降序时,此时再用快速排序,每次都只排了一个数,或者说,每次递归都只递归了一边,不会像平时那样左右两边都会递归,一趟能排好多数,所以这里最坏的情况就是O(n),不是O(\log n),总的快速排序复杂度就是O(n^{2}),当然,这种最坏的情况是比较少的。

例如:当li=[9,8,7,6,5,4,3,2,1]时候

具体代码:

i = 0  # !!

def partition(li, left, right):
    global i  # !!!!
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:  # 填left所指的坑
            right -= 1
        li[left] = li[right]
        # print(li, "right")   # 每一次填left坑的结果

        while left < right and li[left] <= tmp:  # 填right所指的坑
            left += 1
        li[right] = li[left]
        # print(li, "left")  # 每一次填right坑的结果
    i += 1  # !!!!
    li[left] = tmp  # 或写li[right] = tmp,因为跳出循环时候,left=right
    print("第%s趟归位后的结果:" % i, li)  # 每一趟归并后的结果   # !!!!
    return left  # 或写成return left


def quick_sort(li, left, right):
    if left < right:
        mid = partition(li, left, right)  # mid出来以后说明第一个元素已经归位了,或者叫已完成第一趟排序
        quick_sort(li, left, mid - 1)
        quick_sort(li, mid + 1, right)


l1 = [9, 8, 7, 6, 5, 4, 3, 2, 1]  # 快排最坏情况
quick_sort(l1, 0, len(l1) - 1)
print(l1)
# 结果:
第1趟归位后的结果: [1, 8, 7, 6, 5, 4, 3, 2, 9]
第2趟归位后的结果: [1, 8, 7, 6, 5, 4, 3, 2, 9]
第3趟归位后的结果: [1, 2, 7, 6, 5, 4, 3, 8, 9]
第4趟归位后的结果: [1, 2, 7, 6, 5, 4, 3, 8, 9]
第5趟归位后的结果: [1, 2, 3, 6, 5, 4, 7, 8, 9]
第6趟归位后的结果: [1, 2, 3, 6, 5, 4, 7, 8, 9]
第7趟归位后的结果: [1, 2, 3, 4, 5, 6, 7, 8, 9]
第8趟归位后的结果: [1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9]

2、最大深度问题

        对于递归来说,不仅会消耗一部分系统资源,还会遇到最大深度问题。

例如:利用range( )函数,创建一个长度为1000倒叙的列表,此时就会递归1000次,就会报错,遇到最大深度问题。

l1 = list(range(1000, 0, -1))  # 创建一个长度为1000倒叙的列表
quick_sort(l1, 0, len(l1) - 1)
print(l1)

# 结果:
RecursionError: maximum recursion depth exceeded in comparison

进程已结束,退出代码 1

怎么解决?

修改最大深度在代码最上面写上下面两行。
import sys

sys.setrecursionlimit(10000)  # 设置最大递归深度为10000

修改后的完整代码:

import sys
sys.setrecursionlimit(10000)  # 设置最大递归深度

def partition(li, left, right):
    tmp = li[left]
    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]

        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]

    li[left] = tmp
    return left


def quick_sort(li, left, right):
    if left < right:  # 至少有两个元素的时候,才递归
        mid = partition(li, left, right)
        quick_sort(li, left, mid - 1)
        quick_sort(li, mid + 1, right)

l1 = list(range(1000, 0, -1))  # 创建一个长度为1000倒叙的列表
quick_sort(l1, 0, len(l1) - 1)
print(l1)

# 结果:
[1, 2, 3, 4, 5, 6, ......一直到999,1000]

6、怎么避免出现最坏情况?

        解决办法:利用随机化版本的快速排序方法。但这种方法也并不能完全避免出现最坏情况,因为,随机数也有可能找到最两端的数。但一般情况下,出现最坏概率比较下,通常快排没啥大的问题。

        之前我们基准取的都是列表里的第一个元素,现在我们不取第一个元素了,而是从列表里随机取一个元素,让它作为基准

具体做法:

1、导入random模块,利用:random.choice( 列表名),从列表里随机取一个数。

2、让这个数跟列表的第一个数,交换一下

        列表的第一个数:li[0]

        从列表里随机取个数:a = random.choice(列表名),

        找到它在列表中的位置a_loc = li.index(a)

将随机数与列表第一个数进行交换,使得随机数作为基准

li[0], li[a_loc] = a, li[0] 

3、再按照之前写的归位方法(partition函数),把第一个拿出来,进行比较后归位再对两边递归等等,后面操作跟之前都是一样的了。

延申:从列表里随机取一个数的方法。

import random

l1 = [1, 4, 5, 2]
print(random.choice(l1))
# 结果
1 或2 或4 或5

 举例:避免最坏情况发生的例题。

 例如:创建一个长度为5倒叙的列表。

import random

i = 0

def partition(li, left, right):
    global i  # !!!!

    tmp = li[left]

    while left < right:
        while left < right and li[right] >= tmp:
            right -= 1
        li[left] = li[right]

        while left < right and li[left] <= tmp:
            left += 1
        li[right] = li[left]

    i += 1  # !!!!
    li[left] = tmp  
    print("第%s趟归位后的结果:" % i, li)  # 每一趟归并后的结果   # !!!!
    return left 


def quick_sort(li, left, right):
    if left < right:
        mid = partition(li, left, right)
        quick_sort(li, left, mid - 1)
        quick_sort(li, mid + 1, right)


l1 = list(range(5, 0, -1))  # 创建一个长度为5倒叙的列表
print(l1)
a = random.choice(l1)
print("列表随机取的数是:%d ,列表第一个元素是:%d" % (a, l1[0]))
a_loc = l1.index(a)  # 找到随机数在列表里的位置
l1[0], l1[a_loc] = a, l1[0]  # 将随机数与列表第一个数进行交换,使得随机数作为基准
print("互换后的列表:", l1)
quick_sort(l1, 0, len(l1) - 1)
print(l1)
# 结果:
[5, 4, 3, 2, 1]
列表随机取的数是:3 ,列表第一个元素是:5
互换后的列表: [3, 4, 5, 2, 1]
第1趟归位后的结果: [1, 2, 3, 5, 4]
第2趟归位后的结果: [1, 2, 3, 5, 4]
第3趟归位后的结果: [1, 2, 3, 4, 5]
[1, 2, 3, 4, 5]

进程已结束,退出代码 0

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

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

相关文章

MySQL的事务以及springboot中如何使用事务

事务的四大特性&#xff1a; 概念&#xff1a; 事务 是一组操作的集合&#xff0c;它是不可分割的工作单元。事务会把所有操作作为一个整体&#xff0c;一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 注意&#xff1a; 默认MySQ…

图解transformer中的自注意力机制(备忘)

注意力机制 在整个注意力过程中&#xff0c;模型会学习了三个权重:查询、键和值。查询、键和值的思想来源于信息检索系统。所以我们先理解数据库查询的思想。 假设有一个数据库&#xff0c;里面有所有一些作家和他们的书籍信息。现在我想读一些Rabindranath写的书&#xff1a…

网络编程----select 模型总结

为什么要使用select模型&#xff1f; 答&#xff1a;解决基本C/S模型中&#xff0c;accept()、recv()、send()阻塞的问题 select模型与C/S模型的不同点 C/S模型中accept()会阻塞一直傻等socket来链接select模型只解决accept()傻等的问题&#xff0c;不解决recv(),send()执行…

Android View闪烁动画AlphaAnimation,Kotlin

Android View闪烁动画AlphaAnimation&#xff0c;Kotlin private fun flickerAnimation(view: View?) {val animation: Animation AlphaAnimation(1f, 0f) //不透明到透明。animation.duration 500 // 1次过程时长。animation.interpolator LinearInterpolator() // 线性速…

一天搞定jmeter入门到入职全套教程之Jmeter分布式测试

随着并发量的增大&#xff0c;一台机器就不能满足需求了&#xff0c;所以我们采用分布式&#xff08;Master-Slaver&#xff09;的方案去执行高并发的测试 注意事项&#xff1a; Master机器一般我们不执测试&#xff0c;所以可以拿一台配置差些的机器&#xff0c;主要用来采集…

YOLOv8改进 | 2023主干篇 | 利用RT-DETR特征提取网络PPHGNetV2改进YOLOv8(超级轻量化精度更高)

一、本文介绍 本文给大家带来利用RT-DETR模型主干HGNet去替换YOLOv8的主干&#xff0c;RT-DETR是今年由百度推出的第一款实时的ViT模型&#xff0c;其在实时检测的领域上号称是打败了YOLO系列&#xff0c;其利用两个主干一个是HGNet一个是ResNet&#xff0c;其中HGNet就是我们…

养牛场北斗综合管理系统解决方案

1.系统架构 随着我国北斗卫星导航定位系统的快速发展和定位精度的持续不断提高&#xff0c;在牛身上穿戴定位终端后可以实现对牛的位置和温度的测量&#xff0c;在蜂窝网络正常的情况下&#xff0c;定位和温度数据通过蜂窝网络通信方式回传到监控云平台&#xff0c;在蜂窝网络缺…

使用docker编排容器

使用Dockerfile构建一个自定义的nginx 首先用docker拉一个nginx镜像 docker pull nginx拉取完成后&#xff0c;编辑一个Dockerfile文件 vim Dockerfile命令如下所示,FROM 后面跟的你的基础镜像&#xff0c;而run则是表示你构建镜像时需要执行的指令&#xff0c;下面的指令意…

mysql的ON DELETE CASCADE 和ON DELETE RESTRICT区别

​​ON DELETE CASCADE​​​ 和 ​​ON DELETE RESTRICT​​ 是 MySQL 中两种不同的外键约束级联操作。它们之间的主要区别在于当主表中的记录被删除时,子表中相关记录的处理方式。 ON DELETE CASCADE:当在主表中删除一条记录时,所有与之相关的子表中的匹配记录也会被自动删…

Android studio:打开应用程序闪退的问题2.0

目录 找到问题分析问题解决办法 找到问题 老生常谈&#xff0c;可能这东西真的很常见吧&#xff0c;在之前那篇文章中 linkhttp://t.csdnimg.cn/UJQNb 已经谈到了关于打开Androidstuidio开发的软件后明明没有报错却无法运行&#xff08;具体表现为应用程序闪退的问题&#xff…

MySQL之创建时间类型的字段表

mysql之创建时间类型的字段表 CREATE TABLE tab(birthday DATE, -- 生日job_time DATETIME, -- 记录年月日时分秒login_time TIMESTAMP -- 时间戳NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP )解释&#xff1a; NOT NULL DEFAULT &#xff1a;默认不为空…

快速幂(C语言)

前言 快速幂算法一般用于高次幂取模的题目中&#xff0c;比如求3的10000次方对7取模。这时候有些同学会说&#xff1a;这还不简单&#xff1f;我直接调用pow函数然后对结果%7不得了么&#xff1f;可是3的10000次方这么庞大的数字&#xff0c;真的能储存在计算机里么&#xff1f…

Logstash输入Kafka输出Es配置

Logstash介绍 Logstash是一个开源的数据收集引擎&#xff0c;具有实时管道功能。它可以从各种数据源中动态地统一和标准化数据&#xff0c;并将其发送到你选择的目的地。Logstash的早期目标主要是用于收集日志&#xff0c;但现在的功能已经远远超出这个范围。任何事件类型都可…

技术资讯:VSCode大更新,这两个功能终于有了

大家好&#xff0c;我是大澈&#xff01; 本文约1200字&#xff0c;整篇阅读大约需要2分钟。 感谢关注微信公众号&#xff1a;“程序员大澈”&#xff0c;然后免费加入问答群&#xff0c;从此让解决问题的你不再孤单&#xff01; 1. 资讯速览 就在前阵子&#xff0c;前端人都…

【Android嵌入式开发及实训课程实验】【项目1】 图形界面——计算器项目

【项目1】 图形界面——计算器项目 需求分析界面设计实施1、创建项目2、 界面实现实现代码1.activity_main.xml2.Java代码 - MainActivity.java 3、运行测试 注意点结束~ 需求分析 开发一个简单的计算器项目&#xff0c;该程序只能进行加减乘除运算。要求界面美观&#xff0c;…

【Java 基础】27 XML 解析

文章目录 1.SAX 解析器1&#xff09;什么是 SAX2&#xff09;SAX 工作流程初始化实现事件处理类解析 3&#xff09;示例代码 2.DOM 解析器1&#xff09;什么是 DOM2&#xff09;DOM 工作流程初始化解析 XML 文档操作 DOM 树 3&#xff09;示例代码 总结 在项目开发中&#xff0…

电脑ffmpeg.dll丢失如何修复?3个详细修复的教程分享

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“ffmpeg.dll丢失”。ffmpeg.dll是FFmpeg多媒体框架中的一个重要组件&#xff0c;它负责处理音频和视频的编解码。当这个文件丢失或损坏时&#xff0c;可能会导致一些应用程序无法正常运行。…

iframe 与主应用页面之间如何互相通信传递数据

背景 当我们的Web页面需要复用现有网站的页面时&#xff0c;我们通常会考虑代码层面的抽离引用&#xff0c;但是对于一些过于复杂的页面&#xff0c;通过 iframe 嵌套现有的网站页面也是一种不错的方式&#xff0c;。目前我就职的项目组就有多个业务利用 iframe 完成业务的复用…

【数据结构】堆的模拟实现

前言:前面我们学习了顺序表、单链表、栈、队列&#xff0c;今天我们就开始新的学习吧&#xff0c;今天我们将进入堆的学习&#xff01;(最近博主处于低谷期)一起加油吧各位。 &#x1f496; 博主CSDN主页:卫卫卫的个人主页 &#x1f49e; &#x1f449; 专栏分类:数据结构 &…

在AWS EC2中部署和使用Apache Superset的方案

大纲 1 Superset部署1.1 启动AWS EC21.2 下载Superset Docker文件1.3 修改Dockerfile1.4 配置管理员1.5 结果展示1.6 检查数据库驱动1.7 常见错误处理 2 Glue&#xff08;可选参考&#xff09;3 IAM与安全组3.1 使用AWS Athena3.2 使用AWS RedShift或AWS RDS3.2.1 查看AWS Reds…