【Python 算法零基础 4.排序 ⑥ 快速排序】

article/2025/6/25 0:14:53

既有锦绣前程可奔赴,亦有往日岁月可回首

                                                              —— 25.5.25

选择排序回顾

① 遍历数组从索引 0 到 n-1n 为数组长度)。

② 每轮确定最小值假设当前索引 i 为最小值索引 min_index。从 i+1 到 n-1 遍历,若找到更小元素,则更新 min_index

③ 交换元素若 min_index ≠ i,则交换 arr[i] 与 arr[min_index]

'''
① 遍历数组:从索引 0 到 n-1(n 为数组长度)。② 每轮确定最小值:假设当前索引 i 为最小值索引 min_index。从 i+1 到 n-1 遍历,若找到更小元素,则更新 min_index。③ 交换元素:若 min_index ≠ i,则交换 arr[i] 与 arr[min_index]。
'''def selectionSort(arr: List[int]):n = len(arr)for i in range(n):min_index = ifor j in range(i+1, n):if arr[j] < arr[min_index]:min_index = jif min_index != i:arr[i], arr[min_index] = arr[min_index], arr[i]return arr

 冒泡排序回顾

① 初始化设数组长度为 n

② 外层循环遍历 i 从 0 到 n-1(共 n 轮)。

③ 内层循环对于每轮 i,遍历 j 从 0 到 n-i-2

④ 比较与交换若 arr[j] > arr[j+1],则交换两者。

⑤ 结束条件重复步骤 2-4,直到所有轮次完成。

'''
① 初始化:设数组长度为 n。② 外层循环:遍历 i 从 0 到 n-1(共 n 轮)。③ 内层循环:对于每轮 i,遍历 j 从 0 到 n-i-1。④ 比较与交换:若 arr[j] > arr[j+1],则交换两者。⑤ 结束条件:重复步骤 2-4,直到所有轮次完成。
'''
def bubbleSort(arr: List[int]):n = len(arr)for i in range(n):for j in range(n-i-1):if arr[j] > arr[j+1]:arr[j], arr[j+1] = arr[j+1], arr[j]return arr

插入排序回顾

① 遍历未排序元素从索引 1 到 n-1

② 保存当前元素:将 arr[i] 存入 current

③ 元素后移从已排序部分的末尾(索引 j = i-1)向前扫描,将比 current 大的元素后移。直到找到第一个不大于 current 的位置或扫描完所有元素。

④ 插入元素将 current 放入 j+1 位置。

'''
① 遍历未排序元素:从索引 1 到 n-1。② 保存当前元素:将 arr[i] 存入 current。③ 元素后移:从已排序部分的末尾(索引 j = i-1)向前扫描,将比 current 大的元素后移。直到找到第一个不大于 current 的位置或扫描完所有元素。④ 插入元素:将 current 放入 j+1 位置。
'''
def insertSort(arr: List[int]):n = len(arr)for i in range(n):current = arr[i]j = i - 1while current < arr[j] and j >0:arr[j+1] = arr[j]j -= 1arr[j + 1] = currentreturn arr

计数排序回顾

① 初始化设数组长度为 n,元素最大值为 r。创建长度为 r+1 的计数数组 count,初始值全为 0。

② 统计元素频率遍历原数组 arr,对每个元素 x,将 count[x] 加 1。

③ 重构有序数组初始化索引 index = 0。遍历计数数组 count,索引 v 从 0 到 r,若 count[v] > 0,则将 v 填入原数组 arr[index],并将 index 加 1。count[v] - 1,重复此步骤直到 count[v] 为 0。

④ 结束条件当计数数组遍历完成时,排序结束。

'''
输入全为非负整数,且所有元素 ≤ r① 初始化:设数组长度为 n,元素最大值为 r。创建长度为 r+1 的计数数组 count,初始值全为 0。② 统计元素频率:遍历原数组 arr,对每个元素 x,将 count[x] 加 1。③ 重构有序数组:初始化索引 index = 0。遍历计数数组 count,索引 v 从 0 到 r,
若 count[v] > 0,则将 v 填入原数组 arr[index],并将 index 加 1。
count[v] - 1,重复此步骤直到 count[v] 为 0。④ 结束条件:当计数数组遍历完成时,排序结束。
'''def countingSort(arr: List[int], r: int):# count = [0] * len(r + 1)count = [0 for i in range(r + 1)]for x in arr:count[x] += 1index = 0for v in range(r + 1):while count[v] > 0:arr[index] = vindex += 1count[v] -= 1return arr

归并排序回顾

Ⅰ、递归分解列表

