【经典算法】归并排序

  归并排序是以O(NlogN)最坏情形运行时间运行,而所使用的比较次数几乎是最优的。它是递归算法的一个很好的实例。

  归并排序的也遵循分治的思想。直观上其操作如下:

  分解:分解待排序的n个元素的序列成各具n/2个元素的子序列。

  解决:使用归并排序递归地排序两个子序列。

  合并:合并两个已排序的子序列以产生已排序的答案。

  当待排的序列长度为1时,递归“开始回升”,在这种情况下不要做任何工作,因为长度为1的每个序列都已排好序。

  归并排序算法的关键操作是“合并步骤”中两个已排序序列的合并。我们通过调用一个辅助过程Merge(A,p,q,r)来完成合并,其中A是一个数组,p,q,r是数组下标,满足p≤q<r。该过程假设子数组A[p,...,q]和A[q+1,...,r]都已经排好序。它合并这两个子数组形成单一的已排好序的子数组并代替当前的子数组A[p,...,r]。

  过程Merge的思想如下:取两个已排序的输入数组A[p,...,q]和A[q+1,...,r]以及一个临时输出数组B,令i=p,表示的是A[p,...,q]的索引;j=q+1,表示的是A[q+1,...,r]的索引;k=0,表示的是临时输出数组B的的索引。A[i]和B[j]的较小者被复制到B的下一个位置,相关计数器向前推进一步。当两个输入表有一个用完的时候,则将领一个表中的剩余部分拷贝到C中。

  代码如下:

  

void Merge(int *, int, int, int);

void MergeSort(int A[], int p, int r) {
    if (p < r) {
        int q = (p + r) / 2;
        MergeSort(A, p, q);
        MergeSort(A, q + 1, r);
        Merge(A, p, q, r);
    }
}

void Merge(int A[], int p, int q, int r) {
    int *B = new int[r - p + 1];
    int i = p, j = q + 1, k = 0;
    while (i <= q && j <= r) {
        if (A[i] <= A[j]) {
            B[k++] = A[i++];
        } else {
            B[k++] = A[j++];
        }
    }

    while (i <= q) {
        B[k++] = A[i++];
    }

    while (j <= r) {
        B[k++] = A[j++];
    }

    for (k = 0; k < r - p + 1; k++)
        A[p + k] = B[k];

    delete []B;
}

  虽然归并排序的运行时间是O(NlogN),但是它很难用于主存排序,主要问题在于合并两个排序的表需要线性附加内存,在整个算法中还要花费将数据复制到临时数组再复制回来这样一些附加的工作,其结果是严重减慢了排序的速度。

  与其他的O(nlogn)排序相比,归并排序的运行时间很大程度上依赖于在数组中进行元素的比较和移动所消耗的时间。这些消耗是和编程语言相关的。

  例如,在其他语言(如Java)中,当排序一般的对象时,元素的比较耗时很多,但是移动元素就快的多。在所有流行的排序算法中,归并排序使用最少次数的比较。因此,在java中,归并排序是一般目的排序的最佳选择。事实上,在标准Java库中的一般排序就是用的这种算法。

  另一方面,在C++中,对于一般排序,当对象很大时,复制对象的代价是很大的,而对象的比较通常相对消耗小些。这是因为编译器在处理函数模板的模板时具有强大的执行在线优化的能力。

时间: 2024-10-26 15:09:02

【经典算法】归并排序的相关文章

[经典算法] 归并排序

题目说明: 归并排序是建立在归并操作上的一种有效的排序算法.该算法也是采用分治法(Divide and Conquer)的一个非常典型的应用.算法复杂度为O(N*logN). 题目解析: 归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列. 归并排序包括两个步骤: 1)划分子表 2)合并半子表 伪代码: 合并排序伪代码(使用哨兵): merge(A,p,q,r): n1 <-- q-p+1 n2 <-

经典排序算法 - 归并排序Merge sort

