python数据结构与算法-13_高级排序算法-快速排序

快速排序

快速排序名字可不是盖的,很多程序语言标准库实现的内置排序都有它的身影,我们就直奔主题吧。
和归并排序一样,快排也是一种分而治之(divide and conquer)的策略。归并排序把数组递归成只有单个元素的数组,之后再不断两两
合并,最后得到一个有序数组。这里的递归基本条件就是只包含一个元素的数组,当数组只包含一个元素的时候,我们可以认为它本来就是有序的(当然空数组也不用排序)。

快排的工作过程其实比较简单,三步走:

  • 选择基准值 pivot 将数组分成两个子数组:小于基准值的元素和大于基准值的元素。这个过程称之为 partition

  • 对这两个子数组进行快速排序。

  • 合并结果

在这里插入图片描述

根据这个想法我们可以快速写出快排的代码,简直就是在翻译上边的描述:

def quicksort(array):size = len(array)if not array or size < 2:  # NOTE: 递归出口,空数组或者只有一个元素的数组都是有序的return arraypivot_idx = 0pivot = array[pivot_idx]less_part = [array[i] for i in range(size) if array[i] <= pivot and pivot_idx != i]great_part = [array[i] for i in range(size) if array[i] > pivot and pivot_idx != i]return quicksort(less_part) + [pivot] + quicksort(great_part)def test_quicksort():import randomseq = list(range(10))random.shuffle(seq)assert quicksort(seq) == sorted(seq)

是不是很简单,下次面试官让你手写快排你再写不出来就有点不太合适啦。 当然这个实现有两个不好的地方:

  • 第一是它需要额外的存储空间,我们想实现 inplace 原地排序。
  • 第二是它的 partition 操作每次都要两次遍历整个数组,我们想改善一下。

这里我们就来优化一下它,实现 inplace 排序并且改善下 partition 操作。新的代码大概如下:

def quicksort_inplace(array, beg, end):    # 注意这里我们都用左闭右开区间,end 传入 len(array)if beg < end:    # beg == end 的时候递归出口pivot = partition(array, beg, end)quicksort_inplace(array, beg, pivot)quicksort_inplace(array, pivot+1, end)

能否实现只遍历一次数组就可以完成 partition 操作呢?实际上是可以的。我们设置首位俩个指针 left, right,两个指针不断向中间收拢。如果遇到 left 位置的元素大于 pivot 并且 right 指向的元素小于 pivot,我们就交换这俩元素,当 left > right 的时候退出就行了,这样实现了一次遍历就完成了 partition。如果你感觉懵逼,纸上画画就立马明白了。我们来撸代码实现:
在这里插入图片描述

def partition(array, beg, end):pivot_index = begpivot = array[pivot_index]left = pivot_index + 1right = end - 1    # 开区间,最后一个元素位置是 end-1     [0, end-1] or [0: end),括号表示开区间while True:# 从左边找到比 pivot 大的while left <= right and array[left] < pivot:left += 1while right >= left and array[right] >= pivot:right -= 1if left > right:breakelse:array[left], array[right] = array[right], array[left]array[pivot_index], array[right] = array[right], array[pivot_index]return right   # 新的 pivot 位置def test_partition():l = [4, 1, 2, 8]assert partition(l, 0, len(l)) == 2l = [1, 2, 3, 4]assert partition(l, 0, len(l)) == 0l = [4, 3, 2, 1]assert partition(l, 0, len(l))

大功告成,新的快排就实现好了。

时间复杂度

在比较理想的情况下,比如数组每次都被 pivot 均分,我们可以得到递归式:

T(n) = 2T(n/2) + n

上一节我们讲过通过递归树得到它的时间复杂度是 O(nlog(n))。即便是很坏的情况,比如 pivot 每次都把数组按照 1:9 划分,依然是 O(nlog(n)),感兴趣请阅读算法导论相关章节。

在这里插入图片描述

