问题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;
}