Python - 深夜数据结构与算法之 Sort

目录

一.引言

二.排序简介

1.排序类型

2.时间复杂度

3.初级排序 

4.高级排序

A.快速排序

B.归并排序

C.堆排序

5.特殊排序

三.经典算法实战

1.Quick-Sort 

2.Merge-Sort

3.Heap-Sort

4.Relative-Sort-Array [1122]

5.Valid-anagram [242]

6.Merge-Intervals [56]

7.Reverse-Pairs-V1 [LCR 170]

8.Reverse-Pairs-V2 [493]

四.总结


一.引言

排序在日常开发中是最常见的应用之一,常用的有快速排序、冒泡排序、桶排序、归并排序、合并排序等等,下面我们介绍下常用的排序算法与原理。

二.排序简介

1.排序类型

前者可以参考 Java 的 Comparator,我们通过定义看来两个 Class 的比较方法实现元素两两之间的排序;而后者的非比较类排序主要适用于整形类数字,且往往需要额外的内存空间辅助,因此两种方法也是各有利弊。

2.时间复杂度

3.初级排序 

初级排序的平均时间复杂度都在 o(n^2),其排序都是不断缩小范围且比较次数较多。

4.高级排序

A.快速排序

B.归并排序

C.堆排序

5.特殊排序

三.经典算法实战

1.Quick-Sort 

class Solution:def quickSort(self, nums, start, end):if end <= start:returnpivot = self.partition(nums, start, end)self.quickSort(nums, start, pivot - 1)self.quickSort(nums, pivot + 1, end)def partition(self, nums, start, end):# pivot 标杆位置 counter 小于 pivot 的个数counter, pivot = start, endfor i in range(start, end):# 移动小于标杆的元素if nums[i] < nums[pivot]:nums[counter], nums[i] = nums[i], nums[counter]counter += 1# 移动标杆nums[pivot], nums[counter] = nums[counter], nums[pivot]return counterdef sort(self, nums):self.quickSort(nums, 0, len(nums) - 1)return numsif __name__ == '__main__':nums = [3, 7, 1, 5, 9, 2, 4]s = Solution()print(s.sort(nums))

通过二分的方法,每次对数组一分为 2,再到子数组一分为二继续拆分,如果使用 Python 也有更简洁的写法,不过这里推荐大家熟悉上面的双指针写法,对于理解更有帮助。

    def sortV2(self, nums):if len(nums) < 2:return numselse:pivot = nums[0]less = [i for i in nums[1:] if i <= pivot]rather = [i for i in nums[1:] if i > pivot]return self.sortV2(less) + [pivot] + self.sortV2(rather)

这种写法比较容易理解,但是空间复杂度会更高。

2.Merge-Sort

class Solution:def merge_sort(self, arr):if len(arr) <= 1:return arrmid = len(arr) // 2# 一分为二left = self.merge_sort(arr[:mid])right = self.merge_sort(arr[mid:])# 合并return self.merge(left, right)def merge(self, left, right):result = []i = j = 0# 双指针比大小while i < len(left) and j < len(right):if left[i] <= right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1# 追加数组result.extend(left[i:])result.extend(right[j:])return resultif __name__ == '__main__':nums = [3, 7, 1, 5, 9, 2, 4]s = Solution()print(s.merge_sort(nums))

这里也可以看出快排和归并的差距: 

3.Heap-Sort

import heapqclass Solution:def heap_sort(self, nums):heapq.heapify(nums)re = []for i in range(len(nums)):re.append(heapq.heappop(nums))return reif __name__ == '__main__':nums = [3, 7, 1, 5, 9, 2, 4]s = Solution()print(s.heap_sort(nums))

直接调用 heapq 实现小顶堆,随后按顺序 pop 获取最小值即可。

4.Relative-Sort-Array [1122]

数组相对排序: https://leetcode.cn/problems/relative-sort-array

◆ 题目分析

最常规的方法就是把 arr2 的元素重新构建 num2index,按照他们的索引对 arr1 的元素排序,最后再用索引反映射回来即可,即 zipWithIndex,用 index 排序,再 indexWithNum,把 index 转换回来。

◆ 传统实现

class Solution(object):def relativeSortArray(self, arr1, arr2):""":type arr1: List[int]:type arr2: List[int]:rtype: List[int]"""# 构建 i-> num 和 num -> i 的映射m = {}change = {}for i in range(len(arr2)):m[arr2[i]] = ichange[i] = arr2[i]# 补充排序add = []wait = []# 区分待排序与异常排序for i in arr1:if i in m:wait.append(m[i])else:add.append(i)wait.sort()add.sort()# 根据映射返回return [change[i] for i in wait] + add

比较好理解,但是时间、空间复杂度都比较高。 

◆ 计数排序

class Solution(object):def relativeSortArray(self, arr1, arr2):""":type arr1: List[int]:type arr2: List[int]:rtype: List[int]"""# 统计 arr1 中元素的频次count = [0] * 1001for num in arr1:count[num] += 1result = []# 遍历arr2,将arr2中的元素按照在arr1中的频次添加到结果中for num in arr2:result.extend([num] * count[num])count[num] = 0# 遍历count数组,将未在arr2中出现过的元素按照升序添加到结果中for i in range(len(count)):result.extend([i] * count[i])return result

通过计数排序的思想,我们对 arr1 的所有元素进行词频统计,随后根据 arr2 的顺序获取元素的 count 添加至 results,这样就保证了按照 arr2 的顺序,而剩下的数组则默认有序,把 count 拿出来 extend 即可。 

5.Valid-anagram [242]

有效异位词: https://leetcode.cn/problems/valid-anagram/

◆ 题目分析

统计每个字符的数量,如果完全一致则为异位词。也可以字符串排序再比较。

◆ 字符统计

class Solution(object):def isAnagram(self, s, t):""":type s: str:type t: str:rtype: bool"""if len(s) != len(t):return False# 统计字符数量是否相等word_count = [0] * 26for char in s:word_count[ord(char) - ord('a')] += 1for char in t:word_count[ord(char) - ord('a')] -= 1# 判断 0for count in word_count:if count != 0:return Falsereturn True

◆ 字符排序

class Solution(object):def isAnagram(self, s, t):""":type s: str:type t: str:rtype: bool"""if len(s) != len(t):return Falses = list(s)s.sort()t = list(t)t.sort()for i in range(len(s)):if s[i] != t[i]:return Falsereturn True

直接字符串排序即可。 

6.Merge-Intervals [56]

合并区间: https://leetcode.cn/problems/merge-intervals/description/ 

◆ 题目分析

按照 interval 的 start 进行排序,随后顺序比较并插入。

◆ 数字排序

class Solution(object):def merge(self, intervals):""":type intervals: List[List[int]]:rtype: List[List[int]]"""intervals.sort()re = []for i in intervals:# 数组为空或者if not re or re[-1][1] < i[0]:re.append(i)else:re[-1][1] = max(re[-1][1], i[1])return re

7.Reverse-Pairs-V1 [LCR 170]

交易逆序对: https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof/description/

◆ 题目分析

套用归并排序的模版,在归并的过程中记录逆序的情况总数。

◆ 归并排序

class Solution:def mergeSort(self, record, tmp, l, r):if l >= r:return 0mid = (l + r) // 2# 拆分数组inv_count = self.mergeSort(record, tmp, l, mid) + self.mergeSort(record, tmp, mid + 1, r)# 左端 l-mid 右端 mid+1 起始位置 li, j, pos = l, mid + 1, lwhile i <= mid and j <= r:if record[i] <= record[j]:tmp[pos] = record[i]i += 1# 右边有多少的元素比当前的 nums[i] 小,逆序对就加几inv_count += (j - (mid + 1))else:tmp[pos] = record[j]j += 1# 赋值位置 +1pos += 1for k in range(i, mid + 1):tmp[pos] = record[k]inv_count += (j - (mid + 1))pos += 1for k in range(j, r + 1):tmp[pos] = record[k]pos += 1record[l:r + 1] = tmp[l:r + 1]return inv_countdef reversePairs(self, record):n = len(record)tmp = [0] * nreturn self.mergeSort(record, tmp, 0, n - 1)

8.Reverse-Pairs-V2 [493]

翻转对: https://leetcode.cn/problems/reverse-pairs/description/

◆ 题目分析

和上面的题目类似,但是需要注意条件增强,需要满足 2 倍的关系。

◆ 归并排序

class Solution:def reversePairs(self, arr):self.count = 0self.merge_sort(arr)return self.countdef merge_sort(self, arr):if len(arr) <= 1:return arrmid = len(arr) // 2# 一分为二left = self.merge_sort(arr[:mid])right = self.merge_sort(arr[mid:])# 合并return self.merge(left, right)def merge(self, left, right):result = []# 计数i = j = 0while i < len(left) and j < len(right):if left[i] <= 2 * right[j]:self.count += ji += 1else:j += 1self.count += (len(left) - i) * j# 双指针比大小i = j = 0while i < len(left) and j < len(right):if left[i] <= right[j]:result.append(left[i])i += 1else:result.append(right[j])j += 1# 追加数组result.extend(left[i:])result.extend(right[j:])return result

