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

一、快速排序——时间复杂度: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.mfbz.cn/a/239369.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

用23种设计模式打造一个cocos creator的游戏框架----(十四)观察者模式

1、模式标准 模式名称&#xff1a;观察者模式 模式分类&#xff1a;行为型 模式意图&#xff1a;定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于它的对象都得到通知并被自动更新。 结构图&#xff1a; 适用于&#xff1a;…

OFDM模糊函数仿真

文章目录 前言一、OFDM 信号及模糊函数1、OFDM 信号表达式2、模糊函数表达式 二、MATLAB 仿真1、MATLAB 核心源码2、仿真结果①、OFDM 模糊函数②、OFDM 距离模糊函数③、OFDM 速度模糊函数 前言 本文进行 OFDM 的仿真&#xff0c;首先看一下 OFDM 的模糊函数仿真效果&#xf…

SpringBoot程序的打包与运行

&#x1f648;作者简介&#xff1a;练习时长两年半的Java up主 &#x1f649;个人主页&#xff1a;程序员老茶 &#x1f64a; ps:点赞&#x1f44d;是免费的&#xff0c;却可以让写博客的作者开心好久好久&#x1f60e; &#x1f4da;系列专栏&#xff1a;Java全栈&#xff0c;…

【C语言】数据结构——小堆实例探究

&#x1f497;个人主页&#x1f497; ⭐个人专栏——数据结构学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 导读&#xff1a; 我们在前面学习了单链表和顺序表&#xff0c;以及栈和队列。 今天我们来学习小堆。 关注博主或是订阅专栏&a…

【初阶C++】前言

C前言 1. 什么是C2. C发展史3. C的重要性4. 如何学习C 1. 什么是C C语言是结构化和模块化的语言&#xff0c;适合处理较小规模的程序。对于复杂的问题&#xff0c;规模较大的程序&#xff0c;需要高度的抽象和建模时&#xff0c;C语言则不合适。为了解决软件危机&#xff0c; …

“蒙企通”线上平台升级 助力内蒙古自治区民营经济发展

为进一步落实《中共中央、国务院关于促进民营经济发展壮大的意见》和内蒙古自治区党委、政府《关于进一步支持民营经济高质量发展的若干措施》&#xff0c;内蒙古自治区发展改革委联合自治区工商联共同开展“自治区促进民营经济发展项目”&#xff0c;为民营经营主体拓展市场、…

Python编程技巧 – 使用组合运算符

Python编程技巧 – 使用组合运算符 Python Programming Skills – Using Combined Operators Python通过赋值过程&#xff0c;将声明变量与赋值和而为之&#xff0c;可谓讲求效率。此外&#xff0c;在Python赋值运算符里&#xff0c;也有一个强大高效的功能&#xff0c;即复合…

小傅哥星球项目拆解,如何设计复杂的抽奖流程

作者&#xff1a;小傅哥 博客&#xff1a;https://bugstack.cn 沉淀、分享、成长&#xff0c;让自己和他人都能有所收获&#xff01;&#x1f604; 大家好&#xff0c;我是技术UP主小傅哥。 我想做一个抽奖&#xff0c;但想让用户合理的别中奖。现在各个互联网产品场景中&…

算法 - 二分搜索法 / 二分法(704)

原理&#xff1a; 利用数组的有序性&#xff0c;每次取查找范围的中间点&#xff0c;缩窄一半的查找空间。比较中间值和目标值的大小&#xff0c;直到找到目标值或者查找区间为空时返回。 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目&…

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

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

2. 如何通过公网IP端口映射访问到设备的vmware虚拟机的ubuntu服务器

文章目录 1. 主机设备是Windows 11系统2. 安装vmware虚拟机3. 创建ubuntu虚拟机&#xff08;据说CentOS 7 明年就不维护了&#xff0c;就不用这个版本的linux了&#xff09;4. 安装nginx服务:默认端口805. 安装ssh服务:默认端口226. 设置主机 -> ubuntu的端口映射7. 设置路由…

