新闻中心
Python快速排序实现:常见错误与正确分区逻辑解析

本文深入探讨了python快速排序算法的正确实现,重点分析了在分区(partitioning)过程中常见的逻辑错误,如交换操作的缩进问题、支点(pivot)的最终位置确定以及递归调用边界的设置。通过详细的代码示例和修正,旨在帮助开发者理解并构建一个健壮、高效的快速排序算法。
快速排序算法概述
快速排序(Quick Sort)是一种高效的、基于分治策略的排序算法。其核心思想是:
- 选择支点(Pivot):从待排序数组中选择一个元素作为“支点”(pivot)。
- 分区(Partition):将数组中所有小于支点的元素移到支点左边,所有大于支点的元素移到支点右边。这个操作完成后,支点就处于其最终的有序位置。
- 递归排序:对支点左右两边的子数组分别递归地执行快速排序。
当子数组只有一个元素或为空时,递归停止。
常见的快速排序实现陷阱与修正
在实现快速排序时,尤其是在分区逻辑部分,开发者常常会遇到一些细微但关键的错误,导致排序结果不正确。下面我们将结合一个常见的错误示例,分析并给出正确的实现。
原始代码中的问题分析
考虑以下一个尝试实现快速排序的Python代码片段:
class QuickSort:
def quickSort(self, list, low, high):
if (low >= high):
return
else:
leftPointer = low
rightPointer = high
pivot = list[high]
while (leftPointer < rightPointer):
while (leftPointer < rightPointer and list[leftPointer] < pivot):
leftPointer += 1
while (leftPointer < rightPointer and list[rightPointer] > pivot):
rightPointer -= 1
list[leftPointer], list[rightPointer] = list[rightPointer], list[leftPointer] # 问题1:缩进错误
list[high], list[leftPointer] = list[leftPointer], list[high] # 问题2:支点放置逻辑错误
self.quickSort(list, low, rightPointer - 1) # 问题3:递归边界错误
self.quickSort(list, rightPointer + 1, high) # 问题3:递归边界错误
return list这段代码存在以下几个主要问题:
Pinokio
Pinokio是一款开源的AI浏览器,可以安装运行各种AI模型和应用
232
查看详情
- 交换操作的缩进错误:list[leftPointer], list[rightPointer] = list[rightPointer], list[leftPointer] 这行代码的缩进不正确,它应该在 while (leftPointer
- 支点放置逻辑错误:list[high], list[leftPointer] = list[leftPointer], list[high] 这行旨在将支点放到其最终位置。然而,当 leftPointer 最终指向的元素小于支点时,这个交换会导致支点被错误放置。正确的逻辑需要确保支点最终位于所有小于它的元素之后、所有大于它的元素之前。
- 递归调用边界错误:在分区完成后,rightPointer 并不总是支点最终的位置。正确的递归调用应该以支点最终的位置为界限,将数组分成两个子部分。在正确的实现中,leftPointer 最终会指向支点的新位置。
正确的快速排序实现
为了解决上述问题,我们需要对分区逻辑和支点放置策略进行调整。下面是修正后的Python快速排序实现:
class QuickSort:
def quickSort(self, input_list, low, high):
# 1. 基本情况:如果子数组只有一个元素或为空,则已排序
if low >= high:
return
# 优化:处理两个元素的子数组且已排序的情况
# 实际操作中,这个优化可以合并到low >= high的判断中,或者直接依赖后续逻辑
# if high - low == 1 and input_list[low] <= input_list[high]:
# return
# 2. 选择支点:这里选择最后一个元素作为支点
pivot = input_list[high]
# 初始化左右指针
# leftPointer从low开始寻找大于pivot的元素
# rightPointer从high-1开始寻找小于pivot的元素(不包括pivot本身)
leftPointer = low
rightPointer = high - 1
# 3. 分区过程
# 当leftPointer小于等于rightPointer时,继续进行分区
while leftPointer <= rightPointer:
# 找到第一个大于或等于pivot的元素
while leftPointer <= rightPointer and input_list[leftPointer] < pivot:
leftPointer += 1
# 找到第一个小于或等于pivot的元素
while leftPointer <= rightPointer and input_list[rightPointer] > pivot:
rightPointer -= 1
# 如果指针尚未交叉,则交换元素
if leftPointer <= rightPointer:
input_list[leftPointer], input_list[rightPointer] = input_list[rightPointer], input_list[leftPointer]
# 交换后,移动指针继续寻找
leftPointer += 1
rightPointer -= 1
# 4. 将支点放到其最终位置
# 循环结束后,leftPointer指向第一个大于等于pivot的元素的位置
# 将pivot(原位于high)与leftPointer指向的元素交换
# 这样,pivot就位于所有小于它的元素之后,所有大于它的元素之前
input_list[leftPointer], input_list[high] = input_list[high], input_list[leftPointer]
# 5. 递归排序左右子数组
# 支点现在位于leftPointer位置,所以递归范围是(low, leftPointer-1) 和 (leftPointer+1, high)
self.quickSort(input_list, low, leftPointer - 1)
self.quickSort(input_list, leftPointer + 1, high)
return input_list
修正后的关键点解释:
- 支点选择与指针初始化:我们仍然选择 input_list[high] 作为支点。leftPointer 从 low 开始,rightPointer 从 high - 1 开始,这是因为 high 位置是支点,不需要参与初始的比较交换。
- 分区循环 (while leftPointer :
- 内部的 while 循环分别寻找左侧第一个大于等于支点的元素和右侧第一个小于等于支点的元素。
- 当找到这样的两个元素且 leftPointer
- 交换后,leftPointer 和 rightPointer 都向前或向后移动一位,继续寻找。
- 支点最终放置:在主 while 循环结束后,leftPointer 会指向支点最终应该放置的位置(即所有小于支点的元素都在其左侧,所有大于支点的元素都在其右侧)。我们将原支点 (input_list[high]) 与 input_list[leftPointer] 进行交换,从而将支点放置到其最终的有序位置。
- 递归调用边界:由于支点已经放置在 leftPointer 位置,并且它已经处于最终的有序状态,因此在递归调用时,我们将数组分为 (low, leftPointer - 1) 和 (leftPointer + 1, high) 两个子数组,不包括支点本身。
示例代码与测试
为了验证修正后的快速排序算法的正确性,我们可以使用不同的测试用例:
# 实例化快速排序类
quick = QuickSort()
# 测试用例1:普通乱序数组
list1 = [50, 49, 19, 4, 9]
print(f"原始数组: {list1}, 排序后: {quick.quickSort(list1, 0, len(list1) - 1)}") # 预期: [4, 9, 19, 49, 50]
# 测试用例2:已排序数组
list2 = [1, 2, 3, 4, 5]
print(f"原始数组: {list2}, 排序后: {quick.quickSort(list2, 0, len(list2) - 1)}") # 预期: [1, 2, 3, 4, 5]
# 测试用例3:逆序数组
list3 = [4, 3, 2, 1]
print(f"原始数组: {list3}, 排序后: {quick.quickSort(list3, 0, len(list3) - 1)}") # 预期: [1, 2, 3, 4]
# 测试用例4:包含重复元素的数组
list4 = [8, 6, 7, 5, 3, 0, 9, 7, 1]
print(f"原始数组: {list4}, 排序后: {quick.quickSort(list4, 0, len(list4) - 1)}") # 预期: [0, 1, 3, 5, 6, 7, 7, 8, 9]
# 测试用例5:空数组或单元素数组
list5 = []
print(f"原始数组: {list5}, 排序后: {quick.quickSort(list5, 0, len(list5) - 1) if list5 else []}") # 预期: []
list6 = [42]
print(f"原始数组: {list6}, 排序后: {quick.quickSort(list6, 0, len(list6) - 1)}") # 预期: [42]注意事项与总结
- 支点选择:本教程中选择最后一个元素作为支点。其他策略包括选择第一个元素、中间元素或随机选择。不同的支点选择策略对算法的性能有影响,尤其是在处理特定输入(如已排序或逆序数组)时。
-
性能:快速排序在平均情况下的时间复杂度为 O(n log n),但在最坏情况下(例如,每次都选择最大或最小元素作为支点,且数组已排序或逆序)会退化到 O(n^2)。随机化支点选择可以有效降低遇到最坏情况的概率。

- 空间复杂度:快速排序是一种原地排序算法,其空间复杂度主要取决于递归栈的深度,平均为 O(log n),最坏情况下为 O(n)。
- 稳定性:快速排序是一种不稳定的排序算法,即相等元素的相对顺序在排序后可能会改变。
通过理解和掌握正确的快速排序分区逻辑,开发者可以避免常见的实现错误,构建出高效且可靠的排序解决方案。核心在于确保支点在每次分区后都能准确地放置在其最终位置,并且递归调用能够正确地处理左右子数组。
以上就是Python快速排序实现:常见错误与正确分区逻辑解析的详细内容,更多请关注其它相关文章!
# 只有一个
# 丽江营销推广培训招聘
# 金华外贸网站seo
# seo主要负责
# 嘉兴网站建设和推广公司
# 淮安自动化网站建设
# 共青城网站推广
# 成都建设网站首页
# 网站推广运营中心
# 服装店的营销推广
# 唇动营销推广渠道有哪些
# 重写
# python
# 自定义
# 情况下
# 都在
# 最坏
# 是在
# 是一种
# 第一个
# 递归
# 排序算法
# 栈
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
响应式CSS Grid布局:优化网格项在小屏幕下的堆叠与宽度适配
期待已久:小米17 Ultra、小米首款NAS本月登场
蛙漫2日版入口 WAMAN2(日版)无删减漫画官网链接
斑马英语APP如何开启夜间护眼阅读_斑马英语APP夜间模式与低蓝光设置教程
如何在低配置电脑上搭建轻量级J*a环境_占用更小的环境选择技巧
QQ邮箱官方邮箱登录入口 QQ邮箱网页版快速访问
Node.js CSV 数据处理:基于字段空值条件过滤整条记录的策略
漫蛙2在线漫画入口 漫蛙正版漫画网页版直达
Win11截图该按哪些键 Win11截屏完整流程解析【教程】
html网页设计源代码怎么运行_运行html网页设计源代码步骤【指南】
SteamMachine定价或为699美元 大家想入手吗?
Golang如何使用const iota_Go iota常量计数器讲解
C++如何操作注册表_Windows平台下C++读写注册表的API函数详解
谷歌浏览器最新官方入口链接 谷歌浏览器网页版官网导航
Go调试环境为何无法启动_Go调试器启动失败原因与解决策略
poki免费入口快捷访问 poki人气小游戏直接玩站点
如何将一个大型PHP应用拆分为多个Composer包_微服务与模块化架构的Composer实践
理解J*aScript Promise的微任务队列与执行顺序
汽水音乐网页版使用入口_汽水音乐电脑版播放指南
企业名称高精度匹配:N-gram方法在结构相似性分析中的应用
Win11怎么关闭触摸屏_Windows 11禁用HID符合标准触摸屏
深入理解Go语言中的指针类型:以*string为例
微博网页版官方账号登录 微博网页版内容浏览使用指南
抖音极速版最新版本 抖音极速版官方下载地址
处理Kafka消费者会话超时:深入理解消息处理语义与幂等性
12306选座系统怎么选连座_12306选座多人连坐操作方法
Lar*el如何生成PDF或Excel文件_Lar*el文档导出工具与使用教程
HuggingFaceEmbeddings中向量嵌入维度调整的限制与理解
c++项目目录结构应该如何组织_c++工程化项目结构规范
c++如何实现单例设计模式_c++线程安全的单例模式写法
126邮箱网页版官方入口 126邮箱账号在线登录平台
QQ网页版官方账号入口 QQ网页版网页版登录指南
J*aScript中高效管理与清空动态列表:避免循环陷阱
深入理解字体排版:Adobe光学字偶距与CSS字偶距的差异与实现
cad如何更改注释性对象的比例_cad注释性比例调整方法
AO3网页版合集入口 Archive of Our Own同人作品浏览指南
C++的std::forward_list怎么用_C++ STL中单向链表容器的特点与应用
蛙漫正版漫画平台入口_蛙漫免费阅读全站漫画资源
抖音网页版企业服务中心登录入口_抖音网页版企业登录平台
css滚动区域卡顿如何改善_css滚动问题用will-change优化渲染
Python类型检查:优化关联可选属性的Mypy推断策略
Angular Material 垂直步进器:实现底部到顶部排序的教程
微信网页版官方快速登录入口 微信网页版网页版账号直达
抖音创作助手登录入口_抖音创作辅助工具官网直达
AO3官网镜像链接 Archive of Our Own同人文在线浏览
Golang如何实现简单的Web表单_Golang表单提交与验证处理方法
Yandex免登录网页版地址 Yandex搜索引擎官方访问入口
UC浏览器如何安装插件 UC浏览器添加扩展程序详细教程【进阶】
如何在J*a中实现统一对象行为接口_项目大型化时的接口规范化
妖精动漫免费平台 妖精动漫官网资源观看网址


2025-10-29
浏览次数:次
返回列表