算法 -- 归并排序之自然排序

定义:
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个典型应用.应该将已经有序的子序列合并,得到完全有序的序列:即先使每个子序列有序,再使子序列段间有序.若将两个有序表合并成一个有序表,称为二路归并. [摘录于维基百科]

无论哪一种归并算法的实现,它都需要用到一个MergeArray函数实现两个有序数组的合并.如下:
/**
 * 首先定义一个新数组b存储合并后的数据信息,具体的合并过程即架循环遍历
 * 两个数组,如果前一部分数组元素小于后一部分数组元素则i++(前数组的循环
 * 变量),否则j++(后数组的循环变量).当有一个数组遍历结束时,跳出循环.
 * 跳出之后,如果另一个数组没有遍历结束,则将其剩余元素存储在p数组中,
 * 否则跳过.
 **/
void MergeArray ( int a[] , int left , int mid , int right )
{
    // 循环变量i,j分别为两部分数组的检测指针
    int i = left , j = mid + 1 , k = 0 , m , p ; 
    int b[N] ;

    // 其中任意一个数组访问结束,则循环结束
    while ( i <= mid && j <=right ) {
        if ( a[i] <= a[j] ) {
            // 前一个数组当前元素不大于后一个数组,则i++
            b[k++] = a[i++] ;
        } else {
            // 否则j++
            b[k++] = a[j++] ;
        }
    }

    // 前一个数组没有遍历结束则继续循环,使其存储在b数组中
    while ( i <= mid ) {
        b[k++] = a[i++] ;
    }

    // 后一个数组没有遍历结束则继续循环,使其存储在b数组中
    while ( j <= right ) {
        b[k++] = a[j++] ;
    }
}

因为递归式的归并排序实现比较简单,在这里我就不再介绍. 因此下面主要分享非递归方法的实现,分别是:一般排序和自然排序.

A. 非递归归并排序の一般排序: 
        从小的部分实现,首先是一个元素为一组,每次以2的倍数增长,每一次分组后两个小组为一组进行合并即调用MergeArray合并函数实现合并,其实就是"从小到大"的思想,与递归的思想刚好相反.(其中有的细节可以参考代码,重点是理解代码中的注释)
/*************************************************************************
 **     >  Name : FDG_Combine.c
 **     > Author: LiYingXiao (Sweethreart502) 
 **     >  Mail : liyingxiao502@gmail.com
 **     >  Blog : http://blog.csdn.net/u013166575
 **     > Created Time: 2015年10月7日 星期一 20时34分15秒
 ************************************************************************/
#include <iostream>

#define Max 5

// 数组合并算法
void MergeArray ( int * a , int left , int middle , int right , int n ) 
{
    int * p = new int[n] ;

    int i = left , j = middle + 1 , k = 0 ;

    // 架循环使得两个数组按照大小顺序存储在p数组中
    while ( i <= middle && j <= right ) {
        if ( a[i] < a[j] ) {
            p[k++] = a[i++] ;
        } else {
            p[k++] = a[j++] ;
        }
    }

    while ( i <= middle ) {
        p[k++] = a[i++] ;
    }

    while ( j <= right ) {
        p[k++] = a[j++] ;
    }

    // 将合并后的信息赋值给原数组
    for ( i = 0 ; i < n ; i++ ) {
        a[left+i] = p[i] ;
    }

    delete [] p ;
}

// 归并排序算法
void MergeSort ( int * a , int n )
{
    int length = 1 ;
    int begin ;

    for ( length = 1 ; length < n ; length *= 2 ) {
        //  每一次的分组划分合并处理,都是从下标0开始的
        begin = 0 ;
        while ( ( begin + 2 * length ) < n ) {
            // 调用合并函数,实现两两小数组合并
            MergeArray ( a , begin , ( 2*begin + 2*length -1 ) / 2 , begin + 2*length - 1 , 2*length ) ;
            // 循环到下一对合并的小数组
            begin += 2*length ;
        } 

        // 如果剩下的长度不足够2*length但begin+length<n,则继续将其合并
        if ( ( begin + length ) < n ) {
            MergeArray ( a , begin , begin + length - 1 , n - 1 , n ) ;
        }

    }
}