① 终止条件:若链表为空或只有一个节点(head is None 或 head.next is None),直接返回头节点。

② 快慢指针找中点:初始化 slow 和 fast 指针,slow 指向头节点,fast 指向头节点的下一个节点。fast 每次移动两步,slow 每次移动一步。当 fast 到达末尾时,slow 恰好指向链表的中间节点。

③ 分割链表:将链表从中点断开,head2 指向 slow.next(后半部分的头节点)。将 slow.next 置为 None,切断前半部分与后半部分的连接。

④ 递归排序子链表:对前半部分(head)和后半部分(head2)分别递归调用 mergesort 函数。

Ⅱ、合并两个有序列表

① 创建虚拟头节点创建一个值为 -1 的虚拟节点 zero,用于简化边界处理。使用 current 指针指向 zero,用于构建合并后的链表。

② 比较并合并节点:遍历两个子链表 head1 和 head2,比较当前节点的值:若 head1.val <= head2.val,将 head1 接入合并链表,并移动 head1 指针。否则,将 head2 接入合并链表,并移动 head2 指针。每次接入节点后,移动 current 指针到新接入的节点。

③ 处理剩余节点:当其中一个子链表遍历完后,将另一个子链表的剩余部分直接接入合并链表的末尾。

④ 返回合并后的链表:虚拟节点 zero 的下一个节点即为合并后的有序链表的头节点。

'''
Ⅰ、递归分解列表① 终止条件:若链表为空或只有一个节点(head is None 或 head.next is None),直接返回头节点。② 快慢指针找中点:初始化 slow 和 fast 指针,slow 指向头节点,fast 指向头节点的下一个节点。fast 每次移动两步,slow 每次移动一步。当 fast 到达末尾时,slow 恰好指向链表的中间节点。③ 分割链表:将链表从中点断开,head2 指向 slow.next(后半部分的头节点)。将 slow.next 置为 None,切断前半部分与后半部分的连接。④ 递归排序子链表:对前半部分(head)和后半部分(head2)分别递归调用 mergesort 函数。Ⅱ、合并两个有序列表① 创建虚拟头节点:创建一个值为 -1 的虚拟节点 zero,用于简化边界处理。使用 current 指针指向 zero,用于构建合并后的链表。② 比较并合并节点:遍历两个子链表 head1 和 head2,比较当前节点的值:若 head1.val <= head2.val,将 head1 接入合并链表,并移动 head1 指针。否则,将 head2 接入合并链表,并移动 head2 指针。每次接入节点后,移动 current 指针到新接入的节点。③ 处理剩余节点:当其中一个子链表遍历完后,将另一个子链表的剩余部分直接接入合并链表的末尾。④ 返回合并后的链表:虚拟节点 zero 的下一个节点即为合并后的有序链表的头节点。
'''
def mergesort(self, head: ListNode):if head is None or head.next is None:return headslow, fast = head, head.nextwhile fast and fast.next:slow = slow.nextfast = fast.next.nexthead2 = slow.nextslow.next = Nonereturn self.merge(self.mergesort(head), self.mergesort(head2))def merge(self, head1: ListNode, head2: ListNode):zero = ListNode(-1)current = zerowhile head1 and head2:if head1.val <= head2.val:current.next = head1head1 = head1.nextelse:current.next = head2head2 = head2.nextcurrent = current.nextcurrent.next = head1 if head1 else head2return zero.next

一、引言

        快速排序(Quick Sort)是一种分而治之的排序算法。它通过选择一个基准元素,将数组分为两部分,一部分的元素都比基准小,另一部分的元素都比基准大,然后对这两部分再进行快速排序,最终得到有序的数组。


二、算法思想 

        ① 选择基准元素:从数组中随机选择一个元素作为基准。将基准元素放在数组的最左边。

        ② 分割数组:从头遍历数组,将比基准小的元素放在基准的左边,比基准大的元素放在基准的右边。

        ③ 递归排序:对基准左边和右边的子数组分别进行快速排序。

        ④ 重复步骤 ① 至 ③:直到子数组的长度为 1 或 0。

        首先随机选择了一个数字作为【基准数】,并且将它和最左边的数交换,然后依次遍历,小于这个数字的值存储在一起,大于这个数字的值存储在一起,遍历完毕后,将这个【基准数】和下标最大的那个比【基准数】小的数字交换位置,至此,这个 、基准数左边位置上的数都小于它,右边位置上的数字都大于它,左边与右边两部分数字继续递归求解即可


三、算法分析

1.时间复杂度

最优情况:当每次选择的基准元素正好将数组分成两等分时,快速排序的时间复杂度是 O(n*logn) 