思考题

  • 请你补充 quicksort_inplace 的单元测试
  • 最坏的情况下快排的时间复杂度是多少?什么时候会发生这种情况?
  • 我们实现的快排是稳定的啵?
  • 选择基准值如果选不好就可能导致复杂度升高,算导中提到了一种『median of 3』策略,就是说选择 pivot 的时候 从子数组中随机选三个元素,再取它的中位数,你能实现这个想法吗?这里我们的代码很简单地取了第一个元素作为 pivot
  • 利用快排中的 partition 操作,我们还能实现另一个算法,nth_element,快速查找一个无序数组中的第 n 大元素,请你尝试实现它并编写单测。其实这个函数是 C++ STL 中的一个函数。
  • 你知道 Python 内置的 sorted 如何实现的吗?请你 Google 相关资料了解下。很多内置的排序都使用了快排的改良版。

源码

# -*- coding: utf-8 -*-def quicksort(array):size = len(array)if not array or size < 2:  # NOTE: 递归出口,空数组或者只有一个元素的数组都是有序的return arraypivot_idx = 0pivot = array[pivot_idx]less_part = [array[i] for i in range(size) if array[i] <= pivot and pivot_idx != i]great_part = [array[i] for i in range(size) if array[i] > pivot and pivot_idx != i]return quicksort(less_part) + [pivot] + quicksort(great_part)def test_quicksort():import randomseq = list(range(10))random.shuffle(seq)assert quicksort(seq) == sorted(seq)    # 用内置的sorted 『对拍』def quicksort_inplace(array, beg, end):    # 注意这里我们都用左闭右开区间if beg < end:    # beg == end 的时候递归出口pivot = partition(array, beg, end)quicksort_inplace(array, beg, pivot)quicksort_inplace(array, pivot + 1, end)def partition(array, beg, end):"""对给定数组执行 partition 操作,返回新的 pivot 位置"""pivot_index = begpivot = array[pivot_index]left = pivot_index + 1right = end - 1    # 开区间,最后一个元素位置是 end-1     [0, end-1] or [0: end),括号表示开区间while True:# 从左边找到比 pivot 大的while left <= right and array[left] < pivot:left += 1while right >= left and array[right] >= pivot:right -= 1if left > right:breakelse:array[left], array[right] = array[right], array[left]array[pivot_index], array[right] = array[right], array[pivot_index]return right   # 新的 pivot 位置def test_partition():l = [4, 1, 2, 8]assert partition(l, 0, len(l)) == 2l = [1, 2, 3, 4]assert partition(l, 0, len(l)) == 0l = [4, 3, 2, 1]assert partition(l, 0, len(l)) == 3l = [1]assert partition(l, 0, len(l)) == 0l = [2,1]assert partition(l, 0, len(l)) == 1def test_quicksort_inplace():import randomseq = list(range(10))random.shuffle(seq)sorted_seq = sorted(seq)quicksort_inplace(seq, 0, len(seq))assert seq == sorted_seqdef nth_element(array, beg, end, nth):"""查找一个数组第 n 大元素"""if beg < end:pivot_idx = partition(array, beg, end)if pivot_idx == nth - 1:    # 数组小标从 0 开始return array[pivot_idx]elif pivot_idx > nth - 1:return nth_element(array, beg, pivot_idx, nth)else:return nth_element(array, pivot_idx + 1, end, nth)def test_nth_element():l1 = [3, 5, 4, 2, 1]assert nth_element(l1, 0, len(l1), 3) == 3assert nth_element(l1, 0, len(l1), 2) == 2l = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]for i in l:assert nth_element(l, 0, len(l), i) == ifor i in reversed(l):assert nth_element(l, 0, len(l), i) == iarray = [3, 2, 1, 5, 6, 4]assert nth_element(array, 0, len(array), 2) == 2array = [2,1]assert nth_element(array, 0, len(array), 1) == 1assert nth_element(array, 0, len(array), 2) == 2array = [3,3,3,3,3,3,3,3,3]assert nth_element(array, 0, len(array), 1) == 3if __name__ == '__main__':test_nth_element()

延伸阅读

  • 《算法导论》第 7 章
  • 《面试必备 | 排序算法的Python实现》

总结

面试经常问的就是常用排序算法的时间空间复杂,这里列一个表格方便记忆:

排序算法最差时间分析平均时间复杂度稳定度空间复杂度
冒泡排序O(n^2)O(n^2)稳定O(1)
选择排序O(n^2)O(n^2)不稳定O(1)
插入排序O(n^2)O(n^2)稳定O(1)
二叉树排序O(n^2)O(n*log2n)不一顶O(n)
快速排序O(n^2)O(n*log2n)不稳定O(log2n)~O(n)
堆排序O(n*log2n)O(n*log2n)不稳定O(1)

