W. :合并排序

来源:互联网 发布:淘宝哪些代购店是真的 编辑:程序博客网 时间:2024/05/21 08:01

//author: W.
//合并排序. 2.3-2 P22
//时间复杂度:O(nlgn)

#include <stdio.h>
#include <stdlib.h>
//#define USE_CONTINUE
void merge(int a[], int p, int q, int r)
{
    int n1 = q - p + 1;
    int n2 = r - q;
    int* L = (int*)malloc(sizeof(int) * n1);
    int* R = (int*)malloc(sizeof(int) * n2);
    int i;
    int j;
    int k;
    for(i = 0; i < n1; ++i)
    {
        L[i] = a[p+i];
    }
    for(j = 0; j < n2; ++j)
    {
        R[j] = a[q+1+j];
    }
    i = 0; //循环不变式初始化:L[i]和R[i]分别是L与R中最小值,新的a中加入的元素个数为0,即加入的元素是L和R中的最小元素(还没有元素呢)
    j = 0;
    for(k = p; k <= r; ++k)//保持:L[i]和R[i]分别是L与R中最小值,新的a中加入的元素是L和R中的最小元素,即a中已加入元素都是小于L和R中的元素,在完成++k后,a[p]~a[k-1]为已排序
    {
#ifdef USE_CONTINUE  //使用该方式的好处是在循环不变式的主循环中会在循环结束后得到结果,即对循环不变式的终止情况判断清晰
        //但这种方式会导致加入L或R剩余的元素时,每次都要多一次i == n1或 j == n2的判断
        if(i == n1) //使用在主循环中处理完所有操作,注意由于n1 + n2 == r - p + 1,所以这里无需判断j < n2
        {
            a[k] = R[j];
            ++j;
            continue;
        }
        else if(j == n2)
        {
            a[k] = L[i];
            ++i;
            continue;
        }
#endif
#ifndef USE_CONTINUE //如果L或R中某个数组读取完毕,则跳出循环,把剩余的加入到a[k]剩下的空间中
        //但这种方式要求循环不变式的终止要推迟到外面的另一个循环完毕。
        if((i == n1) || (j == n2))
        {
            break;
        }
#endif
        if(L[i] <= R[j])
        {
            a[k] = L[i];
            ++i;
        }
        else
        {
            a[k] = R[j];
            ++j;
        }
    }
#ifndef USE_CONTINUE
    if(k <= r)
    {
        if(i == n1)
        {
            for(; k <= r; ++k)
            {
                a[k] = R[j];
                ++j;
            }
        }
        else if(j == n2)
        {
            for(; k <= r; ++k)
            {
                a[k] = L[i];
                ++i;
            }
        }
    }
#endif
    //无论采用哪种方式,此时循环不变式到此终止,即k此时为r+1,则a[p]~a[r]已排序完毕
    free(L);
    free(R);
}

void merge_sort(int a[], int p, int r)
{
    //分治法:
    int q;
    if(p < r)
    {
        q = (p + r) / 2; //向下取整  //分解:分解成子问题,即分解成2部分子序列
        merge_sort(a, p, q); //解决:递归地解各个子问题。
        merge_sort(a, q+1, r);
        merge(a, p, q, r); //合并:将子问题的结果合并成,即将2部分子序列通过merge函数进行合并。
    }
    //当p >= r时,此时a[p] ~ a[r]至多只有一个元素,则处于已排序状态   //解决:若子问题足够小,直接求解。即这里已排序,无需做任何操作
}

void test_sort()
{
    int a[] = { 5, 2, 4, 6, 1, 3, 10, 6, 7, 9, 8};
    int i;
    for(i = 0; i < sizeof(a)/sizeof(int); ++i)
    {  
        printf("%d ", a[i]);
    }  
    printf("/n");

    merge_sort(a, 0, sizeof(a)/sizeof(int) - 1);
   
    for(i = 0; i < sizeof(a)/sizeof(int); ++i)
    {  
        printf("%d ", a[i]);
    }  
    printf("/n");
}


int main(int argc, char** argv)
{
    test_sort();
    return 0; 
}

//输出:
//5 2 4 6 1 3 10 6 7 9 8
//1 2 3 4 5 6 6 7 8 9 10