最坏情况:当每次选择的基准元素是最大或最小元素时,快速排序的时间复杂度是 O(n^2)

平均情况:在平均情况下,快速排序的时间复杂度也是 O(n*logn)


2.空间复杂度

        快速排序的空间复杂度是 O(logn),因为在递归调用中需要使用栈来存储中间结果。这意味着在排序过程中,最多需要 O(logn) 的额外空间来保存递归调用的栈帧。


3.算法的优点

① 高效性:快速排序在大多数情况下具有较高的排序效率。

② 适应性:适用于各种数据类型的排序。

③ 原地排序:不需要额外的存储空间。


4.算法的缺点

① 最坏情况性能:在最坏情况下,快速排序的时间复杂度可能退化为 O(n^2)

② 不稳定性:快速排序可能会破坏排序的稳定性,即相同元素的相对顺序可能会改变


四、实战练习

217. 存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

示例 1:

输入:nums = [1,2,3,1]

输出:true

解释:

元素 1 在下标 0 和 3 出现。

示例 2:

输入:nums = [1,2,3,4]

输出:false

解释:

所有元素都不同。

示例 3:

输入:nums = [1,1,1,3,3,4,3,2,4,2]

输出:true

提示:

  • 1 <= nums.length <= 105
  • -109 <= nums[i] <= 109

思路与算法

Ⅰ、分区函数 Partition

① 随机选择基准元素:根据左右边界下标随机选择基准元素(选择的是元素并非下标),将基准元素赋值变量进行后续比较

② 交换基准元素:基准元素移动到最左边,将基准元素存储在变量中,

③ 分区操作:对于基准元素右边的元素,找到第一个小于基准元素的值,移动到最左边;对于基准元素左边的元素,找到第一个大于基准元素的值,移动到最右边

④ 返回基准元素的最终位置:循环执行完毕后,基准元素左边的值都小于它,基准元素右边的值都大于它

Ⅱ、递归排序函数

① 定义递归终止条件:左索引小于右索引时,结束递归

② 分区操作: 执行第一次分区操作,找到基准元素

③ 递归调用分区函数:将基准元素的左边、右边部分分别传入递归函数进行排序

Ⅲ、验证是否存在重复元素 

① 排序数组:将数组传入快速排序的实现中,返回排好序的数组

② 遍历数组,寻找是否存在重复元素:遍历数组,如果发现相邻两个元素相等,则返回True,否则遍历完数组后,返回False

class Solution:def Partition(self, arr: List, left, right):index = random.randint(left, right)arr[left], arr[index] = arr[index], arr[left]i = leftj = rightx = arr[i]while i < j:while i < j and arr[j] > x:j -= 1if i < j:arr[i], arr[j] = arr[j], arr[i]i += 1while i < j and arr[i] < x:i += 1if i < j:arr[i], arr[j] = arr[j], arr[i]j -= 1return idef quickSort(self, arr: List, left: int, right: int) -> List[int]:  if left >= right:returnnode = self.Partition(arr, left, right)self.quickSort(arr, left, node - 1)self.quickSort(arr, node + 1, right)def containsDuplicate(self, nums: List[int]) -> bool:n = len(nums)self.quickSort(nums, 0, n - 1)for i in range(1, n):if nums[i] == nums[i - 1]:return Truereturn False        


169. 多数元素

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

输入:nums = [3,2,3]
输出:3

示例 2:

输入:nums = [2,2,1,1,1,2,2]
输出:2

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。

思路与算法

Ⅰ、分区函数 Partition

① 随机选择基准元素:根据左右边界下标随机选择基准元素

② 交换基准元素:基准元素移动到最左边

③ 分区操作:对于基准元素右边的元素,找到第一个小于基准元素的值,移动到最左边;对于基准元素左边的元素,找到第一个大于基准元素的值,移动到最右边

④ 返回基准元素的最终位置:循环执行完毕后,基准元素左边的值都小于它,基准元素右边的值都大于它

Ⅱ、递归排序函数

① 定义递归终止条件:左索引小于右索引时,结束递归

② 分区操作: 执行第一次分区操作,找到基准元素

③ 递归调用分区函数:将基准元素的左边、右边部分分别传入递归函数进行排序

Ⅲ、在排好序的数组中返回多数元素

① 排序数组:将数组传入实现的快速排序函数中,返回已排序的数组 

② 返回多数元素:因为题目中保证一定存在多数元素,所以排序好的数组中,多数一定是多数元素,所以直接返回排序后数组中间位置的值即可

