0

0

合并排序:大型数据集的分而治之

聖光之護

聖光之護

发布时间:2024-12-13 14:30:02

|

892人浏览过

|

来源于dev.to

转载

本文介绍了归并排序,这是一种时间复杂度为 o(n log n) 的分治算法。该算法非常适合对大型数据集进行排序,因为它具有稳定性,并且能够处理因尺寸过大而无法放入内存的数据。它还涵盖了合并排序的优点,包括它对链表和并行实现的适用性,同时强调了一些缺点,例如增加的空间复杂性和递归开销。


在计算机科学中,归并排序被归类为时间复杂度为 o(n log n) 的“分而治之”算法,通常使用递归来对数据进行排序。它最适合大型数据集,特别是当稳定性很重要时,以及数据无法一次全部装入内存的场景,例如外部排序或排序链表。

分治算法可以定义为具有三个步骤:

除法:如果输入大小小于某个阈值(例如一个或两个元素),则使用简单的方法直接解决问题并返回如此获得的解决方案。否则,将输入数据划分为两个或多个不相交的子集。
征服:递归地解决与子集相关的子问题。
合并:将子问题的解合并为原问题的解。
(goodrich 等人,2023,第 12.1 节归并排序)

合并排序采用分而治之的方法,将数据顺序划分为两个大致相等的分区,并递归地将左侧分区排序为较小的已排序子分区,直到达到单个元素。然后它对所有子分区进行组合和排序以构造最终排序的左分区。对未排序的右分区重复相同的过程。最后,将左右分区组合并排序以产生最终的排序输出。请参阅图 1 了解合并排序的划分和组合步骤。

图1
合并排序
在计算机科学中,归并排序被归类为时间复杂度为 o(n log n) 的“分而治之”算法,通常使用递归来对数据进行排序。它最适合大型数据集,特别是当稳定性很重要时,以及数据无法一次全部装入内存的场景,例如外部排序或排序链表。

分治算法可以定义为具有三个步骤:

除法:如果输入大小小于某个阈值(例如,一个或两个元素),则使用简单的方法直接解决问题并返回如此获得的解决方案。否则,将输入数据划分为两个或多个不相交的子集。
征服:递归地解决与子集相关的子问题。
合并:将子问题的解合并为原问题的解。
(goodrich 等人,2023,第 12.1 节归并排序)

合并排序采用分而治之的方法,将数据顺序划分为两个大致相等的分区,并递归地将左侧分区排序为较小的已排序子分区,直到达到单个元素。然后它对所有子分区进行组合和排序以构造最终排序的左分区。对未排序的右分区重复相同的过程。最后,将左右分区组合并排序以产生最终的排序输出。请参阅图 1 了解合并排序的划分和组合步骤。

图1
合并排序
在计算机科学中,归并排序被归类为时间复杂度为 o(n log n) 的“分而治之”算法,通常使用递归来对数据进行排序。它最适合大型数据集,特别是当稳定性很重要时,以及数据无法一次全部装入内存的场景,例如外部排序或排序链表。

分治算法可以定义为具有三个步骤:

除法:如果输入大小小于某个阈值(例如一个或两个元素),则使用简单的方法直接解决问题并返回如此获得的解决方案。否则,将输入数据划分为两个或多个不相交的子集。
征服:递归地解决与子集相关的子问题。
合并:将子问题的解合并为原问题的解。
(goodrich 等人,2023,第 12.1 节归并排序)

合并排序采用分而治之的方法,将数据顺序划分为两个大致相等的分区,并递归地将左侧分区排序为较小的已排序子分区,直到达到单个元素。然后它对所有子分区进行组合和排序以构造最终排序的左分区。对未排序的右分区重复相同的过程。最后,将左右分区组合并排序以产生最终的排序输出。请参阅图 1 了解合并排序的划分和组合步骤。

图1
合并排序
合并排序:大型数据集的分而治之
注意:图中没有显示所有步骤细节,但足以理解整个过程。来自“第 12 章:算法:排序和选择。 goodrich 等人的《数据结构和算法》。 (2023)。修改。

归并排序的优点

归并排序有几个优点。首先,它是一种稳定的排序算法,这意味着它保持未排序数据条目顺序的相对顺序。例如,具有相同值的两个数据元素将保留其原始输入顺序。这对于必须保持输入顺序的应用程序至关重要,例如按字母等级对学生的课程成绩进行排序时,学生在同一班级中可能有多个 a 或 b。另一个例子是在多键排序中,数据按一个标准排序,然后按另一个标准排序,稳定性对于确保保留具有相同值的数据元素的条目顺序至关重要。

此外,归并排序算法由于其顺序访问模式而特别适合链表(khandelwal,2023)。链表不支持像数组那样的索引访问,这允许随机访问数据元素。在链表中,要访问特定索引处的元素,通常需要从头节点开始遍历链表,因此不能随机访问数据。然而,使用合并排序,列表被分成两半并递归排序,而不需要索引数据。数据是按顺序访问的,这自然与链表的访问模式一致,使得合并排序非常适合对链表中存储的数据进行排序。

该算法的另一个优点是它保证了 o(n log n) 时间复杂度,使其对于大型数据集排序既可靠又高效。它的时间复杂度是可靠的,因为它在所有情况场景下都保持一致:

  • 最坏情况:o(n log n) — 当数组被重复分成两半直到到达各个元素,然后进行合并过程时,就会发生这种情况。
  • 平均情况:o(n log n) — 与最坏情况类似,该算法始终将数组分成两半,然后合并它们。
  • 最佳情况:o(n log n) — 即使数组已部分排序,归并排序也会将其分成两半并合并它们,从而获得与最坏情况和平均情况相同的时间复杂度。

(khandelwal,2023,第 1 页)

对于可预测且稳定的时间复杂度至关重要的环境,其可靠的时间复杂度是比快速排序更好的选择。尽管快速排序的时间复杂度也是 o(n log n),但在数据可能部分排序的环境中,它可能会降级为 o(n2),这使得 marge-sort 成为此类情况的更好选择。

此外,由于合并过程中使用的临时数组需要额外的空间,其空间复杂度为 o(n);然而,它是稳定且可预测的,使得该算法非常适合对资源可用性需要可预测的大型数据集进行排序。

与基数排序算法相比,虽然其时间复杂度为 o(n k),优于归并排序的时间复杂度,但归并排序更适合数据多样化且随机的情况。这是因为基数排序最适用于数据类型,例如整数或固定长度字符串。相比之下,归并排序适用于所有类型的数据,并且不受固定或有限值范围需求的限制。而 radix-sorts 的性能取决于输入数据的范围,如果范围很大,性能可能会下降。此外,它的递归和分区性质(向左或向右排序)可以同时或并行实现。这极大地提高了合并排序算法在支持并行处理和多线程的现代计算系统中处理大规模数据集时的效率。

恒浪威购商城
恒浪威购商城

基于asp.net2.0框架技术与企业级分布式框架以及与 ms sql server 2000数据库无缝集合而成,并且融合当前流行的ajax技术进行编写的电子商务系统,她整合了多用户商城、单用户商城功能和恒浪网站整合管理系统,吸收绝大部分同类产品的精华和优点,独创网络团购(b2t)电子商务模式,流程化的团购功能和视频导购等功能,是一款极具商业价值的电子商务系统。商城前台功能概述:商城会员可前台自行

下载

合并排序的缺点

另一方面,归并排序的一些缺点包括处理递归调用造成的额外开销以及深度递归情况下堆栈溢出的潜在风险。与插入排序等更简单的算法相比,这些算法在对小数据集进行排序时效率较低。

此外,它比插入排序或选择排序等更简单的算法实现起来更复杂,并且具有更高的空间复杂度,后者需要更少的内存,并且在某些应用程序中实现起来可能更简单。

java 代码示例如前所述,merge-sort 递归分区排序可以并行或并发实现。第一个代码片段是经典的归并排序实现,第二个示例是使用并行性的归并排序实现。

java 中的基本归并排序:

import java.util.arrays;
import java.util.comparator;

