讲师中心 微信公众号
AI工具推荐 视频效率加速

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

云磊同学_1858

云磊同学_1858

发布时间: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);然而,它是稳定且可预测的,使得该算法非常适合对资源可用性需要可预测的大型数据集进行排序。

AI舌诊面诊问诊医疗系统+诊疗商城系统源码
AI舌诊面诊问诊医疗系统+诊疗商城系统源码

AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。

下载

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

合并排序的缺点

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

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

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

java 中的基本归并排序:

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

public class mergesort {

    public static  void mergesort(t[] array, comparator<? super t> 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<? super t> 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<? super t> comp;

    // constructor to initialize the array and comparator
    public parallelmergesort(t[] array, comparator<? super t> 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<? super t> 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<? super t> 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工具

更多
豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

相关专题

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

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

2911

2023.10.31

php数据类型
php数据类型

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

534

2025.10.31

c语言 数据类型
c语言 数据类型

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

502

2026.02.12

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

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

1138

2023.09.04

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

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

1738

2023.08.03

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

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

2664

2023.09.04

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

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

6044

2023.10.24

字符串介绍
字符串介绍

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

5609

2023.11.24

OpenJDK JVM参数怎么调优
OpenJDK JVM参数怎么调优

Java应用运行缓慢,如何调整JVM参数?本专题介绍OpenJDK JVM性能调优的基本流程,涵盖性能指标确认、运行数据采集、内存配置和垃圾回收分析,帮助你先定位瓶颈,再通过对照测试验证效果,避免直接套用参数而影响应用稳定性。

20

2026.10.16

热门下载

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

精品课程

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

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn