Python 算法高级篇:归并排序的优化与外部排序

Python 算法高级篇:归并排序的优化与外部排序

  • 引言
  • 1. 归并排序的基本原理
  • 2. 归并排序的优化
    • 2.1 自底向上的归并排序
    • 2.2 最后优化
  • 3. 外部排序
  • 4. 性能比较
  • 5. 结论

引言

在计算机科学中,排序是一项基本的任务,而归并排序( Merge Sort )是一种著名的排序算法,它具有稳定性和良好的时间复杂度。本文将介绍归并排序的基本原理,然后深入探讨如何进行优化以及如何应用归并排序进行外部排序。

😃😄 ❤️ ❤️ ❤️

1. 归并排序的基本原理

归并排序采用分治的策略,将一个大问题分解为小问题,解决小问题,然后将它们合并以获得最终解决方案。其基本步骤如下:

  • 1 . 分割( Divide ):将数组划分为两个子数组,通常是平均分割。
  • 2 . 递归( Conquer ):递归地对子数组进行排序。
  • 3 . 合并( Merge ):将排好序的子数组合并为一个有序的数组。

下面是一个简单的归并排序算法的 Python 实现:

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2  # 找到数组的中间位置
        left_half = arr[:mid]
        right_half = arr[mid:]

        merge_sort(left_half)  # 递归排序左半部分
        merge_sort(right_half)  # 递归排序右半部分

        i = j = k = 0

        # 合并两个子数组
        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                arr[k] = left_half[i]
                i += 1
            else:
                arr[k] = right_half[j]
                j += 1
            k += 1

        while i < len(left_half):
            arr[k] = left_half[i]
            i += 1
            k += 1

        while j < len(right_half):
            arr[k] = right_half[j]
            j += 1
            k += 1

归并排序的时间复杂度是 O ( n log n ),它是一种稳定的排序算法,但它需要额外的空间来存储临时数组。

2. 归并排序的优化

尽管归并排序的时间复杂度相对较低,但它在实际应用中可能会因为空间复杂度较高而受到限制。为了解决这个问题,可以进行一些优化。

2.1 自底向上的归并排序

传统的归并排序是自顶向下的,即从顶部开始递归划分子数组。在自底向上的归并排序中,我们从底部开始,首先将相邻的元素两两合并,然后是四四合并,八八合并,直到整个数组排序完成。这样可以减少递归所需的栈空间,降低空间复杂度。

以下是自底向上归并排序的 Python 实现:

def merge_sort_bottom_up(arr):
    n = len(arr)
    curr_size = 1

    while curr_size < n:
        for left in range(0, n - 1, 2 * curr_size):
            mid = min(left + curr_size - 1, n - 1)
            right = min(left + 2 * curr_size - 1, n - 1)

            if mid < right:
                merge(arr, left, mid, right)
        curr_size *= 2

def merge(arr, left, mid, right):
    n1 = mid - left + 1
    n2 = right - mid

    L = [0] * n1
    R = [0] * n2

    for i in range(n1):
        L[i] = arr[left + i]
    for i in range(n2):
        R[i] = arr[mid + i + 1]

    i = j = 0
    k = left

    while i < n1 and j < n2:
        if L[i] <= R[j]:
            arr[k] = L[i]
            i += 1
        else:
            arr[k] = R[j]
            j += 1
        k += 1

    while i < n1:
        arr[k] = L[i]
        i += 1
        k += 1

    while j < n2:
        arr[k] = R[j]
        j += 1
        k += 1

2.2 最后优化

归并排序的一个缺点是它需要额外的空间来存储临时数组。为了避免这种情况,可以使用一个额外的数组来存储一半的数据,然后交替地将数据复制到原始数组中。这可以降低空间复杂度,但增加了一些额外的复制操作。

以下是这种优化方法的 Python 实现:

def merge_sort_optimized(arr):
    n = len(arr)
    temp_arr = [0] * n

    curr_size = 1
    while curr_size < n:
        left = 0
        while left < n - 1:
            mid = min(left + curr_size - 1, n - 1)
            right = min(left + 2 * curr_size - 1, n - 1)
            if mid < right:
                merge_optimized(arr, left, mid, right, temp_arr)
            left += 2 * curr_size
        curr_size *= 2

