归并 与 多路归并


问题1: 实现归并排序

 

归并排序原理:如果两个序列已经排好序,在O(m+n)时间复杂度内,通过扫描即可完成两个序列的合并,由此可以得到归并排序的递归实现

void Merge(int *src, int lstart, int lend, int rend)
{
    int rstart = lend + 1;
    int *buffer = new int[rend - lstart + 1];
    int lPosition = lstart;
    int rPosition = rstart;
    int bPosition = 0;

    while (lPosition <= lend && rPosition <= rend)
        if (src[lPosition] < src[rPosition])
            buffer[bPosition++] = src[lPosition++];
        else
            buffer[bPosition++] = src[rPosition++];
    while (lPosition <= lend)
        buffer[bPosition++] = src[lPosition++];
    while (rPosition <= rend)
        buffer[bPosition++] = src[rPosition++];

    bPosition = 0, lPosition = lstart;
    while (lPosition <= rend)
        src[lPosition++] = buffer[bPosition++];
    
    delete []buffer;
}

void MergeSort(int *src, int start, int end)
{
    // 跳出条件
    if (start >= end) return;
    // 递归关系式:
    //     MergeSort使一个数组有序
    //     所以只要用MergeSort对左右两部分有序化
    //     然后调用Merge函数合并数组的左右两部分,即可完成排序
    int middle = (start + end) / 2;
    MergeSort(src, start, middle);
    MergeSort(src, middle + 1, end);
    Merge(src, start, middle, end);
}

 

 

问题2:有两个有序的int array A和B,其中B的长度足以保存所有A和B的数据,如何将这两个array合并?样例 A {2, 5, 6} B {3, 4, NULL, NULL, NULL}

 

通过查看归并排序中的Merge函数,不难发现合并两个数组的做法:不同的是,因为B中有空余的空间,只要从大到小进行归并即可

void MergeValue(int *a, int *b, int aNumberCount, int bNumberCount)
{
    if (a == NULL || b == NULL) return;
    int insertPosition = aNumberCount + bNumberCount - 1;
    int aPosition = aNumberCount - 1;
    int bPosition = bNumberCount - 1;
    while (aPosition >= 0 && bPosition >= 0)
        if (a[aPosition] > b[bPosition])
            b[insertPosition--] = a[aPosition--];
        else
            b[insertPosition--] = b[bPosition--];

    while (aPosition >= 0)
        b[insertPosition--] = a[aPosition--];
    // why here need not move b ?
}


 

问题3:对N个有序数组进行排序,如:

A = {5, 8, 15, 25, 36}

B = {0, 4, 18, 50}

C = {3, 7, 20, 30, 45}

D = {1, 3, 5, 6, 8}

 

这是一个多路归并问题,每次比较对每个数组中现存的最小数字进行比较即可

void MergeAll(int (*src)[5], int *rowSize, int col)
{
    const int INTMAX = 0x7fffffff;
    int i, j;
    int totalSize = 0;
    for (i = 0; i < col; ++i)
        totalSize += rowSize[i];
    int *resultArray = new int[totalSize];

    int *rowPosition = new int[col];
    for (i = 0; i < col; ++i)
        rowPosition[i] = 0;

    int resultPosition = 0;
    int minPosition, minValue;
    while (resultPosition < totalSize)
    {
        minValue = INTMAX;
        for (i = 0; i < col; ++i)
        {
            if (rowPosition[i] < rowSize[i] && src[i][rowPosition[i]] < minValue)
            {
                minPosition = i;
                minValue = src[i][rowPosition[i]];
            }
        }
        resultArray[resultPosition++] = src[minPosition][rowPosition[minPosition]++];
    }

    for (i = 0; i < totalSize; ++i)
        cout << resultArray[i] << " ";
    cout << endl;

    delete[] resultArray;
    delete[] rowPosition;
}

// 调用
int a[4][5] = {
    {5, 8, 15, 25, 36},
    {0, 4, 18, 50},
    {3, 7, 20, 30, 45},
    {1, 3, 5, 6, 8}
};
int aLen[4] = {5, 4, 5, 5};
MergeAll(a, aLen, 4);


 

为了减少比较次数,推荐采用一个数据结构来存储比较结果:


 实现不再赘述。


补充知识:

数组指针:int (*p)[4]-> 指针指向的是一个数组

指针数组:int *p[4]-> 数组中存放的是指针 写成 int* p[4] 更好理解一点

优先级:()>[]>*

 

问题4:25匹马,5个跑道,即每次只允许5匹马进行比赛,为选出跑的最快的前5匹马,需要比赛多少次?

 

需要10次:即先分成5组排序,然后对这5组排序的结果进行归并

 

扩展问题:如果只需要排定冠亚季军呢?

需要7次:

前5次: 先分成5组排序

第6次: 选各组第一赛跑

第7次: 选第六次中排名第一组的2 3 名, 排名第二的 1 2名 与排名第三的马进行比赛

因为第六次中的4,5名不可能成为亚军和季军,第六次比赛中的第3名不可能成为亚军。

 

问题5:有N个有序数组,如

N = 3

A = {5, 8, 15, 25, 36}

B = {0, 4, 18, 50}

C = {3, 7, 20, 30, 45}

求一个范围,如[3, 5],使得每个数组中都存在在此范围内的数字,并保证该范围的跨度最小

 

在归并的过程中对范围进行判断即可,示例代码:

#include <iostream>
using namespace std;

int k = 3;
int list[3][5] = {
    {5, 8, 15, 25, 36},
    {0, 4, 18, 50},
    {3, 7, 20, 30, 45}};

int eachLen[3] = {5, 4, 5};
int eachPos[3] = {0, 0, 0};
int pairstart, pairend;
const int MAXINT = 0x7fffffff;

void Merge()
{
    int maxPosition;
    int minPosition;
    int maxValue;
    int minValue;
    while (true)
    {
        int maxValue = -1;
        int minValue = MAXINT;
        for (int i = 0; i < 3; ++i)
        {
            if (list[i][eachPos[i]] > maxValue)
            {
                maxValue = list[i][eachPos[i]];
                maxPosition = i;
            }
            if (list[i][eachPos[i]] < minValue)
            {
                minValue = list[i][eachPos[i]];
                minPosition = i;
            }
        }
        if (pairend - pairstart > maxValue - minValue)
        {
            pairend = maxValue;
            pairstart = minValue;
        }
        eachPos[minPosition] ++;
        if (eachPos[minPosition] >= eachLen[minPosition])
            return;
    }
}

int main()
{
    pairstart = 0;
    pairend = MAXINT;
    Merge();
    cout << pairstart << " " << pairend << endl;
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值