1.一个长度为 n(n≥1)的升序序列 S,处在第 n/2 个位置的数称为 S 的中位数。例如,若序列 S1=(11,13,15,17,19),则 S1 的中位数是 15。两个序列的中位数是含它们所有元素的升序序列的中位数。例如,若 S2=(2,4,6,8,20),则 S1 和 S2 的中位数是 11。现有两个等长升序序列 A 和 B,试设计一个在时间和空间两方面都尽可能高效的算法,找出两个序列 A 和 B 的中位数。假设升序序列采用顺序表存储。顺序表结构描述如下:
# define MaxSize 50 typedef struct
{ ElemType data[MaxSize];
int length;
}SqList;
要求:
- 给出算法的基本设计思想;
- 根据设计思想,采用 C/C++语言描述算法,关键之处给出简要注释;
- 说明你所设计算法的时间复杂度和空间复杂度.
1.第一种思路为暴力求解(没啥好说的)
2.第二种思路为用指针方法(代码如下)
int find( int* a, int* b, int len )
{
int i = 0,j = 0,k = 0;
for ( ; k < len-1; k++)
{
if ( a[i] < b[j] )
i++;
else
j++;
}
return (a[i] < b[j])? a[i]: b[j];
}
3.第三种思路为减治
核心:将升序序列左右两边同时减去相等的数字,中位数不变。
设A的中位数为a,B的中位数为b.
(1)若a = b,则A与B组成的序列的中位数为a。
(2)若a < b,则舍弃A中较小的部分,舍弃B中较大的部分,且两次舍弃的长度一样。
(3)若a > b,则舍弃A中较大的部分,舍弃B中较小的部分,且两次舍弃的长度一样。
重复(1),(2),(3),直到两个序列只剩下一个元素时为止,较小者即为中位数。
分析:
若a = b,很明显是对的,因为在中间位置不影响。
若a > b(不举 a < b,两个一样的)
当序列为奇数时(分析图如下)
当序列为偶数时(分析图如下)
则我的代码如下:
int find(SqList* A, SqList* B)
{
int a = 0,b = 0,end1 = A.length - 1,end2 = B.length - 1,mid1,mid2; //定义A,B序列的首项,末项,中位项
while(a!= end1 || b!= end2) //当A,B项只有一个元素时退出循环
{
mid1 = (a + end1) / 2;
mid2 = (b + end2) / 2;
if(A.data[mid1] == B.data[mid2]) //如果相等,则中位数求出
return A.data[mid1];
else if(A.data[mid1] < B.data[mid2])
{
if(end % 2 == 0) //当序列长度为奇数时
{
a = mid1; //舍弃A中中位数前面的数并保留A的中位数
end2 = mid2; //舍弃B中中位数后面的数并保留B的中位数
}
else
{
a = mid1 + 1; //舍弃A中中位数后面的数(包括中位数)
end2 = mid2; //舍弃B中中位数后面的数并保留B的中位数
}
}
else //当A.data[mid1] < B.data[mid2]时
{
if(end % 2 == 0) //跟前面原理一样,只不过A,B调换了一下位置
{
b = mid2;
end1 = mid1;
}
else
{
b = mid2 + 1;
end1 = mid1;
}
}
}
return A.data[a] < B.data[b] ? A.data[a] : B.data[b]; //两者中最小的即为中位数
}
//因为每次循环求中位数时除以2,所以时间复杂度为O(㏒2n),而空间没有其他的开销,所以空间复杂度为O(1)