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。

相关专题

更多
java
java

Java是一个通用术语,用于表示Java软件及其组件,包括“Java运行时环境 (JRE)”、“Java虚拟机 (JVM)”以及“插件”。php中文网还为大家带了Java相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

832

2023.06.15

java正则表达式语法
java正则表达式语法

java正则表达式语法是一种模式匹配工具,它非常有用,可以在处理文本和字符串时快速地查找、替换、验证和提取特定的模式和数据。本专题提供java正则表达式语法的相关文章、下载和专题,供大家免费下载体验。

738

2023.07.05

java自学难吗
java自学难吗

Java自学并不难。Java语言相对于其他一些编程语言而言,有着较为简洁和易读的语法,本专题为大家提供java自学难吗相关的文章,大家可以免费体验。

734

2023.07.31

java配置jdk环境变量
java配置jdk环境变量

Java是一种广泛使用的高级编程语言,用于开发各种类型的应用程序。为了能够在计算机上正确运行和编译Java代码,需要正确配置Java Development Kit(JDK)环境变量。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

397

2023.08.01

java保留两位小数
java保留两位小数

Java是一种广泛应用于编程领域的高级编程语言。在Java中,保留两位小数是指在进行数值计算或输出时,限制小数部分只有两位有效数字,并将多余的位数进行四舍五入或截取。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

398

2023.08.02

java基本数据类型
java基本数据类型

java基本数据类型有:1、byte;2、short;3、int;4、long;5、float;6、double;7、char;8、boolean。本专题为大家提供java基本数据类型的相关的文章、下载、课程内容,供大家免费下载体验。

446

2023.08.02

java有什么用
java有什么用

java可以开发应用程序、移动应用、Web应用、企业级应用、嵌入式系统等方面。本专题为大家提供java有什么用的相关的文章、下载、课程内容,供大家免费下载体验。

430

2023.08.02

java在线网站
java在线网站

Java在线网站是指提供Java编程学习、实践和交流平台的网络服务。近年来,随着Java语言在软件开发领域的广泛应用,越来越多的人对Java编程感兴趣,并希望能够通过在线网站来学习和提高自己的Java编程技能。php中文网给大家带来了相关的视频、教程以及文章,欢迎大家前来学习阅读和下载。

16925

2023.08.03

Golang gRPC 服务开发与Protobuf实战
Golang gRPC 服务开发与Protobuf实战

本专题系统讲解 Golang 在 gRPC 服务开发中的完整实践,涵盖 Protobuf 定义与代码生成、gRPC 服务端与客户端实现、流式 RPC(Unary/Server/Client/Bidirectional)、错误处理、拦截器、中间件以及与 HTTP/REST 的对接方案。通过实际案例,帮助学习者掌握 使用 Go 构建高性能、强类型、可扩展的 RPC 服务体系,适用于微服务与内部系统通信场景。

6

2026.01.15

热门下载

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

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
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号