类似的逆序题目我们都可以套用 Merge Sort 的模版,注意不要忘了在 i/j 遍历完后,补充后面的尾巴,因为 i 或者 j 会率先到达自己的边界即 mid 或 right,所以还有一小撮修要我们收尾。

四.总结

上面整理了日常几种常用的排序算法,我们主要需要重复的是快速排序的指针版本以及归并排序的临时内存版本,这两个方法更考验基本功同时可以作为很多题目的扩展,所以一定要多多回溯练习。

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

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

相关文章

Java NIO (二)NIO Buffer类的重要方法(备份)

1 allocate()方法 在使用Buffer实例前&#xff0c;我们需要先获取Buffer子类的实例对象&#xff0c;并且分配内存空间。需要获取一个Buffer实例对象时&#xff0c;并不是使用子类的构造器来创建&#xff0c;而是调用子类的allocate()方法。 public class AllocateTest {static…

如何快速看懂一篇英文AI论文?

已经2024年了&#xff0c;该出现一个写论文解读AI Agent了。 大家肯定也在经常刷论文吧。 但真正尝试过用GPT去刷论文、写论文解读的小伙伴&#xff0c;一定深有体验——费劲。其他agents也没有能搞定的&#xff0c;今天我发现了一个超级厉害的写论文解读的agent &#xff0c…

某银行主机安全运营体系建设实践

随着商业银行业务的发展&#xff0c;主机规模持续增长&#xff0c;给安全团队运营工作带来极大挑战&#xff0c;传统的运营手段已经无法适应业务规模的快速发展&#xff0c;主要体现在主机资产数量多、类型复杂&#xff0c;安全团队难以对全量资产进行及时有效的梳理、管理&…

HCIA—— 16每日一讲:HTTP和HTTPS、无状态和cookie、持久连接和管线化、(初稿丢了,这是新稿,请宽恕我)

学习目标&#xff1a; HTTP和HTTPS、无状态和cookie、持久连接和管线化、HTTP的报文、URI和URL&#xff08;初稿丢了&#xff0c;这是新稿&#xff0c;请宽恕我&#x1f636;‍&#x1f32b;️&#xff09; 学习内容&#xff1a; HTTP无状态和cookieHTTPS持久连接和管线化 目…

vue2 pdfjs-2.8.335-dist pdf文件在线预览功能

1、首先先将 pdfjs-2.8.335-dist 文件夹从网上搜索下载&#xff0c;复制到public文件夹下. 2、在components下新建组件PdfViewer.vue文件 3、在el-upload 中调用 pdf-viewer 组件 4、在el-upload 中的 on-preview方法中加上对应的src路径 internalPreview(file) { //判断需要…

编译原理1.3习题 程序设计语言的发展历程

图源&#xff1a;文心一言 编译原理习题整理~&#x1f95d;&#x1f95d; 作为初学者的我&#xff0c;这些习题主要用于自我巩固。由于是自学&#xff0c;答案难免有误&#xff0c;非常欢迎各位小伙伴指正与讨论&#xff01;&#x1f44f;&#x1f4a1; 第1版&#xff1a;自…

IPv6隧道--GRE隧道

GRE隧道 通用路由封装协议GRE(Generic Routing Encapsulation)可以对某些网络层协议(如IPX、ATM、IPv6、AppleTalk等)的数据报文进行封装,使这些被封装的数据报文能够在另一个网络层协议(如IPv4)中传输。 GRE提供了将一种协议的报文封装在另一种协议报文中的机制,是一…

个人网站制作 Part 7 添加用户认证和数据库集成 | Web开发项目

文章目录 &#x1f469;‍&#x1f4bb; 基础Web开发练手项目系列&#xff1a;个人网站制作&#x1f680; 用户认证与数据库集成&#x1f528;添加用户认证&#x1f527;步骤 1: 使用Passport.js &#x1f528;集成数据库&#x1f527;步骤 2: 使用MongoDB和Mongoose &#x1f…

Grafana(二)Grafana 两种数据源图表展示(json-api与数据库)