数据结构与算法-排序篇-Python描述

Leetcode

无序数组寻找第 k 大的数字,不止一种方法。
https://leetcode.com/problems/kth-largest-element-in-an-array/description/

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

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

相关文章

docker安装mysql挂着目录和mysql备份和恢复

第一&#xff0c;镜像拉取&#xff0c;运行镜像并挂载目录&#xff0c;尝试挂bin下&#xff0c;启动不了&#xff0c;不知为啥 docker run --privilegedtrue -itd --namevmysql -p 3306:3306 -v /home/vmysql:/home/vmysql -e MYSQL_ROOT_PASSWORD123456 mysql&#xff08;图…

Nancy (二)

最近做CS项目&#xff0c;一直在使用TCPSocket 做数据传输&#xff0c;不太爽&#xff0c;砸门可是多年BS的开发&#xff0c;这样开发接口出去比较费劲&#xff0c;但是又不想用asp.net mvc webapi,要按照IIS&#xff0c;有些工控机的系统环境也是很尴尬的&#xff0c;那么也可…

用好说 AI 玩转奥特曼表情包,居然还能和他们聊个天

你喜欢奥特曼吗&#xff1f;你相信光吗&#xff1f; 如果你已经追完了特摄剧、刷完了大电影、用滥了那几个表情包&#xff0c;那不如来试试用 AI 给自己整点活儿新 “物料”。 不管是和奥特曼 “面对面” 聊天还是 “无中生有” 表情包&#xff0c;AI 都能做&#xff01; (※…

spring boot 热部署

相信小伙伴们在日常的开发中&#xff0c;调试代码时&#xff0c;免不了经常修改代码&#xff0c;这个时候&#xff0c;为了验证效果&#xff0c;必须要重启 Spring Boot 应用。 频繁地重启应用&#xff0c;导致开发效率降低&#xff0c;加班随之而来。有没有什么办法&#xff0…

宏电股份受邀参加中国联通战新共创启航大会,共筑产业生态,链通数智未来

11月21日&#xff0c;由中国联通举办的主题为“共筑产业生态&#xff0c;链通数智未来”的网络安全现代产业链共链行动计划暨战新共创启航大会“5G工业互联网”专题供需对接会在北京顺利召开&#xff0c;宏电股份董事长左绍舟应邀出席活动。 会议现场&#xff0c;中国联通雁飞…

Rust开发——数据对象的内存布局

枚举与Sized 数据 一般数据类型的布局是其大小&#xff08;size&#xff09;、对齐方式&#xff08;align&#xff09;及其字段的相对偏移量。 1. 枚举&#xff08;Enum&#xff09;的布局&#xff1a; 枚举类型在内存中的布局通常是由编译器来确定的。不同的编译器可能有不…

centos7 系统keepalived 定时执行脚本