经典排序算法 - 归并排序Merge sort 原理,把原始数组分成若干子数组,对每个子数组进行排序, 继续把子数组与子数组合并,合并后仍然有序,直到所有合并完,形成有序的数组 举例 无序数组[6 2 4 1 5 9] 先看一下每一个步骤下的状态,完了再看合并细节 第一步 [6 2 4 1 5 9]原始状态 第二步 [2 6] [1 4] [5 9]两两合并排序,排序细节后边介绍 第三步 [1 2 4 6] [5 9]继续两组两组合并 第四步 [1 2 4 5 6 9]合并完成,排序完成 输出结

白话经典算法系列之五 归并排序的实现(转)

归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 首先考虑下如何将将二个有序数列合并.这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数.然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可. //将有序数组a[]和b[]合并到c[]中 void MemeryArray(int a[], int n, int b[], int m, int c[]) { int

白话经典算法系列之九 从归并排序到数列的逆序数对(微软笔试题)

首先来看看原题 微软2010年笔试题 在一个排列中,如果一对数的前后位置与大小顺序相反,即前面的数大于后面的数,那么它们就称为一个逆序数对.一个排列中逆序的总数就称为这个排列的逆序数.如{2,4,3,1}中,2和1,4和3,4和1,3和1是逆序数对,因此整个数组的逆序数对个数为4,现在给定一数组,要求统计出该数组的逆序数对个数. 计算数列的逆序数对个数最简单的方便就最从前向后依次统计每个数字与它后面的数字是否能组成逆序数对.代码如下: #include <stdio.h> int main()

经典算法之归并排序——python和JS实现

前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:韩忠康 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取http://t.cn/A6Zvjdun 算法 归并排序(merge-sort),典型的分治策略(divide and conquer).核心思路是将整体序列一分为二形成两个子序列,分别对子序列排序,再将两个有序子序列合并成一个有序序列. 思路如图: 整体过程分为拆分和合并两大阶段. 拆分,核

动态展示十大经典算法

算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序n个项目要Ο(nlogn)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(innerloop)可以在大部分的架构上很有效率地被实现出来. 快速排序使用分治法(Divideandconquer)策略来把一个串行(list)分为两个子串行(sub-lists). 算法步骤: 1.从数列中挑出一个元素,称为“基准”(pivot),

白话经典算法系列之七 堆与堆排序

堆排序与高速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先解说下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是全然二叉树或者是近似全然二叉树. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)不论什么一个子节点的键值. 2.每一个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 当父结点的键值总是大于或等于不论什么一个子节点的键值时为最大堆.当父结点的键值总是小于或等于不论什么一个子节点的键值时为最小堆.下图展示一个最小堆

python数据结构与算法——归并排序

归并排序: 原理与C语言实现 参考:白话经典算法系列之五 归并排序的实现 1. 容易对有序数组A,B进行排序. 2. 为了使得A,B组内数据有序:可以将A,B组各自再分成二组. 3. 经过不断分组,当分出来的小组只有一个数据时(有序),合并相邻二个小组. 这样通过先递归的分解数列,再合并数列就完成了归并排序. 代码摘自<Python Algorithm> 1 # 对数组seq进行归并排序 2 # 返回排序后数组 3 def mergesort(seq): 4 mid = len(seq)//2

经典算法宝典——分治思想(四)(1)

分治法(Divide and Conquer)的设计思想是,将一个难以直接解决的大问题,分割成一些规模较小的几个相似问题,以便各个击破,分而治之. 说明: 分治策略的应用很广,具体表现形式各异,比如:折半查找.合并排序.快速排序.二叉树遍历(先遍历左子树再遍历右子树).二叉排序树的查找等算法. 一.分治算法框架 1.算法设计思想 分治法求解问题的过程是,将整个问题分解成若干个小问题后分而治之.如果分解得到的子问题相对来说还太大,则可反复使用分治策略将这些子问题分成更小的同类型子问题,直至产生出方

白话经典算法系列之七 堆与堆排序(转)

堆排序与快速排序,归并排序一样都是时间复杂度为O(N*logN)的几种常见排序方法.学习堆排序前,先讲解下什么是数据结构中的二叉堆. 二叉堆的定义 二叉堆是完全二叉树或者是近似完全二叉树. 二叉堆满足二个特性: 1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值. 2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆). 当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆.当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆.下图展示一个最小堆: 由于其它几