public class mergesort {

    public static  void mergesort(t[] array, comparator comp) {
        int n = array.length;

        // --- base case ---
        // if the array has 1 or 0 elements, it is already sorted, so return.
        if (n < 2) {
            return;
        }

        // --- divide step ---
        // find the midpoint to divide the array into two halves
        int mid = n / 2;

            // create two subarrays: one from the left half and one from the right half
                // left subarray from index 0 to mid-1
        t[] left = arrays.copyofrange(array, 0, mid); 
                // right subarray from index mid to n-1
        t[] right = arrays.copyofrange(array, mid, n); 

                // --- conquer step ---
        // recursive call - sort the left (first) and right halves of the array
        mergesort(left, comp); // sort the left half
        mergesort(right, comp); // sort the right half

                // --- combine step ---
                // after both halves are sorted, merge them back into a single sorted array
        merge(left, right, array, comp); // merge sorted halves into original    array
    }

    // method to merge two sorted subarrays (left and right) into the original
    // result array
    private static  void merge(t[] left, t[] right, t[] result, 
                                comparator comp){
                // i, j track position in left and right arrays; k tracks result        
                int i = 0, j = 0, k = 0; 

        // --- merging process ---
        // compare elements from the left and right arrays and place the smaller 
                // one into result.
        while (i < left.length && j < right.length) {
            // compare elements and merge them in sorted order
            if (comp.compare(left[i], right[j]) <= 0) {
                                // copy from left array and move the index i
                result[k++] = left[i++]; 
            } else {
                                // copy from right array and move the index j
                result[k++] = right[j++]; 
            }
        }
        // --- copy remaining elements ---
        // if there are any remaining elements in the left array, copy them into 
                // result
        while (i < left.length) {
            result[k++] = left[i++];
        }
        // if there are any remaining elements in the right array, copy them into
        // result
        while (j < right.length) {
            result[k++] = right[j++];
        }
    }

    public static void main(string[] args) {
        integer[] array = { 3, 5, 1, 6, 4, 7, 2 };
        mergesort(array, comparator.naturalorder());
        system.out.println(arrays.tostring(array));
    }
}

java 中的并行归并排序:

import java.util.arrays;
import java.util.comparator;
import java.util.concurrent.recursiveaction;
import java.util.concurrent.forkjoinpool;

// parallelmergesort class
class parallelmergesort extends recursiveaction {

    private final t[] array;
    private final comparator comp;

    // constructor to initialize the array and comparator
    public parallelmergesort(t[] array, comparator comp) {
        this.array = array;
        this.comp = comp;
    }

    // the compute method defines the parallel sorting process
    @override
    protected void compute() {
        int n = array.length;

        // --- base case ---
        // if the array has 1 or 0 elements, it is already sorted, so return
        if (n < 2) {
            return;
        }

        // --- divide step ---
        // divide the array into two halves: left (first half) 
        // and right (second half)
        int mid = n / 2;
        // create two subarrays: left subarray from index 0 to mid-1
        t[] left = arrays.copyofrange(array, 0, mid);
        // right subarray from index mid to n-1
        t[] right = arrays.copyofrange(array, mid, n);

        // --- conquer step ---
        // create two tasks to sort the left and right halves in parallel
        parallelmergesort lefttask = new parallelmergesort<>(left, comp);
        parallelmergesort righttask = new parallelmergesort<>(right, comp);

        // invoke the tasks concurrently, allowing them to run in parallel
        invokeall(lefttask, righttask); 

        // --- combine step ---
        // after both halves are sorted, merge them back into the original array
        merge(left, right, array, comp);
    }