def merge_optimized(arr, left, mid, right, temp_arr):
    i = left
    j = mid + 1
    for k in range(left, right + 1):
        temp_arr[k] = arr[k]
    k = left
    while i <= mid and j <= right:
        if temp_arr[i] <= temp_arr[j]:
            arr[k] = temp_arr[i]
            i += 1
        else:
            arr[k] = temp_arr[j]
            j += 1
        k += 1
    while i <= mid:
        arr[k] = temp_arr[i]
        k += 1
        i += 1

这种优化方法减少了内存的使用,但增加了一些额外的复制操作。

3. 外部排序

归并排序还可以应用于外部排序,这是一种处理大规模数据集的排序方法。外部排序的主要思想是将大数据集分成多个小数据块,每个小数据块都可以在内存中进行排序。排序后,将这些小数据块合并成一个有序的大数据集。

下面是一个简单的外部排序示例,假设我们有一个非常大的文件,无法一次性加载到内存中进行排序。我们可以将文件划分为多个小文件块,分别进行排序,然后合并它们。

def external_sort(input_file, output_file, chunk_size):
    # 划分文件为多个块
    divide_file(input_file, chunk_size)

    # 对每个块进行内部排序
    sort_chunks()

    # 合并排序后的块
    merge_sorted_chunks(output_file)

def divide_file(input_file, chunk_size):
    # 从输入文件中读取数据并划分为块
    pass

def sort_chunks():
    # 对每个块进行内部排序
    pass

def merge_sorted_chunks(output_file):
    # 合并排序后的块
    pass

这个示例演示了如何将大文件划分为多个小文件块,每个块都可以在内存中排序。然后,排序后的块将被合并为一个有序的输出文件。

4. 性能比较

为了演示归并排序的不同优化版本之间的性能差异,我们可以使用一些基准测试来比较它们的运行时间。下面是一个简单的性能比较示例:

import random
import timeit

arr = [random.randint(1, 1000) for _ in range(1000)]

# 未优化的归并排序
def merge_sort_original(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]

        merge_sort_original(left_half)
        merge_sort_original(right_half)

        i = j = k = 0

        while i < len(left_half) and j < len(right_half):
            if left_half[i] < right_half[j]:
                arr[k] = left_half[i]
                i += 1
            else:
                arr[k] = right_half[j]
                j += 1
            k += 1

        while i < len(left_half):
            arr[k] = left_half[i]
            i += 1
            k += 1

        while j < len(right_half):
            arr[k] = right_half[j]
            j += 1
            k += 1

# 测试未优化的归并排序的性能
original_time = timeit.timeit(lambda: merge_sort_original(arr.copy()), number=1000)

# 优化的归并排序
def merge_sort_optimized(arr):
    # 同上,省略优化后的代码

# 测试优化的归并排序的性能
optimized_time = timeit.timeit(lambda: merge_sort_optimized(arr.copy()), number=1000)

print("未优化的归并排序耗时:", original_time)
print("优化的归并排序耗时:", optimized_time)

在上述示例中,我们对未优化的归并排序和优化后的归并排序进行了性能测试。通过这种方式,你可以比较它们的性能并选择最适合你应用的版本。

5. 结论

归并排序是一种经典的排序算法,它使用分治策略和合并操作,具有稳定的性质和较低的时间复杂度。通过进行优化,例如自底向上的归并排序和减少内存使用的外部排序,我们可以提高归并排序的性能和适用性。根据应用的需求和资源限制,选择合适的排序算法版本,以获得最佳性能。这些优化方法可以在处理大数据集和内存受限的情况下发挥重要作用。

[ 专栏推荐 ]
😃 Python 算法初阶:入门篇》😄
❤️【简介】:本课程是针对 Python 初学者设计的算法基础入门课程,涵盖算法概念、时间复杂度、空间复杂度等基础知识。通过实例演示线性搜索、二分搜索等算法,并介绍哈希表、深度优先搜索、广度优先搜索等搜索算法。此课程将为学员提供扎实的 Python 编程基础与算法入门,为解决实际问题打下坚实基础。
在这里插入图片描述

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

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