class Solution:def Partition(self, arr: List, left, right):index = random.randint(left, right)arr[left], arr[index] = arr[index], arr[left]i = leftj = rightx = arr[i]while i < j:while i < j and arr[j] > x:j -= 1if i < j:arr[i], arr[j] = arr[j], arr[i]i += 1while i < j and arr[i] < x:i += 1if i < j:arr[i], arr[j] = arr[j], arr[i]j -= 1return idef quickSort(self, arr: List, left: int, right: int) -> List[int]:  if left >= right:returnnode = self.Partition(arr, left, right)self.quickSort(arr, left, node - 1)self.quickSort(arr, node + 1, right)def majorityElement(self, nums: List[int]) -> int:n = len(nums)self.quickSort(nums, 0, n - 1)return nums[n//2]


http://www.hkcw.cn/article/RobrcmxpVQ.shtml

相关文章

Global Securities Markets 第7章知识点总结

一、银行中介的角色定位与核心作用 1. 角色定位&#xff1a;证券流转的核心枢纽 银行作为证券中介&#xff0c;在投资者与证券市场之间扮演三重角色&#xff1a; 资产托管者&#xff1a;负责证券实物或电子形式的安全保管&#xff08;如美国道富银行托管全球ETF资产&#xf…

docker B站学习

镜像是一个只读的模板&#xff0c;用来创建容器 容器是docker的运行实例&#xff0c;提供了独立可移植的环境 https://www.bilibili.com/video/BV11L411g7U1?spm_id_from333.788.videopod.episodes&vd_sourcee60c804914459274157197c4388a4d2f&p3 目录挂载 尚硅谷doc…

我爱学算法之—— 前缀和(上)

一、【模板】前缀和 题目解析 这道题&#xff0c;给定一个长度为n的数组&#xff0c;和m次询问&#xff1b; 每一次询问给出两个整数l和r&#xff0c;让我们求出区间[l , r]中所有数的和&#xff0c;然后输出。 算法思路 这道题暴力解法&#xff1a; 首先是m次查询&#xff0…

RK3568+LINUX + CODESYS带授权+实时系统,同时开自己的视觉应用

RK3568LINUX CODESYS带授权实时系统,同时开自己的视觉应用&#xff0c;让你即可以选择几个核心跑codesys&#xff0c;又可以选几个核心跑自定义的应用。 基于RK3568处理器构建的工业控制与视觉融合解决方案&#xff0c;可通过以下技术架构实现&#xff1a; 一、硬件平台核心配…

Boss直聘批量投简历工具使用教程

Boss直聘批量投简历工具使用教程 为什么boss有活跃度显示 但是不给我们筛选&#xff0c;推荐的十个七个都是十天半个月不在线的岗位hr 我投递了有意思吗 不就是在浪费我们的精力 同时也浪费了每天的投递次数 第二个问题 我想做一个批量投简历工具 或者&#xff08;自动投简历…

写个简单的浏览器插件,收藏网页内容

要求&#xff1a; 此时我觉得都ok了。请帮我写一篇文章来介绍这个项目。从最初的要求到最后实现的效果。要求篇幅不要太长&#xff0c;语言幽默有趣&#xff0c; 平易近人&#xff0c; 有吸引力。重点介绍的是起因&#xff0c;即&#xff0c;需求和起因增加篇幅&#xff0c;其…

Attention注意力机制

Attention核心思想 作用&#xff1a;处理时序问题 核心思想&#xff1a;处理序列数据时&#xff0c;网络应该更加关注输入中重要的部分&#xff0c;忽略不重要的部分。 要怎么做到&#xff1f; 通过学习不同部分的权重&#xff0c;将输入的序列中的重要部分显式加权&#xf…

CRC 原理概述

CRC 原理概述 摘要&#xff1a;循环冗余校验&#xff08;CRC, Cyclic Redundancy Check&#xff09;是一种基于多项式除法&#xff08;modulo-2&#xff09;的差错检测码。它将数据视为一个二进制多项式 D(x)&#xff0c;生成多项式为 G(x)&#xff0c;通过“除法”得到的余数 …

风控研发大数据学习路线

在如今信息爆炸时代&#xff0c;风控系统离不开大数据技术的支撑&#xff0c;大数据技术可以帮助风控系统跑的更快&#xff0c;算的更准。因此&#xff0c;风控技术研发需要掌握大数据相关技术。然而大数据技术栈内容庞大丰富&#xff0c;风控研发同学很可能会面临以下这些痛点…

美载有2.5亿只蜜蜂卡车翻车 养蜂专家紧急救援

美国华盛顿州近日发生了一起罕见事故,一辆载有约2.5亿只蜜蜂的半挂卡车在行驶途中翻覆,导致大量蜜蜂倾巢而出。事故地点靠近加拿大边境,距离温哥华仅约48公里。当地警方迅速封锁道路,并呼吁民众远离蜂群,同时紧急召集养蜂专家到场协助处理。怀特康郡警局发布公告称,事故发…

MySQL垂直分库(基于MyCat)

参考资料&#xff1a; 参考视频 参考博客 Mycat基本部署 视频参考资料&#xff1a;链接: https://pan.baidu.com/s/1xT_WokN_xlRv0h06b6F3yg 提取码: aag3 概要&#xff1a; 本文的垂直分库&#xff0c;全部是基于前文部署的基本架构进行的 垂直分库&#xff1a; 垂直分库…

MySQL 核心知识整理【一】

一、MySQL存储引擎对比&#xff1a;InnoDB vs MyISAM 在使用MySQL时&#xff0c;选择合适的存储引擎对性能影响很大。最常见的两个引擎是 InnoDB 和 MyISAM&#xff0c;它们各自的设计目标不同&#xff0c;适用场景也不一样。 事务与数据安全性方面&#xff0c;InnoDB 支持事…

MySQL下载安装配置环境变量

MySQL下载安装配置环境变量 文章目录 MySQL下载安装配置环境变量一、安装MySQL1.1 下载1.2 安装 二、查看MySQL服务是否启动三、配置环境变量四、验证 一、安装MySQL 1.1 下载 官网社区版&#xff08;免费版&#xff09;&#xff1a;https://dev.mysql.com/downloads/mysql/ …

火语言UI组件--文件夹对话框

【组件功能】&#xff1a;选择单个或多个文件的对话框。 样式预览 设置 基础设置 属性名称属性释义输入值类型标题(title)对话框的标题字符串类型默认路径(defaultPath)对话框的默认展示路径字符串类型多选(multiSelections)是否允许多选布尔型(true / false)显示隐藏文件(s…

海底三维可视化平台

1. 摘要 本文作者为视觉分析构建了一个真实海底的“虚拟世界”。在3D环境中导入底部轮廓。在该模型中&#xff0c;通过地震反射获得的海床地层剖面被数字化为离散点&#xff0c;并用克里金算法进行插值&#xff0c;以在每个地层中产生均匀的网格。然后在每一层构建 Delaunay三…

联合国裁员计划曝光 预算削减20%

多家媒体29日披露的联合国内部文件显示,联合国秘书处计划削减20%预算,并裁员6900人,约占员工总数的20%。根据法新社和路透社获取的联合国内部备忘录,负责财政事务的联合国助理秘书长钱德拉穆利拉马纳坦本周向各部门负责人发出信函,要求执行联合国秘书长安东尼奥古特雷斯提…

一村民鸡舍惊现50斤重蟒蛇 民警与捕蛇师傅联手救助

6月1日清晨,当小朋友们正享受儿童节的快乐时,五华县公安局丁畲派出所接到了村民温先生的紧急求助电话。温先生称自家鸡舍里钻进了一条大蛇,吓得鸡和鹅四处逃窜。接到报警后,丁畲派出所的值班民辅警迅速行动,并联系了专业的捕蛇师傅一同前往现场。到达后,他们发现大蛇蜷缩…

朋友圈哪些内容不能随便晒 保护隐私安全

端午假期即将来临,许多人计划出行并准备在朋友圈分享快乐。然而,过度分享可能带来隐私泄露的风险。有些内容不宜随意晒出。例如,火车票、飞机票、登机牌、家门钥匙、车牌等含有个人信息的物品,一旦发布,可能会被不法分子利用高科技手段窃取,导致个人隐私泄露。身份证、护…

火出圈的“苏超”不只是有梗 比赛第一,友谊第十四!

“友谊第一,比赛第二”这句话在江苏省首届城市足球联赛中被重新演绎为“比赛第一,友谊第十四”,这句口号迅速在网络上走红。这个被球迷称为“苏超”的足球联赛近日火出圈。“苏超”是江苏省体育局与各设区市政府联合主办的江苏省首届城市足球联赛,共有13个设区市派出队伍参…

电影《女足》杀青 星爷新作引期待

6月2日,周星驰执导的电影《女足》正式杀青。该片结合了少林功夫与足球元素,围绕女子足球队展开,讲述了一群热爱足球的女孩如何克服困难,追求梦想的故事。剧情紧凑,笑点与泪点并存,令人期待。全组历时3个月拍摄,迪丽热巴于6月2日完成个人戏份,其他主演如张小斐、张艺兴也…