当前位置:首页 > 文章列表 > 文章 > java教程 > 合并排序:大型数据集的分而治之

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

来源:dev.to 2024-12-18 09:04:05 0浏览 收藏

亲爱的编程学习爱好者,如果你点开了这篇文章,说明你对《合并排序:大型数据集的分而治之》很感兴趣。本篇文章就来给大家详细解析一下,主要介绍一下,希望所有认真读完的童鞋们,都有实质性的提高。

本文介绍了归并排序,这是一种时间复杂度为 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 的性能取决于输入数据的范围,如果范围很大,性能可能会下降。此外,它的递归和分区性质(向左或向右排序)可以同时或并行实现。这极大地提高了合并排序算法在支持并行处理和多线程的现代计算系统中处理大规模数据集时的效率。

合并排序的缺点

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

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

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。

今天关于《合并排序:大型数据集的分而治之》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于的内容请关注golang学习网公众号!

版本声明
本文转载于:dev.to 如有侵犯,请联系study_golang@163.com删除
探秘国产电脑品牌:选择与发展的全景指南探秘国产电脑品牌:选择与发展的全景指南
上一篇
探秘国产电脑品牌:选择与发展的全景指南
性能远超当前SOTA,首个可解释RNA的AI植物基础模型来了,整合1124种植物RNA信息
下一篇
性能远超当前SOTA,首个可解释RNA的AI植物基础模型来了,整合1124种植物RNA信息
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    542次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    508次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    497次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    484次学习
查看更多
AI推荐
  • AI Make Song:零门槛AI音乐创作平台,助你轻松制作个性化音乐
    AI Make Song
    AI Make Song是一款革命性的AI音乐生成平台,提供文本和歌词转音乐的双模式输入,支持多语言及商业友好版权体系。无论你是音乐爱好者、内容创作者还是广告从业者,都能在这里实现“用文字创造音乐”的梦想。平台已生成超百万首原创音乐,覆盖全球20个国家,用户满意度高达95%。
    2次使用
  • SongGenerator.io:零门槛AI音乐生成器,快速创作高质量音乐
    SongGenerator
    探索SongGenerator.io,零门槛、全免费的AI音乐生成器。无需注册,通过简单文本输入即可生成多风格音乐,适用于内容创作者、音乐爱好者和教育工作者。日均生成量超10万次,全球50国家用户信赖。
    2次使用
  •  BeArt AI换脸:免费在线工具,轻松实现照片、视频、GIF换脸
    BeArt AI换脸
    探索BeArt AI换脸工具,免费在线使用,无需下载软件,即可对照片、视频和GIF进行高质量换脸。体验快速、流畅、无水印的换脸效果,适用于娱乐创作、影视制作、广告营销等多种场景。
    2次使用
  • SEO标题协启动:AI驱动的智能对话与内容生成平台 - 提升创作效率
    协启动
    SEO摘要协启动(XieQiDong Chatbot)是由深圳协启动传媒有限公司运营的AI智能服务平台,提供多模型支持的对话服务、文档处理和图像生成工具,旨在提升用户内容创作与信息处理效率。平台支持订阅制付费,适合个人及企业用户,满足日常聊天、文案生成、学习辅助等需求。
    9次使用
  • Brev AI:零注册门槛的全功能免费AI音乐创作平台
    Brev AI
    探索Brev AI,一个无需注册即可免费使用的AI音乐创作平台,提供多功能工具如音乐生成、去人声、歌词创作等,适用于内容创作、商业配乐和个人创作,满足您的音乐需求。
    10次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码