算法排序5——归并排序&分治思想

简介: 算法排序5——归并排序&分治思想

🟡前言


今天是21天挑战赛第二周,本文主要讲归并排序,也是分治思想的重要体现


🟡概述


1️⃣原理图


fbd46989ec404b9b8248f80b20b06d9a.png


2️⃣排序原理&思想体现


归并排序就是将数组尽可能拆成等分的两组,直至变成单个元素(

然后对拆分出来的元素两两排序后组合,变成一个有序的数组(


🟡调用API解决


1️⃣成员变量


private static Comparable[] assist:完成归并操作所需要的辅助数组


2️⃣构造方法和成员方法


构造方法


Merge();


成员方法


  • public static void sort(Comparable[]a):对数组a中元素进行排序
  • public static void sort(Comparable[]a, int lo, int hi):对数组a中从索引lo到索引hi之间元素进行排序
  • public static void merge(Comparable[]a, int lo, int mid,int hi):数组a中从从索引 lo 到索引 min 为一个子数组,从索引 min+1 到索引 hi 为另一个子数组,把a中的两个子数组合并成一个大数组
  • public static boolean less(Comparable x, Comparable y):判断x是否小于y
  • public static void exch(Comparable[]a, int i, int j):交换数组a中下标为 i 和 j 的元素


3️⃣解题思路


1.定义并初始化一个辅助数组,来完成归并操作

2.定义并初始化数组的最小索引 lo最大索引 hi

3.调用一个方法对数组内元素进行排序后归并排序


4️⃣归并排序思路


1.定义三个"指针"p1、p2和 i,用来指向第一子数组的元素、第二子数组的元素和辅助数组的元素

2.比较p1和p2指向元素的大小,将较小的放入辅助数组中


26321235ae084d4e9191ccd05bb8376f.jpg


3.将刚才较小元素的索引值和辅助数组索引值都加1,

4.将指针指向新的元素值与另一个子数组内指针指向元素比较,并将较小的元素存放到辅助数组内

5.重复2-4的步骤,直至其中一个指针指向该子数组的最后一个元素为止


7d3efdcf41c24a4b814f6f2c6b7ecf95.png

579dfd4ad8fc43acb82d3a94a2361e1a.png

bac3a5d4167641d3a3cc4730552d3f53.png

3eb718997e3c4184ac135044a76b081c.png

d29713b098154e2fad01af01aa1b7046.png


6.当其中一个指针走完时,直接将另一个子数组中元素放入辅助数组中


4eda14f1aaf7457ca4e4837cca814f72.png


7.将排序完成的元素放回元素组中


362b816205614ca4842746e57f79f7b0.png


5️⃣归并排序代码实现


//定义一个归并排序的含参方法
public static void merge(Comparable[]a, int lo, int mid,int hi){
        int p1 = lo;//定义并初始化一个子数组的索引
        int p2 = mid + 1;//定义并初始化另一个子数组的索引
        int i = lo;//定义并初始化辅助数组的索引
        //当两个索引值都在索引范围内,将数值更小的元素放入辅助数组
        while (p1 <= mid && p2 <= hi){
            if(less(a[p1],a[p2])){
                assist[i++] = a[p1++];
            }
            else {
                assist[i++] = a[p2++];
            }
        }
        //当p2索引值超出索引范围时(即满足p1没有超出索引范围)
        while (p1 <= mid){
            assist[i++] = a[p1++];//将p1索引所对应剩下的元素复制到辅助数组中
        }
        //当p1索引值超出索引范围时(即满足p2没有超出索引范围)
        while (p2 <= hi){
            assist[i++] = a[p2++];//将p2索引所对应剩下的元素复制到辅助数组中
        }
        //将已排序的辅助数组赋值给原数组
        for(int index = lo; index <= hi; index++){
            a[index] = assist[index];
        }
    }


6️⃣完整代码


public class MergeSort {
    private static  Comparable[] assist;
    public static void sort(Comparable[] a){
        assist = new Comparable[a.length];
        int lo = 0;
        int hi = a.length-1;
        sort(a,lo,hi);//归并排序
    }
    public static void sort(Comparable[]a, int lo, int hi){
        if(hi <= lo){
            return;
        }
        int mid = lo + (hi-lo)/2;
        sort(a, lo, mid);//子数组排序
        sort(a, mid+1, hi);//子数组排序
        merge(a, lo, mid, hi);//合成有序的一个大数组
    }
    public static void merge(Comparable[]a, int lo, int mid,int hi){
        int p1 = lo;//定义并初始化一个子数组的索引
        int p2 = mid + 1;//定义并初始化另一个子数组的索引
        int i = lo;//定义并初始化辅助数组的索引
        //当两个索引值都在索引范围内,将数值更小的元素放入辅助数组
        while (p1 <= mid && p2 <= hi){
            if(less(a[p1],a[p2])){
                assist[i++] = a[p1++];
            }
            else {
                assist[i++] = a[p2++];
            }
        }
        //当p2索引值超出索引范围时(即满足p1没有超出索引范围)
        while (p1 <= mid){
            assist[i++] = a[p1++];//将p1索引所对应剩下的元素复制到辅助数组中
        }
        //当p1索引值超出索引范围时(即满足p2没有超出索引范围)
        while (p2 <= hi){
            assist[i++] = a[p2++];//将p2索引所对应剩下的元素复制到辅助数组中
        }
        //将已排序的辅助数组赋值给原数组
        for(int index = lo; index <= hi; index++){
            a[index] = assist[index];
        }
    }
    public static boolean less(Comparable x, Comparable y){
        return x.compareTo(y) < 0;
    }
}


🟡空间复杂度


  • 总共分为log8(底数为2)层,即log2(n)


  • 每一层都有2^k个元素(8,4,2)


  • 每个子数组的长度:2^(3-k)(0,2,4)


  • 归并最多需要次数:2^(3-k)


  • 每层归并需要的次数:每一层元素个数*每个子数组的长度:23 =8


  • 总次数:层数 * 每层归并需要的次数:3 * 23 ,即log2(n) * 2log2(n) = log2(n) * n


  • 所以时间复杂度为:O(nlogn)


🟡归并排序缺点


要开辟一个新空间,使得空间复杂度提高,典型的 以时间换空间


🟡结语


归并排序利用了分治思想和递归的思路,所以要先掌握这两个知识点才能掌握这种算法

相关文章
|
1月前
|
算法
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
【算法】二分查找——在排序数组中查找元素的第一个和最后一个位置
|
2月前
|
算法 开发者 Python
惊呆了!Python算法设计与分析,分治法、贪心、动态规划...这些你都会了吗?不会?那还不快来学!
【7月更文挑战第10天】探索编程巅峰,算法至关重要。Python以其易读性成为学习算法的首选。分治法,如归并排序,将大问题拆解;贪心算法,如找零问题,每步求局部最优;动态规划,如斐波那契数列,利用子问题解。通过示例代码,理解并掌握这些算法,提升编程技能,面对挑战更加从容。动手实践,体验算法的神奇力量吧!
60 8
|
1月前
|
搜索推荐 算法 Java
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
该博客文章通过UML类图和Java源码示例,展示了如何使用适配器模式将QuickSort类和BinarySearch类的排序和查找功能适配到DataOperation接口中,实现算法的解耦和复用。
17 1
现有一个接口DataOperation定义了排序方法sort(int[])和查找方法search(int[],int),已知类QuickSort的quickSort(int[])方法实现了快速排序算法
|
30天前
|
算法 搜索推荐 Java
算法实战:手写归并排序,让复杂排序变简单!
归并排序是一种基于“分治法”的经典算法,通过递归分割和合并数组,实现O(n log n)的高效排序。本文将通过Java手写代码,详细讲解归并排序的原理及实现,帮助你快速掌握这一实用算法。
37 0
|
1月前
|
算法 关系型数据库 MySQL
揭秘MySQL中的版本号排序:这个超级算法将颠覆你的排序世界!
【8月更文挑战第8天】在软件开发与数据管理中,正确排序版本号对软件更新及数据分析至关重要。因MySQL默认按字符串排序版本号,可能出现&#39;1.20.0&#39;在&#39;1.10.0&#39;之前的不合理情况。解决办法是将版本号各部分转换为整数后排序。例如,使用`SUBSTRING_INDEX`和`CAST`函数从`software`表的`version`字段提取并转换版本号,再按这些整数排序。这种方法可确保版本号按逻辑正确排序,适用于&#39;major.minor.patch&#39;格式的版本号。对于更复杂格式,需调整处理逻辑。掌握此技巧可有效应对版本号排序需求。
86 3
|
30天前
|
数据采集 搜索推荐 算法
【高手进阶】Java排序算法:从零到精通——揭秘冒泡、快速、归并排序的原理与实战应用,让你的代码效率飙升!
【8月更文挑战第21天】Java排序算法是编程基础的重要部分,在算法设计与分析及实际开发中不可或缺。本文介绍内部排序算法,包括简单的冒泡排序及其逐步优化至高效的快速排序和稳定的归并排序,并提供了每种算法的Java实现示例。此外,还探讨了排序算法在电子商务、搜索引擎和数据分析等领域的广泛应用,帮助读者更好地理解和应用这些算法。
21 0
|
1月前
|
算法 搜索推荐
算法设计 (分治法应用实验报告)基于分治法的合并排序、快速排序、最近对问题
这篇文章是关于分治法应用的实验报告,详细介绍了如何利用分治法实现合并排序和快速排序算法,并探讨了使用分治法解决二维平面上的最近对问题的方法,包括伪代码、源代码实现及时间效率分析,并附有运行结果和小结。
|
1月前
|
存储 算法 Java
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
LeetCode初级算法题:反转链表+统计N以内的素数+删除排序数组中的重复项Java详解
18 0
|
2月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
【7月更文挑战第12天】归并排序是高效稳定的排序算法,采用分治策略。Python 实现包括递归地分割数组及合并已排序部分。示例代码展示了如何将 `[12, 11, 13, 5, 6]` 分割并归并成有序数组 `[5, 6, 11, 12, 13]`。虽然 $O(n log n)$ 时间复杂度优秀,但需额外空间,适合大规模数据排序。对于小规模数据,可考虑其他算法。**
65 4
|
2月前
|
算法 Python
Python算法高手进阶指南:分治法、贪心算法、动态规划,掌握它们,算法难题迎刃而解!
【7月更文挑战第10天】探索Python算法的精华:分治法(如归并排序)、贪心策略(如找零钱问题)和动态规划(解复杂问题)。通过示例代码揭示它们如何优化问题解决,提升编程技能。掌握这些策略,攀登技术巅峰。
62 2