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:
            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,所以还有一小撮修要我们收尾。

四.总结

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

idea设置编辑器背景颜色

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

leetcode-344. 反转字符串、9. 回文数

题目1&#xff1a; 解题方法 直接用reverse()即可 代码&#xff1a; class Solution(object):def reverseString(self, s):""":type s: List[str]:rtype: None Do not return anything, modify s in-place instead."""return s.reverse()如果不…

翻译: Pyenv管理Python版本从入门到精通二

Pyenv系列&#xff1a; 翻译: Pyenv管理Python版本从入门到精通一 1. 高级 Pyenv 用法 1.1 在 pyenv-virtualenv 中使用虚拟环境 可以使用 pyenv-virtualenv 扩展 Pyenv 来管理虚拟环境。这是隔离项目环境并有效管理其依赖项的好方法。以下是使用 pyenv-virtualenv 创建虚…

C# 面向切面编程之AspectCore实践(二)

写在前面 在上一篇中对AspectCore进行了初步的了解&#xff0c;用于拦截的属性加在了具体类的方法上。 C# 面向切面编程之AspectCore初探 这一篇验证一下把拦截属性加在接口上&#xff0c;这样实现该接口的类中所对应的方法都会被拦截到&#xff1b;另外示例中还尝试对方法的…

如何从命令行运行testng.xml?

目录 创建一个新的java项目并从命令行运行testng.xml 使用命令行运行XML文件 从命令行运行现有maven项目的XML文件 在这篇文章中&#xff0c;我们将使用命令行运行testng.xml。有多种场景需要使用命令行工具运行testng.xml。也许您已经创建了一个maven项目&#xff0c;现在想…

【VTKExamples::PolyData】第三期 DecimatePolylineDeleteCell

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 前言 本文分享VTK样例 DecimatePolyline折线抽取和 DeleteCell样例,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我的动力(^U^)ノ~YO 1. DecimatePo…

YOLOv8改进 | 主干篇 | 低照度增强网络PE-YOLO改进主干(改进暗光条件下的物体检测模型)

一、本文介绍 本文给大家带来的改进机制是低照度图像增强网络PE-YOLO中的PENet,PENet通过拉普拉斯金字塔将图像分解成多个分辨率的组件,增强图像细节和低频信息。它包括一个细节处理模块(DPM),用于通过上下文分支和边缘分支增强图像细节,以及一个低频增强滤波器(LEF),…

C语言经典算法之顺序查找算法

目录 前言 A.建议 B.简介 一 代码实现 二 算法时空复杂度 A.时间复杂度&#xff1a; B.空间复杂度&#xff1a; 三 优点和缺点 A.优点&#xff1a; B.缺点&#xff1a; 四 现实中的应用 前言 A.建议 1.学习算法最重要的是理解算法的每一步&#xff0c;而不是记住算…

steam搬砖项目风险在哪里?这几点一定要记牢

你可能已经听说过Steam搬砖项目&#xff0c;这是一个通过在Steam平台充值美元&#xff0c;购买道具&#xff0c;然后将这些道具转移到BUFF平台进行交易&#xff0c;从而获取利润的方式。这个项目的操作方法主要是利用一些渠道和经验技巧&#xff0c;购买低价道具&#xff0c;然…

力扣每日一练(24-1-17):轮转数组

方法一&#xff1a;使用额外的数组 这个方法的思路是创建一个新的数组&#xff0c;然后将每个元素放到正确的位置上。新数组的第i个元素应该是原数组的第(i len(nums) - k) % len(nums)个元素。 def rotate(nums, k):n len(nums)rotated [0] * nfor i in range(n):rotated[(…

用Axure RP 9制作元件教程

制作 软件&#xff1a;Axure RP 9 图标&#xff1a;iconfont 制作流程 1.打开Axure RP 9 2.iconfont 搜索眼睛 图标 图标找好之后 我们可以开始了 3.制作 我们用文本框 如图片 第2步 我们选中这2个把他们变成动态面板 在动态面板增加一个状态 如图片 我们接下来…

locust快速入门--使用分布式提高测试压力

背景&#xff1a; 使用默认的locust启动命令进行压测时&#xff0c;尽管已经将用户数设置大比较大&#xff08;400&#xff09;&#xff0c;但是压测的时候RPS一直在100左右。需要增加压测的压力。 问题原因&#xff1a; 如果你是通过命令行启动的或者参考之前文章的启动方式…

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-…

AI大模型预先学习笔记一:transformer和fine tune技术介绍

一、商业观点&#xff1a;企业借助大模型获得业务增长可能 二、底层原理&#xff1a;transformer 1&#xff09;备注 ①下面每个步骤都是自回归的过程&#xff08;aotu-regressive&#xff09;&#xff1a;已输出内容的每个字作为输入&#xff0c;一起生成下一个字 ②合起来就…

决战排序之巅(二)

决战排序之巅&#xff08;二&#xff09; 排序测试函数 void verify(int* arr, int n) 归并排序递归方案代码可行性测试 非递归方案代码可行性测试 特点分析 计数排序代码实现代码可行性测试 特点分析 归并排序 VS 计数排序&#xff08;Release版本&#xff09;说明1w rand( ) …

23/76-LeNet

LeNet 早期成功的神经网络。 先使用卷积层来学习图片空间信息。 然后使用全连接层转换到类别空间。 #In[]LeNet,上世纪80年代的产物,最初为了手写识别设计from d2l import torch as d2l import torch from torch import nn from torch.nn.modules.loss import CrossEntropyLos…

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

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

C++面试宝典第21题:字符串解码

题目 给定一个经过编码的字符串,返回其解码后的字符串。具体的编码规则为:k[encoded_string],表示方括号内部的encoded_string正好重复k次。注意:k保证为正整数;encoded_string只包含大小写字母,不包含空格和数字;方括号确定是匹配的,且可以嵌套。 示例: 编码字符串为…

tcpdump常用参数以及wireshark密文解密

tcpdump常用参数以及wireshark密文解密 文章目录 一、tcpdump命令和常用参数二、在wireshark中协议解析 tcpdump常用参数 一、tcpdump命令和常用参数 tcpdump常用命令&#xff1a;tcpdump -i eth0 src host 11.6.224.1 and udp port 161 -s 0 -w 161.pcap &#xff08;161为sn…

开发知识点-JAVA-springboot

springboot springbootConfiguration注解的底层核心原理Bean注解的底层核心原理 springboot Configuration注解的底层核心原理 https://www.bilibili.com/video/BV1rq4y1E7gK/?spm_id_from333.999.0.0&vd_sourcef21773b7086456ae21a58a6cc59023be spring.io 全家桶 24…