2

如果这个问题不属于这里,我很抱歉,我的问题不在于代码,而在于算法,所以也许它更适合另一个网站,但 stackoverflow 的好人从未让我失望。

这是问题

给定 2 个排序数组A,并且B它们具有相同数量的元素,假设n,并且它们不共享元素,并且没有元素在同一个数组中出现两次,以对数时间复杂度找到数组并集的中位数.

非常重要的说明:如果n是奇数,则中位数是中间元素。但如果n是偶数,则中位数不是中间元素的平均值。它被定义为中间元素的最小值。

解决方案:这个想法很简单。由于它们是排序的,我们可以找到(call ) 的中位数和 (call )A的中位数。如果那么我们知道并集的中位数是 小于的元素或大于的元素,如果 则相反。所以我们扔掉多余的元素并做同样的过程,直到和足够小,比如说每个有 2 个元素,然后我们只需要找到这 4 个数字之间的中位数。4 个数字的中位数将是第二个最小值,因为 4 是偶数,即.med1Bmed2O(1)med1>med2Amed1Bmed2med2>med1ABO(1)

这是我的代码

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
int *scan_array(int* array_length);
int second_min_four_numbers(int a,int b,int c,int d);
int first_question(int *arr1,int *arr2,int left1,int right1,int left2,int right2);
void main()
{
    int *arr1,*arr2,length_arr1=0,length_arr2=0;
    printf("For the first sorted array:\n");
    arr1=scan_array(&length_arr1);
    printf("\nFor the second sorted array, enter %d numbers:\n",length_arr1);
    arr2=scan_array(&length_arr2);
    if(length_arr1==1) //edge case, arrays are length one. return the min
    {
        if(arr1[0] > arr2[0])
            printf("The Median is %d",arr2[0]);
        else
            printf("The Median is %d",arr1[0]);
    }
    else
        printf("The Median is %d",first_question(arr1,arr2,0,length_arr1-1,0,length_arr2-1));
    getch();
}
int *scan_array(int* array_length) //nothing fancy. just scan the arrays.
{
    int* temp,temp_length,array_element,i=0,*real_array;
    temp=(int*)malloc(50*sizeof(int));
    printf("Enter positive numbers. To stop enter negative or zero.\nDon't enter more than 50 numbers\n");
    scanf("%d",&array_element);
    while(array_element>0)
    {
        (*array_length)++;
        temp[i]=array_element;
        i++;
        scanf("%d",&array_element);
    }
    real_array=(int*)malloc((*array_length)*sizeof(int));
    for(i=0;i<*array_length;i++)
        real_array[i]=temp[i];
    free(temp);
    return real_array;
}
int first_question(int *arr1,int *arr2,int left1,int right1,int left2,int right2) 
{
    int med1,med2;
    if(right1-left1+right2-left2 == 2) //we are done. reached 4 elements. we will always be here for arrays larger than 1 element each
        return second_min_four_numbers(arr1[left1],arr1[right1],arr2[left2],arr2[right2]);
    med1=arr1[(left1+right1)/2]; //not done. find the medians in O(1).
    med2=arr2[(left2+right2)/2];
    if(med1 < med2)//the median of the union is somewhere between them
        return first_question(arr1,arr2,(left1+right1)/2,right1,left2,(left2+right2)/2);
    else
        return first_question(arr1,arr2,left1,(left1+right1)/2,(left2+right2)/2,right2);
}
int second_min_four_numbers(int a,int b,int c,int d) //find second min between four numbers
{
    int min=0,second_min=0; //very crude, and inefficient but simple to understand and still O(1)
    min = a;
    if(min > b)
        min = b;
    if(min > c)
        min = c;
    if(min > d)
        min = d;
    if(a == min) 
    {
        second_min=b;
        if(second_min > c)
            second_min = c;
        if(second_min > d)
            second_min = d;
        return second_min;
    }
    if(b == min)
    {
        second_min=a;
        if(second_min > c)
            second_min=c;
        if(second_min > d)
            second_min = d;
        return second_min;
    }
    if(c == min)
    {
        second_min=a;
        if(second_min > b)
            second_min = b;
        if(second_min > d)
            second_min = d;
        return second_min;
    }
    if(d == min)
    {
        second_min=a;
        if(second_min > b)
            second_min=b;
        if(second_min > c)
            second_min=c;
        return second_min;
    }
}

它按预期工作并编译。正如我所说,问题不在于我的代码,而在于算法。让我们看一个演示该问题的示例:

假设我们的输入是A=[1,3,5]B=[2,4,6]。然后med1=3med2=4。扔掉多余的元素,现在我们有了A=[3,5]B=[2,4]。现在我们总共只有 4 个元素,数据足够小,所以只需找到这 4 个数字的中位数即可[3,5,2,4]。中位数是3,这也是 和 的并集的中位数的正确结果AB所以结果是正确的。

现在让我们假设我们的输入是A=[1,3,5,7]B=[2,4,6,8]med1=3med2=4。扔掉多余的元素得到A=[3,5,7]B=[2,4]。现在med1=5med2=2。再次丢弃冗余得到A=[3,5]B=[2,4]。现在我们的数据足够小,找到其中的中位数[3,5,2,4]将再次给我们3。但这个结果是不正确的。3不是 和 的并集的中A位数B。正确的结果是4.

我们如何解决这个问题?

4

2 回答 2

0

让我提出一种概念化这个问题的不同方法。假设每个数组中有 4 个元素。考虑这个网格:

a1 a2 a3 a4
b1 b2 b3 b4

我们正在寻找一条穿过排列中心的线,这保证了该行左侧的条目数和该行右侧的条目数相等。另请注意,有两条不同的水平线作为划分条目的可能方式(上方较小或下方较小)。所以在这种情况下我们需要考虑的行数是 5,一般是 n+1。现在,通过行的二进制搜索应该可以解决问题。

于 2015-04-17T21:35:14.550 回答
0

该算法需要对中位数进行二分查找,即提出中位数的可能值。如果该值太低,则在下一次迭代中选择更高的值。如果太高,则选择较低的值。

在每次迭代中,我们从 A 中选择一个候选者,并从 B 中选择一个候选者。较小的候选者被提议作为中位数,并进行评估。如果建议的中位数太小,则 A 和 B 中的所有较小值都可以不考虑。同样,如果建议的中位数太大,则可以忽略 A 和 B 中较大的值。

例如,A=[1,2,7,19,22]假设 A 中的候选人是 7。假设 B 提出了更大的候选人,因此选择 7 作为可能的中位数。如果 7 太低,那么我们可以消除<= 7A 和 B 中的所有元素作为可能的候选。所以 A 变成A=[1,2,7,{19,22}]了花括号中的元素是中位数的剩余可能候选者。重复该过程,但这次来自 A 的候选人将是 19 岁。

继续这个例子,让我们说B=[20,25,26,27]. B 提出的候选者为 25。A 的候选者较低,因此我们评估 19。列表 A 有 3 个值低于 19,1 个高于 19。列表 B 有 4 个更高的值。总共低3,高5。结论:19 太低了,所以排除所有可能的候选数字 <= 19。经过两次通过后,我们有

A=[1,2,7,19,{22}]  B=[{20,25,26,27}]

A 的候选人是 22,B 是 25,建议 22 作为中位数。22 太高了,所以数字 >= 22 可以忽略,我们有

A=[1,2,7,19,{},22]  // 19 was too low and 22 was too high, so no candidates are left in A
B=[{20},25,26,27]   // 22 was too high, so the only remaining candidate in B is 20

20 是任一列表中唯一剩下的候选人,因此是答案。

于 2015-04-17T20:53:02.647 回答