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

news2024/10/5 16:25:29

目录

一.引言

二.排序简介

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:
            return
        pivot = 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, end

        for 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 counter

    def sort(self, nums):
        self.quickSort(nums, 0, len(nums) - 1)
        return nums


if __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 nums
        else:
            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 arr

        mid = 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 += 1
            else:
                result.append(right[j])
                j += 1
        
        # 追加数组
        result.extend(left[i:])
        result.extend(right[j:])

        return result


if __name__ == '__main__':
    nums = [3, 7, 1, 5, 9, 2, 4]
    s = Solution()
    print(s.merge_sort(nums))

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

3.Heap-Sort

import heapq

class Solution:

    def heap_sort(self, nums):
        heapq.heapify(nums)

        re = []
        for i in range(len(nums)):
            re.append(heapq.heappop(nums))

        return re

if __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]] = i
            change[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] * 1001
        for num in arr1:
            count[num] += 1
        
        result = []
        
        # 遍历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] * 26

        for char in s:
            word_count[ord(char) - ord('a')] += 1

        for char in t:
            word_count[ord(char) - ord('a')] -= 1

        # 判断 0
        for count in word_count:
            if count != 0:
                return False

        return True
            

◆ 字符排序

class Solution(object):
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        if len(s) != len(t):
            return False

        s = list(s)
        s.sort()

        t = list(t)
        t.sort()

        for i in range(len(s)):
            if s[i] != t[i]:
                return False
        
        return 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 0

        mid = (l + r) // 2
        # 拆分数组
        inv_count = self.mergeSort(record, tmp, l, mid) + self.mergeSort(record, tmp, mid + 1, r)
        # 左端 l-mid 右端 mid+1 起始位置 l
        i, j, pos = l, mid + 1, l
        while 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
            # 赋值位置 +1
            pos += 1

        for k in range(i, mid + 1):
            tmp[pos] = record[k]
            inv_count += (j - (mid + 1))
            pos += 1

        for k in range(j, r + 1):
            tmp[pos] = record[k]
            pos += 1

        record[l:r + 1] = tmp[l:r + 1]
        return inv_count

    def reversePairs(self, record):
        n = len(record)
        tmp = [0] * n
        return 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 = 0
        self.merge_sort(arr)
        return self.count

    def merge_sort(self, arr):
        if len(arr) <= 1:
            return arr

        mid = 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] <= 2 * right[j]:
                self.count += j
                i += 1
            else:
                j += 1

        self.count += (len(left) - i) * j

        # 双指针比大小
        i = j = 0
        while i < len(left) and j < len(right):
            if left[i] <= right[j]:
                result.append(left[i])
                i += 1
            else:
                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.coloradmin.cn/o/1391216.html

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

相关文章

Go新项目-配置文件的选取及区别和写法(1)

先说结论&#xff1a;我们选型TOML yaml&#xff0c;toml&#xff0c;json&#xff0c;ini 实际业务都有用 实际栗子是&#xff1a;我们想要把Go的切片作为配置文件&#xff0c;YAML写起来比较吃力&#xff0c;TOML就很容易了。 配置文件是用于配置计算机程序的参数、初始化设…

linux创建文件

创建文件夹&#xff1a; mkdir folder_name其中&#xff0c;folder_name是想要创建的文件夹的名称。 例如&#xff0c;如果想在当前目录下创建一个名为 "my_folder" 的文件夹&#xff0c;可以运行以下命令&#xff1a; mkdir my_folder如果想在特定路径下创建文件…

计算机导论10-软件与软件工程

文章目录 软件软件的概念软件的定义软件的特征 软件的保护与授权软件的法律保护软件许可 软件工程软件危机软件危机的概念产生软件危机的原因 软件工程的概念软件工程的定义软件工程基本原理软件工程框架软件工程三要素 软件工程方法学软件工程方法软件工程工具软件工程过程 软…

详细讲解Python中的aioschedule定时任务操作

目录 前言1. 基本概念2. 基本API3. Demo 前言 如果下面的函数库无法执行&#xff0c;出现类似&#xff1a;&#xff08;前提是python3.7以上&#xff09; AttributeError: module ‘asyncio‘ has no attribute ‘run‘请检查run是否可跳转&#xff0c;如果无法跳转&#xff…

【RPC】网络通信:哪种网络IO模型最适合RPC框架?

一、背景 RPC是解决进程间通信的一种方式。一次RPC调用&#xff0c;本质就是服务消费者与服务提供者间的一次网络信息交换的过程。服务调用者通过网络IO发送一条请求消息&#xff0c;服务提供者接收并解析&#xff0c;处理完相关的业务逻辑之后&#xff0c;再发送一条响应消息…

Qt命令行安装:linux(ubuntu)

起因是我上一篇文章说的&#xff0c;官网下的安装包卡死在第一步安装界面了。 于是我就问GPT有没有纯命令行的安装方式&#xff0c;果然是有的。 在Ubuntu上安装Qt可以使用以下命令&#xff1a; 1. 首先&#xff0c;添加Qt的官方存储库到系统中&#xff1a; sudo add-apt-rep…

Pandas.DataFrame.groupby() 数据分组(数据透视、分类汇总) 详解 含代码 含测试数据集 随Pandas版本持续更新