    // method to merge two sorted subarrays (left and right) into the original 
    // result array
    private static  void merge(t[] left, t[] right, t[] result, 
                               comparator comp) {
        int i = 0, j = 0, k = 0; // i, j track position in left and right arrays; k 
                                 // tracks result

        // --- merging process ---
        // compare elements from the left and right arrays and place the smaller one 
        // into result
        while (i < left.length && j < right.length) {
            if (comp.compare(left[i], right[j]) <= 0) {
                result[k++] = left[i++]; // take element from left array and move 
                                         // the index i
            } else {
                result[k++] = right[j++]; // take element from right array and move 
                                          // the index j
            }
        }
        // --- copy remaining elements ---
        // if there are any remaining elements in the left array, copy them into 
        // result
        while (i < left.length) {
            result[k++] = left[i++];
        }
        // if there are any remaining elements in the right array, copy them into 
        // result
        while (j < right.length) {
            result[k++] = right[j++];
        }
    }

    // initializes parallel merge sort using forkjoinpool
    public static  void parallelmergesort(t[] array, comparator comp) {
        // create a forkjoinpool for parallel execution
        forkjoinpool pool = new forkjoinpool();
        // start the parallel sorting task by invoking the main parallelmergesort 
        // task
        pool.invoke(new parallelmergesort<>(array, comp));
    }
}
public class Main {
    public static void main(String[] args) {
        Integer[] array = {3, 5, 1, 6, 4, 7, 2}; 
        System.out.println("Unsorted array: " + Arrays.toString(array));
        ParallelMergeSort.parallelMergeSort(array, Comparator.naturalOrder());
        System.out.println("Sorted array: " + Arrays.toString(array));
    }
}

现实生活中的使用示例

合并排序通常在需要获取大型数据集并将其存储在磁盘上的情况下实现,例如在数据中心中,此过程通常称为外部排序。在线零售商就是一个很好的例子,例如亚马逊或 ebay,其中数以百万计的客户订单需要根据时间戳进行排序。由于数据集太大而无法放入内存,因此合并排序非常适合此任务。数据可以分块加载,在内存中并行排序以保持数据的稳定性,并在磁盘上合并。

总而言之,归并排序可靠、稳定,能够处理大型数据集,其递归分区可以并行实现,非常适合大规模在线商店排序应用。然而,在实现算法时应考虑其额外的空间和递归开销,尤其是在资源可能有限的环境中。


参考文献:

goodrich t, m.、tamassia, r. 和 goldwasser h.m.(2023 年 6 月)。第 12 章:算法:排序和选择。数据结构和算法。 zybook isbn: 979–8–203–40813–6.

khandelwal, v.(2023 年,10 月 25 日)。什么是合并排序算法:它是如何工作的,等等。简单学习。 https://www.simplilearn.com/tutorials/data-structure-tutorial/merge-sort-algorithm


最初于 2024 年 10 月 2 日发表于 alex.omegapy - medium。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

309

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

222

2025.10.31

sort排序函数用法
sort排序函数用法

sort排序函数的用法:1、对列表进行排序,默认情况下,sort函数按升序排序,因此最终输出的结果是按从小到大的顺序排列的;2、对元组进行排序,默认情况下,sort函数按元素的大小进行排序,因此最终输出的结果是按从小到大的顺序排列的;3、对字典进行排序,由于字典是无序的,因此排序后的结果仍然是原来的字典,使用一个lambda表达式作为key参数的值,用于指定排序的依据。

391

2023.09.04

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

298

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

212

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1501

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

624

2023.11.24

java读取文件转成字符串的方法
java读取文件转成字符串的方法

Java8引入了新的文件I/O API,使用java.nio.file.Files类读取文件内容更加方便。对于较旧版本的Java,可以使用java.io.FileReader和java.io.BufferedReader来读取文件。在这些方法中,你需要将文件路径替换为你的实际文件路径,并且可能需要处理可能的IOException异常。想了解更多java的相关内容,可以阅读本专题下面的文章。

633

2024.03.22

clawdbot ai使用教程 保姆级clawdbot部署安装手册
clawdbot ai使用教程 保姆级clawdbot部署安装手册

Clawdbot是一个“有灵魂”的AI助手,可以帮用户清空收件箱、发送电子邮件、管理日历、办理航班值机等等,并且可以接入用户常用的任何聊天APP,所有的操作均可通过WhatsApp、Telegram等平台完成,用户只需通过对话,就能操控设备自动执行各类任务。

19

2026.01.29

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号