基础排序之归并排序

Start

前言

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。《百度百科》

1. 归并排序

归并过程为:比较 a[i] 和 b[j] 的大小,若 a[i] ≤ b[j],则将第一个有序表中的元素 a[i] 复制到 r[k] 中,并令 i 和 k 分别加上 1;否则将第二个有序表中的元素 b[j] 复制到 r[k] 中,并令 j 和 k 分别加 上1,如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标 k 到下标 t 的单元。归并排序的算法我们通常用递归实现,先把待排序区间 [s,t] 以中点二分,接着把左边子区间排序,再把右边子区间排序,最后把左区间和右区间用一次归并操作合并成有序的区间 [s,t]。

原理:

归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾

总结:

  • 将两个已排好序的数组合并成一个有序的数组,称之为归并排序
  • 步骤:遍历两个数组,比较它们的值。谁比较小,谁先放入大数组中,直到数组遍历完成

1. 演算归并排序过程

现在我有两个已经排好顺序的数组:int[] arr1 = {2, 7, 8}和int[] arr2 = {1, 4, 9},我还有一个大数组来装载它们int[] arr = new int[6];

1.1 第一轮

那么,我将两个数组的值进行比较,谁的值比较小,谁就放入大数组中!

首先,拿出 arr1[0] 和 arr2[0] 进行比较,显然是 arr2[0] 比较小,因此将 arr2[0] 放入大数组中,同时 arr2 指针往后一格。

所以,现在目前为止arr = {1}。

1.2 第二轮

随后,拿 arr1[0] 和 arr2[1] 进行比较,显然是 arr1[0] 比较小,将 arr1[0] 放入大数组中,同时 arr1 指针往后一格。

所以,现在目前为止arr = {1,2}。

1.3 第三轮

随后,拿 arr1[1] 和 arr2[1] 进行比较,显然是 arr2[1] 比较小,将 arr2[1] 放入大数组中,同时 arr2 指针往后一格。

所以,现在目前为止 arr = {1,2,4}。

……..

遍历到最后,我们会将两个已排好序的数组变成一个已排好序的数组 arr = {1,2,4,7,8,9}。

2. 归并排序前提分析(分治法)

从上面的演算我们就直到,归并排序的前提是需要两个已经排好顺序的数组,那往往不会有两个已经排好顺序的数组给我们的呀(一般是杂乱无章的一个数组),那这个算法是不是很鸡肋的呢??

其实并不是的,首先假设题目给出的数组是这样子的:int[] arr = {2, 7, 8, 1, 4, 9};

当我们要做归并的时候就以 arr[3] 也就元素为 1的 那个地方分开。是然后用一个指针 L 指向 arr[0],一个指针 M 指向 arr[3],用一个指针 R 指向 arr5。有了指针的帮助,我们就可以将这个数组切割成是两个有序的数组了(操作的方式就可以和上面一样了)。

可是上面说了,一般给出的是杂乱无章的一个数组,现在还是达不到要求。比如给出的是这样一个数组:int[] arrays = {9, 2, 5, 1, 3, 2, 9, 5, 2, 1, 8};

此时,我们就得用到分治的思想了:

那么我们也可以这样想将 int[] arr = {2, 7, 8, 1, 4, 9};数组分隔成一份一份的,arr[0] 它是一个有序的"数组",arr[1] 它也是一个有序的"数组",利用指针 (L,M,R) 又可以像操作两个数组一样进行排序。最终合成{2,7}…….再不断拆分合并,最后又回到了我们的arr = {1,2,4,7,8,9},因此归并排序是可以排序杂乱无章的数组的。
这就是我们的分治法--->将一个大问题分成很多个小问题进行解决,最后重新组合起来。

3. 归并代码实现

实现步骤:

  • 拆分
  • 合并
/**
 * 归并排序
 * 分解待排序的数组成两个各具 n/2 个元素的子数组,递归调用归并排序两个子数组,合并两个已排序的子数组成一个已排序的数组
 */
public class MergeSort {

    public static void main(String[] args) {
        int[] sort = SortManager.sortArr;
        int[] temp = new int[sort.length];

        mergeSort(sort, temp, 0, sort.length - 1);

        for (int i = 0; i < sort.length; i++) {
            System.out.println("输出结果:" + sort[i]);
        }
    }

    /**
     * 归并排序
     *
     * @param arrays
     * @param L      指向数组第一个元素
     * @param R      指向数组最后一个元素
     */

    public static void mergeSort(int[] arrays, int[] temp, int L, int R) {
        // 当left == right时,不需要再划分
        if (L < R) {
            //取中间的数,进行拆分
            int M = (L + R) / 2;

            //左边的数不断拆分
            mergeSort(arrays, temp, L, M);

            //右边的数不断拆分
            mergeSort(arrays, temp, M + 1, R);

            //合并
            merge(arrays, temp, L, M, R);
        }

    }

    /**
     * 合并数组
     *
     * @param arrays
     * @param L      指向数组第一个元素
     * @param M      指向数组分隔的元素
     * @param R      指向数组最后的元素
     */
    private static void merge(int[] arrays, int[] temp, int L, int M, int R) {

        int i = L, j = M + 1;
        // arrays数组的第一个元素
        int k = 0;

        //比较这两个数组的值,哪个小,就往数组上放
        while (i <= M && j <= R) {
            temp[k++] = arrays[i] < arrays[j] ? arrays[i++] : arrays[j++];
        }

        //如果左边的数组还没比较完,右边的数都已经完了,那么将左边的数抄到大数组中(剩下的都是大数字)
        while (i <= M) {
            temp[k++] = arrays[i++];
        }

        //如果右边的数组还没比较完,左边的数都已经完了,那么将右边的数抄到大数组中(剩下的都是大数字)
        while (j <= R) {
            temp[k++] = arrays[j++];
        }

        // 把temp数据复制回原数组
        for (i = 0; i < k; i++) {
            arrays[L + i] = temp[i];
        }
    }
}

参考文章:https://mp.weixin.qq.com/s?__biz=MzI4Njg5MDA5NA==&mid=2247484058&idx=1&sn=432c2dd8e4bda662ce066c09f8e22bda&chksm=ebd7439bdca0ca8ded40d0f431db411928936db9b4b5f5595027c8acd2efdef5ba35348641d2#rd

申明:开始的图片来源网络,侵删

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容

  • 在Java常见排序基础 - 上中主要介绍了冒泡排序、选择排序、插入排序三种基础排序,本篇文章主要介绍的是 快速排序...
    骑小猪看流星阅读 996评论 0 13
  • 归并排序就这么简单 从前面已经讲解了冒泡排序、选择排序、插入排序,快速排序了,本章主要讲解的是归并排序,希望大家看...
    Java3y阅读 538评论 2 6
  • functionswap(arr,i,j){ vartemp=arr[i]; arr[i]=arr[j]; arr...
    sweetytang阅读 337评论 0 1
  • 今天换了个手机,安装简书APP时提示输入性别和生日,没多想就随手填了,进入发现可以领10个钻。 错过了新手任务的老...
    文介阅读 950评论 4 52
  • 天道是损有余,补不足 人道是补有余,损不足 尊重其规律,按势而行。 道德,道理,是非对错皆偏向于能量~
    李娜_b39c阅读 72评论 0 0