关于Pandas版本&#xff1a; 本文基于 pandas2.1.2 编写。 关于本文内容更新&#xff1a; 随着pandas的stable版本更迭&#xff0c;本文持续更新&#xff0c;不断完善补充。 Pandas稳定版更新及变动内容整合专题&#xff1a; Pandas稳定版更新及变动迭持续更新。 Pandas API参…

使用PyTorch实现混合专家(MoE)模型

Mixtral 8x7B 的推出在开放 AI 领域引发了广泛关注&#xff0c;特别是混合专家&#xff08;Mixture-of-Experts&#xff1a;MoEs&#xff09;这一概念被大家所认知。混合专家(MoE)概念是协作智能的象征&#xff0c;体现了“整体大于部分之和”的说法。MoE模型汇集了各种专家模型…

Transformer详解(附代码实现及翻译任务实现)

一&#xff1a;了解背景和动机 阅读Transformer论文&#xff1a; 阅读原始的Transformer论文&#xff1a;“Attention is All You Need”&#xff0c;由Vaswani等人于2017年提出&#xff0c;是Transformer模型的开创性工作。 二&#xff1a;理解基本构建块 注意力机制&#…

软件研发过程中,项目管理工具应该如何选择?

本文作者&#xff1a;极狐GitLab 资深解决方案架构师 尹学峰 许多企业依旧在用老旧的方式&#xff0c;如Excel离线表格进行项目管理。表格无法简介的呈现出项目的任务分解、完成进度、任务类别等多种项目管理过程中必备的要求&#xff0c;更无法实现与企业员工的日常即时通信系…

MATLAB - 加载预定义的机器人模型

系列文章目录 前言 一、 要快速访问常见的机器人模型&#xff0c;可使用 loadrobot 功能&#xff0c;该功能可加载市售的机器人模型&#xff0c;如 Universal Robots™ UR10 cobot、Boston Dynamics™ Atlas 人形机器人和 KINOVA™ Gen 3 机械手。探索如何生成关节配置并与机器…

day02_计算机常识丶第一个程序丶注释丶关键字丶标识符

计算机常识 计算机如何存储数据 计算机世界中只有二进制。那么在计算机中存储和运算的所有数据都要转为二进制。包括数字、字符、图片、声音、视频等。 进制 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法 实例&#xff1a; // 在java 中 可以使用不同…

蓝桥杯备赛day02 -- 算法训练题 拿金币Java

目录 题目&#xff1a; 问题描述 输入格式 输出格式 解题过程 第一步 定义dp数组 第二步 确定 dp 数组递推公式 第三步 dp数组的初始化 第四步 dp数组的遍历顺序 第五步 举例说明 报错&#xff1a;内存超限 用dp数组去存储位置上的金币 dp数组从二维降为一维 收获&a…

如何在CentOS 7 中搭建Python 3.0 环境

1、下载 通过https://www.python.org/ftp/python/下载Python安装包&#xff0c;这里下载Python-3.10.9.tgz&#xff1b; 2、上传 借助MobaXterm等工具将Python安装包上传至/opt目录&#xff1b; 3、解压 将JDK压缩文件解压至/opt目录&#xff1a;tar -xvf /opt/Python-3.1…

idea设置编辑器背景颜色

文章目录 一、Ided常用工具栏显示二、更改idea主题设置三、设置代码编辑器背景颜色为豆沙绿四、设置新项目 默认Jdk配置、maven配置1、settings for new projects2、structre for new projects 五、修改代码中注释的字体颜色六、设置编辑器字体大小七、文件编码的设置(可以设置…

【网络安全】【密码学】【北京航空航天大学】实验一、数论基础(上)【C语言和Java实现】

实验一、数论基础&#xff08;上&#xff09; 一、实验目的 1、通过本次实验&#xff0c;熟悉相关的编程环境&#xff0c;为后续的实验做好铺垫&#xff1b; 2、回顾数论学科中的重要基本算法&#xff0c;并加深对其的理解&#xff0c;为本学期密码学理论及实验课程打下良好…

Python - 深夜数据结构与算法之 DP 串讲

目录 一.引言 二.DP 知识点回顾 1.递归 2.分治 3.动态规划 三.DP 经典题目回顾 1.Climb-Stairs [70] 2.Unique-Paths [62] 3.House-Robber [198] 4.Min-Path-Sum [64] 5.Best-Time-Sell-Stock [121] 6.Min-Cost-Climb [746] 7.Edit-Distance [72] 8.Longest-Sub-…

Android PendingIntent 闪退

先来给大家推荐一个我日常会使用到的图片高清处理在线工具&#xff0c;主要是免费&#xff0c;直接白嫖 。 有时候我看到一张图片感觉很不错&#xff0c;但是图片清晰度不合我意&#xff0c;就想有没有什么工具可以处理让其更清晰&#xff0c; 网上随便搜下就能找到&#xff…

C++设计模式(李建忠)笔记1

C设计模式&#xff08;李建忠&#xff09; 本文是学习笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考链接 Youtube: C设计模式 Gtihub源码与PPT&#xff1a;https://github.com/ZachL1/Bilibili-plus 豆瓣: 设计模式–可复用面向对象软件的基础 文章目录 C设计模…