一. 背景介绍 在先前的博客文章中&#xff0c;我们搭建了Grafana &#xff0c;它是一个开源的度量分析和可视化工具&#xff0c;可以通过将采集的数据分析、查询&#xff0c;然后进行可视化的展示&#xff0c;接下来我们重点介绍如何使用它来进行数据渲染图表展示 Docker安装G…

AIOps探索 | 基于大模型构建高效的运维知识及智能问答平台(2)

前面分享了平台对运维效率提升的重要性和挑战以及基于大模型的平台建设解决方案&#xff0c;新来的朋友点这里&#xff0c;一键回看精彩原文。 基于大模型构建高效的运维知识及智能问答平台&#xff08;1&#xff09;https://mp.csdn.net/mp_blog/creation/editor/135223109 …

【REMB 】翻译:草案remb-03

REMB REMB消息 以及 绝对时间戳选项 在带宽估计中的使用 :an absolute-value timestamp option for use in bandwidth estimatoin. 接收方带宽估计的RTCP消息 REMB 这位大神翻译的更好。 RTCP message for Receiver Estimated Maximum Bitrate draft-alvestrand-rmcat-remb-03…

iOS开发进阶(六):Xcode14 使用信号量造成线程优先级反转问题修复

文章目录 一、前言二、关于线程优先级反转三、优先级反转会造成什么后果四、怎么避免线程优先级反转五、使用信号量可能会造成线程优先级反转&#xff0c;且无法避免六、延伸阅读&#xff1a;iOS | Xcode中快速打开终端6.1 .sh绑定6.2 执行 pod install 脚本 七、延伸阅读&…

Android Activity的启动流程(Android-10)

前言 在Android开发中&#xff0c;我们经常会用到startActivity(Intent)方法&#xff0c;但是你知道startActivity(Intent)后Activity的启动流程吗&#xff1f;今天就专门讲一下最基础的startActivity(Intent)看一下Activity的启动流程&#xff0c;同时由于Launcher的启动后续…

STM32——DMA知识点及实战总结

1.DMA概念介绍 DMA&#xff0c;全称Direct Memory Access&#xff0c;即直接存储器访问。 DMA传输 将数据从一个地址空间复制到另一个地址空间。 注意&#xff1a;DMA传输无需CPU直接控制传输 2.DMA框图 3.DMA处理过程 外设的 8 个请求独立连接到每个通道&#xff0c;由 DMA_…

YOLOv5改进 | 融合改进篇 | 轻量化CCFM + SENetv2进行融合改进涨点 (全网独家首发)

一、本文介绍 本文给大家带来的改进机制是轻量化的Neck结构CCFM配合SENetv2改进的网络结构进行融合改进,其中CCFM为我本人根据RT-DETR模型一比一总结出来的,文中配其手撕结构图,其中SENetV2为网络结构重构化模块,通过其改进主干从而提取更有效的特征,这两个模块搭配在一起…

Java实现海南旅游景点推荐系统 JAVA+Vue+SpringBoot+MySQL

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 用户端2.2 管理员端 三、系统展示四、核心代码4.1 随机景点推荐4.2 景点评价4.3 协同推荐算法4.4 网站登录4.5 查询景点美食 五、免责说明 一、摘要 1.1 项目介绍 基于VueSpringBootMySQL的海南旅游推荐系统&#xff…

探索单元测试和 E2E 测试:提升软件质量的关键步骤(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

探索Redis特殊数据结构:Bitmaps(位图)在实际中的应用

一、概述 Redis官方提供了多种数据类型&#xff0c;除了常见的String、Hash、List、Set、zSet之外&#xff0c;还包括Stream、Geospatial、Bitmaps、Bitfields、Probabilistic&#xff08;HyperLogLog、Bloom filter、Cuckoo filter、t-digest、Top-K、Count-min sketch、Confi…

【机组】算术逻辑运算单元实验的解密与实战

​&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《机组 | 模块单元实验》⏰诗赋清音&#xff1a;云生高巅梦远游&#xff0c; 星光点缀碧海愁。 山川深邃情难晤&#xff0c; 剑气凌云志自修。 ​ 目录 &#x1f33a; 一、 实验目的…

为您的网站添加网站底部美化代码 支持任意网站

将下面代码放在网站底部或者侧边栏&#xff0c;主要是cssjs的代码&#xff01;html格式&#xff01; 在逛 Gitee、Github 等各大代码仓库时&#xff0c;往往能看到以下这种徽章式的网页链接&#xff0c;美观好看又方便&#xff0c;那么这个徽章是如何制作出来的呢&#xff1f;…