相关文章

MTK AEE_EXP调试方法及user版本打开方案

一、AEE介绍 AEE (Android Exception Engine)是安卓的一个异常捕获和调试信息生成机制。 手机发生错误(异常重启/卡死)时生成db文件(一种被加密过的二进制文件)用来保存和记录异常发生时候的全部内存信息,经过调试和仿真这些信息,能够追踪到异常的缘由。 二、调试方法…

【C++】Linux下如何查看opencv的版本

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

职业技术认证:《研发效能(DevOps)工程师》——开启职业发展新篇章

在互联网行业中&#xff0c;资质认证可以证明在该领域内的专业能力和知识水平。各种技术水平认证也是层出不穷&#xff0c;而考取具有公信力和权威性的认证是从业者的首选。同时&#xff0c;随着国内企业技术实力的提升和国家对于自主可控的重视程度不断提高&#xff0c;国产证…

buuctf_练[CSCCTF 2019 Qual]FlaskLight

[CSCCTF 2019 Qual]FlaskLight 文章目录 [CSCCTF 2019 Qual]FlaskLight掌握知识解题思路关键paylaod 掌握知识 内置函数的过滤&#xff0c;globals变量的过滤&#xff0c;调用内部变量或函数的OS函数进行命令执行 解题思路 打开题目链接&#xff0c;很明显看标题和内容是fla…

代码随想录算法训练营第三十五天丨 贪心算法part06

738.单调递增的数字 思路 暴力解法 题意很简单&#xff0c;那么首先想的就是暴力解法了【超时】。 贪心算法 题目要求小于等于N的最大单调递增的整数&#xff0c;那么拿一个两位的数字来举例。 例如&#xff1a;98&#xff0c;一旦出现strNum[i - 1] > strNum[i]的情况…

CTF-Crypto学习记录-第四天 “ “ --- SHA1安全散列算法,实现原理。

文章目录 前言SHA-1加密算法介绍关于SHA-1和MD5 SHA-1 加密过程原文处理设置初始值和数据结构定义加密运算原理过程 在python中调用SHA-1 前言 MD5学习MD5加密算法 SHA-1加密算法介绍 SHA-1&#xff08;Secure Hash Algorithm1&#xff0c;安全散列算法1&#xff09;是一种密…

安卓逆向之雷电模拟器中控

一, 雷电模拟器 安装使用 官方地址: https://www.ldmnq.com ,官方论坛 https://www.ldmnq.com/forum/ . 有一个多开管理器,还有就是设置手机的参数比较关键。 二,雷电模拟器开启面具,安装LSP。 设置root 权限。

电脑技巧:Win10飞行模式相关知识介绍

目录 一、飞行模式简介 二、如何开关Windows 10中的飞行模式 方法一&#xff1a;使用硬件开关 方法二&#xff1a;使用Windows 10操作中心 方法三&#xff1a;使用Windows 10设置 三、飞行模式开关被卡住、变灰或不工作时怎么办 什么是 Windows 10 飞行模式? 用户如何打…

【ARM Cortex-M 系列 4 番外篇 -- 常用 benchmark 介绍】

文章目录 1.1 CPU 性能测试 MIPS 计算1.1.1 Cortex-M7 CPI 1.2 benchmark 小节1.3.1 Geekbenck 介绍 1.3 编译参数配置 1.1 CPU 性能测试 MIPS 计算 每秒百万指令数 (MIPS)&#xff1a;在数据压缩测试中&#xff0c;MIPS 每秒测量一次 CPU 执行的低级指令的数量。越高越好&…

系列十八、请描述下bean的生命周期

一、概述 bean的生命周期是指bean从创建到销毁的整个过程。 二、生命周期 bean的生命周期是指bean从创建到销毁的整个过程&#xff0c;大致可以分为如下四个过程&#xff1a; 2.1、实例化 实例化可以通过如下几种方式完成&#xff1a;&#xff08;参考系列十五&#xff09…

