排序算法(五)归并排序

前面我们讲了堆排序,因为它用到了完全二叉树所以效率 比较高。不过堆结构的设计本身是比较复杂的,老实说,能想出这样的结构就挺不容易 , 有没有更直接简单的办法利用完全二叉树来排序呢?当然有。

为了更清晰地说清楚这里的思想,大家来看图 9-8-1 所示,我们将本是无序的数组序列 {16,7,13,10,9,15,3,2,5,8,12,1,11,4,6,14} ,通过两两合并排序后再合井,最终获得了 一个有序的数组。注意仔细观察它的形状,你会发现 , 它像极了一棵倒置的完全二叉树,通常涉及到完全二叉树结构的排序算法,效率一般都不低的一一这就是我们要讲的归并排序法。

1,算法描述

归并排序是创建在归并操作上的一种有效的排序算法,效率为O(nlogn),1945年由冯·诺伊曼首次提出。

归并排序的实现分为递归实现非递归(迭代)实现。递归实现的归并排序是算法设计中分治策略的典型应用,我们将一个大问题分割成小问题分别解决,然后用所有小问题的答案来解决整个大问题。非递归(迭代)实现的归并排序首先进行是两两归并,然后四四归并,然后是八八归并,一直下去直到归并了整个数组。

2,实现步骤

归并排序算法主要依赖归并(Merge)操作。归并操作指的是将两个已经排序的序列合并成一个序列的操作,归并操作步骤如下:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾

  实现

 1 // 递归实现的归并排序(自顶向下(树))
 2     public static void mergeSort(int[] array, int start, int end) {
 3         int len = end - start + 1;
 4         if (len < 2) {
 5             return;
 6         }
 7         int middle = (start + end) / 2;
 8         mergeSort(array, start, middle);
 9         mergeSort(array, middle + 1, end);
10         merge(array, start, middle, end);
11     }
12
13     // 非递归(迭代)实现的归并排序(自底向上(树))
14     public static void mergeSort2(int[] array, int start, int end) {
15         int len = end - start + 1;
16         int mid;     //子数组索引,前一个为a[start...mid],后一个子数组为a[mid+1...end]
17         for(int i = 1; i < len; i *= 2){    // 子数组的大小i初始为1,每轮翻倍
18             start = 0;
19             while(start + i < len){    // 后一个子数组存在(需要归并)
20                 mid = start + i - 1;
21                 end = (mid + i < len)  ? (mid + i) : (len - 1);//后一个子数组大小可能不够
22                 merge(array, start, mid, end);    //递归法的时候可以不往merge()函数中传mid,但非递归法的时候必须传mid,因非递归方法mid不一定是((start + end)/2)
23                 start = end + 1;    // 前一个子数组索引向后移动
24             }
25         }
26     }
27
28     //归并函数
29     // 合并两个已排好序的数组a[start...mid]和A[mid+1...end]
30     private static void merge(int[] array, int start, int mid, int end) {
31         int[] temp = new int[end - start + 1]; //辅助空间O(n)
32         int left = start;     // 前一数组的起始元素
33         int right = mid + 1; // 后一数组的起始元素
34         int point = 0;
35         while (left <= mid && right <= end) {
36             if (array[left] <= array[right]) {// 带等号保证归并排序的稳定性
37                 temp[point++] = array[left++];//++在后面,表示先运算,最后加1,不是先加1在运算
38             } else {
39                 temp[point++] = array[right++];
40             }
41         }
42         while (left <= mid) {            //如果左指针还没到mid(即右指针已经遍历完右序列)
43             temp[point++] = array[left++];
44         }
45         while (right <= end) {            //如果右指针还没到mid(即左指针已经遍历完左序列)
46             temp[point++] = array[right++];
47         }
48         for (int i = 0; i < temp.length; i++) {//把暂存数组里排好序的元素 写回到原数组
49             array[i + start] = temp[i];
50         }
51     }

左侧中间的50,10也是分成单个元素序列然后再归并,只是这里没画出来。

3,算法分析

时间复杂度:

最差时间复杂度 ---- O(nlogn)

最优时间复杂度 ---- O(nlogn)

平均时间复杂度 ---- O(nlogn)

空间复杂度:

空间复杂度为O(n)

稳定性:稳定

4,应用

归并排序除了可以对数组进行排序,还可以高效的求出数组小和(即单调和)以及数组中的逆序对https://www.jianshu.com/p/3ab5033074f1

参考:

《大话数据结构》

http://www.cnblogs.com/eniac12/p/5329396.html

原文地址:https://www.cnblogs.com/xdyixia/p/9146384.html

时间: 2024-10-14 10:17:30

排序算法(五)归并排序的相关文章

排序算法系列——归并排序

记录学习点滴,菜鸟成长记 归并排序的英文叫做Merge-Sort,要想明白归并排序算法,还要从“递归”的概念谈起. 1.递归 一般来讲,人在做决策行事的时候是往往是从已知出发,比如,我又要举个不恰当的例子了→_→: 看到漂亮姑娘→喜欢人家→追→女朋友→老婆 但是人家施瓦辛格不是这么想的,人家从小就立志当总统: 要当总统←先当州长←竞选州长要有钱←那得找个有钱妹子←妹子都喜欢明星←身材好能当明星←健身 递归,就像一个人对自己的发展有清晰的规划和坚定的信心一样,他知道每一步会有怎么样的结果,他需要仅