ReentrantLock详解

介绍 它是可重入锁的互斥锁&#xff0c;又被称为“独占锁”。它在同一时间点只能被一个线程锁持有&#xff1b;可重入表示&#xff0c;ReentrantLock锁可被同一个线程多次获取。它是通过一个FIFO的等待队列来管理获取该锁所有线程的。在“公平锁”的机制下&#xff0c;线程依次…

MySQL笔记-第08章_聚合函数

视频链接&#xff1a;【MySQL数据库入门到大牛&#xff0c;mysql安装到优化&#xff0c;百科全书级&#xff0c;全网天花板】 文章目录 第08章_聚合函数1. 聚合函数介绍1.1 AVG和SUM函数1.2 MIN和MAX函数1.3 COUNT函数 2. GROUP BY2.1 基本使用2.2 使用多个列分组2.3 GROUP BY中…

理解排序算法:冒泡排序、选择排序与归并排序

简介&#xff1a; 在计算机科学中&#xff0c;排序算法是基础且重要的概念。本文将介绍三种常见的排序方法&#xff1a;冒泡排序、选择排序和归并排序。我们将探讨它们的工作原理、特点和适用场景&#xff0c;以帮助读者更好地理解和选择合适的排序方法。 冒泡排序 冒泡排序是…

cs环境部署

配置搭建cs工具 两种方式 cs工具 》狐狸工具箱,微信上搜索 或者cs - OneDrive (sharepoint.com)提取密码www.ddosi.org 需要云服务器&#xff08;个人猜测如果是靶场的话&#xff0c;可以采用一台所有主机都能访问的主机作为服务端配置&#xff09; 非docker方式搭建 将c…

ue5材质预览界面ue 变黑

发现在5.2和5.1上都有这个bug 原因是开了ray tracing引起的&#xff0c;这个bug真是长时间存在&#xff0c;类似的bug还包括草地上奇怪的影子和地形上的影子等等 解决方法也很简单&#xff0c;就是关闭光追&#xff08;不是…… 就是关闭预览&#xff0c;在材质界面preview sc…

10基于matlab的悬臂梁四节点/八节点四边形单元有限元编程(平面单元)

悬臂梁&#xff0c;有限元编程。基于matlab的悬臂梁四节点/八节点四边形单元有限元编程&#xff08;平面单元&#xff09;&#xff0c;程序有详细注解&#xff0c;可根据需要更改参数&#xff0c;包括长度、截面宽度和高度、密度、泊松比、均布力、集中力、单元数量等。需要就拍…

【算法】递归、搜索与回溯算法

文章目录 一. 名词解释1. 递归1.1 什么是递归&#xff1f;1.2 为什么会用到递归&#xff1f;1.3 如何理解递归&#xff1f;1.4 如何写好一个递归&#xff1f; 2. 遍历和搜索3. 回溯和剪枝 二. 递归系列专题1. 汉诺塔问题2. 合并两个有序链表3. 反转链表4. 两两交换链表中的节点…

关于Anaconda的安装和环境部署(此章专为新手制定)

目录 Anaconda简介 一、软件下载&#xff08;地址&#x1f447;&#xff09; 2&#xff1a;点击下载 3&#xff1a;版本选择&#xff1a; 4&#xff1a;Anaconda的安装包就下载完成了 2&#xff1a;恭喜你&#xff0c;看到这里已经完成安装了 三、部署环境 1&#xff1…

什么是 AWS IAM?如何使用 IAM 数据库身份验证连接到 Amazon RDS(上)

驾驭云服务的安全环境可能很复杂&#xff0c;但 AWS IAM 为安全访问管理提供了强大的框架。在本文中&#xff0c;我们将探讨什么是 AWS Identity and Access Management (IAM) 以及它如何增强安全性。我们还将提供有关使用 IAM 连接到 Amazon Relational Database Service (RDS…