【中国知名企业高管团队】系列49:VIVO

今天为您介绍蓝绿两厂的蓝厂——VIVO。这两家公司同源于步步高&#xff0c;两家公司除了名字都是四个字以外&#xff0c;其他方面也实在是太像了&#xff0c;就连核心价值观的前两个词都一样&#xff1a;本分、用户导向。 一、VIVO公司简介 和OPPO一样&#xff0c;VIVO也来源…

springboot操作nosql的mongodb,或者是如何在mongodb官网创建服务器并进行操作

第一步&#xff1a;在mongodb的官网里面创建云服务器 点进去 这是免费的&#xff0c;由于是一个项目只可以创建一个&#xff0c;这里我已经创建好了 用本地的mongodb服务也是可以的 第二步&#xff1a;点击connect,下载连接mongodb的软件&#xff1a;MongoDBCompass 第三步&am…

LVS-keepalived实现高可用

概念&#xff1a; 本章核心&#xff1a; Keepalived为LVS应运而生的高可用服务。LVS的调度无法做高可用&#xff0c;预算keepalived这个软件&#xff0c;实现了调度器的高可用。 但是&#xff1a;Keeplived不是专门为LVS集群服务的&#xff0c;也可以做其他服务器的高可用 LVS…

HFSS笔记——求解器和求解分析

文章目录 1、求解器2、求解类型3、自适应网格剖分4、求解频率选择4.1 求解设置项的含义4.2 扫频类型 1、求解器 自从ANSYS将HFSS收购后&#xff0c;其所有的求解器都集成在一起了&#xff0c;点击Project&#xff0c;会显示所有的求解器类型。 其中&#xff0c; HFSS design&…

三十七、【进阶】验证索引的效率

1、准备工作&#xff1a; 创建一张表&#xff0c;该表中有一千万条数据&#xff0c;名为tb_sku&#xff1b; 2、使用主键查询&#xff1a; select * from tb_stu where id1\G; 3、使用非索引查询&#xff1a; 4、给sn字段创建索引&#xff1a; 在创建过程中&#xff0c;发现…

数据结构时间复杂度(补充)和空间复杂度

Hello&#xff0c;今天事10月27日&#xff0c;距离刚开始写博客已经过去挺久了&#xff0c;我也不知道是什么让我坚持这么久&#xff0c;但是学校的课真的很多&#xff0c;很少有时间多出来再学习&#xff0c;有些科目马上要考试了&#xff0c;我还不知道我呢不能过哈哈哈&…

在docker中创建EMQX 加数据卷

1、从虚拟容器中复制出来文件 docker run --rm emqx/emqx:5.3.0 sh -c cd /opt/emqx && tar -c etc | tar -C $PWD -x 2、将这三个文件夹分别赋予最高权限&#xff0c;也可以777可以755 chmod -R 777 data chmod -R 777 etc chmod -R 777 log 3、创建容器代码 docke…

Linux常用命令——chpasswd命令

在线Linux命令查询工具 chpasswd 批量更新用户口令的工具 补充说明 chpasswd命令是批量更新用户口令的工具&#xff0c;是把一个文件内容重新定向添加到/etc/shadow中。 语法 chpasswd(选项)选项 -e&#xff1a;输入的密码是加密后的密文&#xff1b; -h&#xff1a;显示…

[Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷

一.Docker 部署 Nginx 以及端口映射 Docker 部署 Nginx,首先需要下载nginx镜像,然后启动这个镜像,就运行了一个nginx的容器了 1.下载 nginx 镜像并启动容器 #查看是否存在nginx镜像:发现没有nginx镜像 [rootlocalhost zph]# docker images | grep nginx#下载nginx镜像 [rootl…

如何做到在 5 分钟之内将应用大小减少 60% 的?

移动设备的资源总是有限的。有限的电量&#xff0c;有限的存储&#xff0c;有限的处理能力&#xff0c;有限的内存&#xff0c;有限的网络带宽……无论你面对的是 Android 还是 iOS&#xff0c;这都是真理。 在前几个月&#xff0c;我在开发一个安卓应用。当有人尝试用错误的密…