// 主函数
int main (  ) 
{
    int array[Max] = { 0 } ;

    std::cout << "Please input the " << Max << " elements of the array : " << std::endl ;
    for ( int i = 0 ; i < Max ; i++ ) {
        std::cin >> array[i] ;
    }

    // 调用归并排序函数
    MergeSort ( array , Max ) ;

    // 排序结束后的数组进行输出展示
    std::cout << std::endl << "The sorted array is : " << std::endl ;
    for ( int i = 0 ; i < Max ; i++ ) {
        std::cout << array[i] << " " ;
    }

    std::cout << std::endl ;

    return 0 ;
}
B. 非递归归并排序の自然归并:    
     自然合并的核心主要是一个Pass函数,这个函数中设置了一个array数组,来存放每一组有序元素的起始元素的下标,最后再将最后一个元素的下标+1存放为array数组的最后一个元素,这样,在后面的合并实现中会显现出这样记录的原因.(其中具体实现可以参考代码,重点是理解代码中的注释)
/*************************************************************************
 **     >  Name : Nature_Combine.c
 **     > Author: LiYingXiao (Sweethreart502) 
 **     >  Mail : liyingxiao502@gmail.com
 **     >  Blog : http://blog.csdn.net/u013166575
 **     > Created Time: 2015年10月7日 星期一 20时34分15秒
 ************************************************************************/
#include <iostream>
#define N 10

// 定义全局数组,记录每个子数组的其实坐标
int array[N] ;

// 两个数组合并函数
void MergeArray ( int a[] , int left , int right , int mid )
{
    // 循环变量i,j分别为两部分数组的检测指针
    int i = left , j = mid + 1 , k = 0 , m , p ; 

    int b[N] ;

    while ( i <= mid && j <=right ) {
        if ( a[i] <= a[j] ) {
            b[k++] = a[i++] ;
        } else {
            b[k++] = a[j++] ;
        }
    }

    while ( i <= mid ) {
        b[k++] = a[i++] ;
    }

    while ( j <= right ) {
        b[k++] = a[j++] ;
    }

    for ( p = 0 , m = left ; m <= right ; p++ , m++ ) {
        a[m] = b[p] ;
    }
}

// 扫描算法(这个函数是理解的关键!!!)
int Pass ( int a[] , int n )  
{
    int num = 0 , i ;
    int biger = a[0] ;
    array[num++] = 0 ;  // 将全局数组下标为0的值记为0

    for ( i = 1 ; i < n ; i++ ) {
        if ( a[i] >= biger ) {
            biger = a[i] ;
        } else {
            array[num++] = i ;
            biger = a[i] ;
        }
    }

    // array数组存储每一组有序的数组的起始元素的下标,同时存储最后一个元素的下标+1,为了方便最后一组元素的合并二需要!!!
    array[num++] = N ;

    return num ;        // num此时为数组最后一个元素下标+1
}

// 归并排序的自然实现
void MergeSort ( int a[] , int n , int left , int right ) 
{
    int i ; 

    int num = Pass ( a , n ) ;

    while ( num != 2 ) {
        // num = 2 说明已经排序完成即只存储了下标为1的元素
        // 每循环一次,执行一次pass函数

        for ( i = 0 ; i < num ; i += 2 ) {
        // array[i]即合并数据的起点; array[i+2]后一组的后面一组的起点-1即合并数据的终点; array[i+1]后一组的起点-1即合并数据的中点
            MergeArray ( a , array[i] , array[i+2] - 1 , array[i+1] - 1 ) ;

//            num = Pass ( a, n ) ;
        }
    num = Pass ( a , n ) ;
    }
}

// 主函数
int main (  )
{
    int a[N] ;
    int i ;

    std::cout << "Printf input the num :" << std::endl ;
    for ( i = 0 ; i < N ; i++ ) {
        std::cin >> a[i] ;
    }

    MergeSort ( a , N , 0 ,N - 1 ) ;

    std::cout << "Output the num :" << std::endl ;
    for ( i = 0 ; i < N ; i++ ) {
        std::cout << a[i] << " " ;
    }

    std::cout << std::endl ;

    return 0 ;
}
 以上就是我今天所要总结的归并排序算法的实现.欢迎大家互相讨论,提出意见.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值