安装keepalived yum install -y keepalived 修改配置文件 配置文件路径 /etc/keepalived 配置文件内容 global_defs {router_id localhost.localdomain # 访问到主机&#xff0c;本机的hostname&#xff0c;需要修改 }vrrp_script chk_http_port {script "/etc/kee…

INFLOW:用于检测隐藏服务器的反向网络流水印

文章信息 论文题目&#xff1a;INFLOW: Inverse Network Flow Watermarking for Detecting Hidden Servers 期刊&#xff08;会议&#xff09;&#xff1a;IEEE INFOCOM 2018 - IEEE Conference on Computer Communications 级别&#xff1a;CCF A 文章链接&#xff1a;https:…

Docker 安装 Apache

目录 拉取官方 Apache 镜像 查看本地镜像 列出正在运行的容器 运行 Apache 容器 创建一个 HTML 文件&#xff1a;index.html 访问 Apache 拉取官方 Apache 镜像 查找 Docker Hub 上的 httpd 镜像。 可以通过 Tags 查看其他版本的 httpd&#xff0c;默认是最新版本 httpd…

SpringMVC 基础知识

学习目标 掌握基于 SpringMVC 获取请求参数与响应 json 数据操作熟练应用基于 REST 风格的请求路径设置与参数传递能够根据实际业务建立前后端开发通信协议并进行实现基于 SSM 整合技术开发任意业务模块功能 1 SpringMVC 简介 1.1 概述 1.1.1 web程序开发流程 【执行过程】…

从入门到精通!Python数据分析畅销书《利用Python进行数据分析》第三版中文版助你成为数据分析师!

Python数据分析畅销书《利用Python进行数据分析》第三版中文版助你成为数据分析师&#xff01; 个人简介什么是数据分析如何自学数据分析书籍推荐作译者简介作者简介译者简介 主要变动导读视频&#xff1a;购书链接&#xff1a;参与方式往期赠书回顾 个人简介 &#x1f3d8;️&…

【好玩的开源项目】Linux系统之部署proxx扫清黑洞小游戏

【好玩的开源项目】Linux系统之部署proxx扫清黑洞小游戏 一、proxx小游戏介绍1.1 proxx小游戏简介1.2 开源地址 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本 四、部署Node.js环境4.1 下载Node.js安装包4.…

动态规划求二维网格中从左上角到右下角的最短路径( 每次只能向下、向右、向右下走 ) java 实现

dp[i][j] 表示在以点(0,0)作为左上角&#xff0c;点(i,i) 作为右下角的二维网格中 左上角到右下角的最短路径&#xff0c; 动态转移方程为&#xff1a;dp[i][j] min{ dp[i][j-1],dp[i-1][j],dp[i-1][j-1] }.distance weight[i][j] ImageUtils.java&#xff1a; import java.a…

三十分钟学会Shell(上)

Shell ​ Shell 本身并不是内核的一部分&#xff0c;它只是站在内核的基础上编写的一个应用程序&#xff0c;是用户和Linux文件系统之间的桥梁。Shell 有自己的特殊性&#xff0c;就是开机立马启动&#xff0c;并呈现在用户面前&#xff1b;用户通过 Shell 来使用 Linux&#x…

Windows日常故障自我排查:用工具eventvwr.msc(事件查看器)分析问题故障

windows故障排查方法一&#xff1a; 工具用法 系统故障问题时&#xff0c;找不到解决方法 首先&#xff0c; 在搜索栏输入&#xff1a; 事件查看器(eventvwr.msc) 打开程序 根据程序找到程序运行的LOG 根据程序Operational筛选出错误日志&#xff1a; 日志中找错误原因&…

假期宅家不无聊,用极米投影仪H6 Pro 4K解锁宅家多样玩法

不知道大家平时的假期是怎么度过的&#xff0c;是选择出门游玩?还是要在家好好安排自己的学习充电计划?平日工作这么累&#xff0c;不如趁假期好好休息一下&#xff0c;把以前落下的番剧补回来。不过手机的屏幕太小&#xff0c;电脑又不能躺在床上看&#xff0c;只有够大尺寸…

Lora学习资料汇总

目录 LoRa联盟 Semtech lora网关供应商: LoRaMAC API文档 论坛 开发板 主流技术对比分析 LoRa网络距离模拟测试方法 LoRa应用 Lora LoraWAN教程 LoRa联盟 LoRa联盟&#xff1a;LoRaWAN规范的制定组织 https://www.lora-alliance.org/ LoRa技术白皮书&#xff1a;htt…

解决mv3版本浏览器插件,不能注入js脚本问题

文章目录 背景引入ifream解决ifream和父页面完全跨域问题参考链接 背景 浏览器插件升级mv3版本后&#xff0c;不能再使用content_script内容脚本向原浏览器&#xff08;top&#xff09;注入script标签达到注入脚本的目的。浏览器认为插入未经审核的脚本是不安全的行为。 引入…

ajax请求方式处理

1、前置准备 1.1、SpringBoot项目下&#xff1a;写一个controller RestController public class TestController {RequestMapping("/yyy")public void test(HttpServletRequest request, HttpServletResponse response){String yang request.getParameter("y…

AI助力钢铁产业数字化,python基于YOLOv5开发构建钢铁产业产品智能自动化检测识别系统

AI为工业产业智能化数字化赋能早已不是什么新鲜事&#xff0c;越来越多的行业和领域开始更大范围去拥抱AI&#xff0c;享受科技带来的变革力量&#xff0c;在我们之前的文章中也有很多相关领域项目的实践经历&#xff0c;本文的核心目标就是想要基于钢铁领域产品数据来开发构建…