常用排序算法之——归并排序

归并排序的原理: 如果数组的元素个数大于1,则: 将数组平均分为两部分: 左边的数组归并排序:递归 右边的数组归并排序:递归 将两个各自有序的数组合并,需要一个额外的辅助数组,暂时保存合并结果:返回 否则,数组元素个数为1时,已经有序:直接返回. 稳定排序.时间复杂度在最坏.最好.平均情况下都为O(N lgN),空间复杂度为O(N). 代码: 1 #include <iostream> 2 using namespace std; 3 4 template<typename T>

Java常见排序算法之归并排序

在学习算法的过程中,我们难免会接触很多和排序相关的算法.总而言之,对于任何编程人员来说,基本的排序算法是必须要掌握的. 从今天开始,我们将要进行基本的排序算法的讲解.Are you ready?Let‘s go~~~ 1.排序算法的基本概念的讲解 时间复杂度:需要排序的的关键字的比较次数和相应的移动的次数. 空间复杂度:分析需要多少辅助的内存. 稳定性:如果记录两个关键字的A和B它们的值相等,经过排序后它们相对的位置没有发生交换,那么我们称这个排序算法是稳定的. 否则我们称这个排序算法是不稳定的

我的Java开发学习之旅------&amp;gt;Java经典排序算法之归并排序

一.归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是採用分治法(Divide and Conquer)的一个很典型的应用.将已有序的子序列合并,得到全然有序的序列.即先使每一个子序列有序.再使子序列段间有序.若将两个有序表合并成一个有序表.称为二路归并. 归并过程为:比較a[i]和a[j]的大小.若a[i]≤a[j],则将第一个有序表中的元素a[i]拷贝到r[k]中,并令i和k分别加上1.否则将第二个有序表中的元素a[j]拷贝到r[k]中,并令j和k分别加上1.如此循环下去.直

我的Java开发学习之旅------&gt;Java经典排序算法之归并排序

一.归并排序 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用.将已有序的子序列合并,得到完全有序的序列:即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为二路归并. 归并过程为:比较a[i]和a[j]的大小,若a[i]≤a[j],则将第一个有序表中的元素a[i]复制到r[k]中,并令i和k分别加上1:否则将第二个有序表中的元素a[j]复制到r[k]中,并令j和k分别加上1,如此循环下去,直

排序算法之归并排序(Mergesort)解析

一.归并排序的优缺点(pros and cons) 耗费心思来理解它,总要有个理由吧: 归并排序的效率达到了巅峰:时间复杂度为O(nlogn),这是基于比较的排序算法所能达到的最高境界 归并排序是一种稳定的算法(即在排序过程中大小相同的元素能够保持排序前的顺序,3212升序排序结果是1223,排序前后两个2的顺序不变),这一点在某些场景下至关重要 归并排序是最常用的外部排序方法(当待排序的记录放在外存上,内存装不下全部数据时,归并排序仍然适用,当然归并排序同样适用于内部排序...) 缺点: 归并

排序算法 之 归并排序

归并排序也是基于分治思想的一种排序算法,是通过对两个或两个以上的有序序列合并来实现的,对两个序列合并的叫两路归并,对两个以上序列合并的叫多路归并.归并排序的时间复杂度也为O(N*logN).下面来看一下两路归并的实现: 基本思想:归并排序时先找出序列的中间元素把序列分解为两个子序列,对子序列重复这个过程直至把序列分解成为只包含单个元素的序列,然后把相邻的序列两两合并使之有序,重复两两合并直至合并成为一个序列归并结束序列有序. 代码实现: /// <summary> /// 归并排序 /// &

八大排序算法之七-归并排序

归并类的排序算法 归并:将两个或两个以上的有序表组合成一个新的有序表. 内部排序中,通常采用的是 2-路归并排序.即:将两个位置相邻的记录有序子序列归并为一个记录有序的序列.归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 图解如下 看成是 n 个有序的子序列(长度为 1),然后两两归并. 得到 n/2 个长度为2 或 1 的有序子序列.继续亮亮归并 最后一趟 代码如下: #include<iostream> #i

指针实现时间复杂度为O(n*logN)的排序算法(归并排序算法)

归并排序 1 /** 2 * Definition for singly-linked list. 3 * struct ListNode { 4 * int val; 5 * ListNode *next; 6 * ListNode(int x) : val(x), next(NULL) {} 7 * }; 8 */ 9 class Solution { 10 public: 11 ListNode* sortList(ListNode* head) { 12 //排序算法 空间复杂度N*lo

Java实现排序算法之归并排序

一.综述 归并排序(Merge sort,台湾译作:合并排序)是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 算法描述 归并操作的过程如下: 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 设定两个指针,最初位置分别为两个已经排序序列的起始位置 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 重复步骤3直到某一指针达到序列尾 将另一序列剩下的所有元素直接复制到合并序列尾