美文网首页
算法题之--《寻找两个有序数组的中位数》

算法题之--《寻找两个有序数组的中位数》

作者: 忧郁的小码仔 | 来源:发表于2019-09-15 15:59 被阅读0次

这是LeetCode上的一道算法题

给定两个有序数组,求两个有序数组的中位数,要求时间复杂度O(log(m + n))

看到时间复杂度带log,那就不是简单的合并排序直接获取中位数这么简单了。第一反应能想到的就是最直接有效的二分法,这样才能把时间复杂度降下来。在开始图解之前,我们先来看下简单的例子:

比如:

arr1: 1 3 5
arr2: 2 4 6 7 8

这里一共有8个元素,中位数要根据他俩合并后中间的第4,第5个元素来得到,也就是:(4 + 5)/ 2 = 4.5
问题就简化为找到第4小和第5小的元素。以寻找第4小为例:
第4小有可能藏在arr1和arr2的前两个元素里,并且是其中最大的,也有可能不在里面。所以,只要我们不删除arr1和arr2前两个元素中的最大元素,第4小就会继续留在两个数组里。

arr1: 1 3 | 5
arr2: 2 4 | 6 7 8
通过比较arr1和arr2的第2个元素: 3 < 4; 说明第4小肯定不在arr1的 【1 3】中间,因为第4小如果在【1 3】和【2 4】中间的话,它一定是最大的,所以即使我们移除【1 3】,第4小仍然会留在两个数组中,但是移除【1 3】后我们要查找的范围就缩小了一半,好处多多

下面,我们看下一个更详细例子的图解:


两个有序数组寻找中位数

理解了上面的解析后,我们还要注意下下面几种特殊情况:

特殊情况

这几种情况都是我们要在代码中需要注意的地方,下面直接看示例代码:

@implementation MediaOfTwoArr

+(CGFloat)mediaInArr:(NSMutableArray *)arr1 andArr:(NSMutableArray *)arr2 {
    
    NSInteger count1 = arr1.count;
    NSInteger count2 = arr2.count;
    
    /**
     求中位数,只需要得到两个数组合并后中间的一个或者两个数字即可
     
     比如偶数情况下:一共14个数字,则中位数=(第7小数 + 第8小数)/2;
     奇数情况下:比如一共15个数字,则中位数 = 第8个小数;
     */
    
    // 根据两个数组的总个数,计算要得到的第k小数和第k+1小数
    NSInteger kMin = 0;
    if ((count1 + count2) % 2 == 0) {
        // 偶数情况下需要得到中间的两个数才能求的中位数,这里我们直接放到数组中
        kMin = (count1 + count2) / 2;
        NSMutableArray *result = [NSMutableArray array];
        [MediaOfTwoArr getKminAndK1minInArr:arr1
                                   withArr2:arr2
                        withLeftIndexOfArr1:0
                        withLeftIndexOfArr2:0
                                   withKmin:kMin
                                 withResult:result];
        return ([result[0] floatValue] + [result[1] floatValue]) / 2;
        
    } else {
        // 奇数情况下,只需要得到最中间的数就可以了
        kMin = (count1 + count2 + 1) / 2;
        return [MediaOfTwoArr getKminInArr:arr1
                           withArr2:arr2
                withLeftIndexOfArr1:0
                withLeftIndexOfArr2:0
                           withKmin:kMin];
    }
}

+(void)getKminAndK1minInArr:(NSMutableArray *)arr1
                withArr2:(NSMutableArray *)arr2
     withLeftIndexOfArr1:(NSInteger)arr1Left
     withLeftIndexOfArr2:(NSInteger)arr2Left
                withKmin:(NSInteger)kmin
                 withResult:(NSMutableArray *)result {
    
    if (arr1Left >= arr1.count) { // arr1已经遍历完,则直接从arr2当前索引开始取第kmin小数即可
        [result addObject:arr2[arr2Left + kmin - 1]]; // 第k小数
        [result addObject:arr2[arr2Left + kmin]]; // 第k + 1小数
        return;
    }
    
    if (arr2Left >= arr2.count) { // 同上
        [result addObject:arr1[arr1Left + kmin - 1]];
        [result addObject:arr1[arr1Left + kmin]];
        return;
    }
    
    if (kmin == 1) {
        // 当kin为1时,直接比较arr1和arr2当前索引的元素大小即可得到要求的小数
        if ([arr1[arr1Left] integerValue] <= [arr2[arr2Left] integerValue]) {
            [result addObject:arr1[arr1Left]]; // 第k小数
            
            // 如果arr1中只剩下第k小数了,那么k + 1小数一定在arr2中
            if (arr1Left == arr1.count - 1) {
                [result addObject:arr2[arr2Left]];
                
                // 否则继续比较arr1下一个元素和arr2的当前索引,得到第k + 1小数
            } else {
                if ([arr1[arr1Left + 1] integerValue] < [arr2[arr2Left] integerValue]) {
                    [result addObject:arr1[arr1Left + 1]];
                } else {
                    [result addObject:arr2[arr2Left]];
                }
            }
        } else {
            [result addObject:arr2[arr2Left]]; // 第k小数
            
            // 如果arr2中只剩下第k小数了,那么k + 1小数一定在arr1中
            if (arr2Left == arr2.count - 1) {
                [result addObject:arr1[arr1Left]];
                
                // 否则继续比较arr1下一个元素和arr2的当前索引,得到第k + 1小数
            } else {
                if ([arr2[arr2Left + 1] integerValue] < [arr1[arr1Left] integerValue]) {
                    [result addObject:arr2[arr2Left + 1]];
                } else {
                    [result addObject:arr1[arr1Left]];
                }
            }
        }
        
        return;
    }
    
    // 比较arr1和arr2 从当前索引开始的第kmin/2个元素大小,这里向下取整
    NSInteger removeCount = kmin / 2;
    
    // 第kmin/2个元素超出arr1的范围,则将removeCount直接设为arr1剩余元素的个数
    if (removeCount + arr1Left > arr1.count) {
        removeCount = arr1.count - arr1Left;
    } else if (removeCount + arr2Left > arr2.count) { // 同上
        removeCount = arr2.count - arr2Left;
    }
    
    if ([arr1[removeCount + arr1Left - 1] integerValue] <= [arr2[removeCount + arr2Left - 1] integerValue]) {
        // arr1比较小,将arr1Left右移继续递归
        arr1Left = arr1Left + removeCount;
    } else {
        // arr2比较小,将arr2Left右移继续递归
        arr2Left = arr2Left + removeCount;
    }
    
    return [MediaOfTwoArr getKminAndK1minInArr:arr1
                                      withArr2:arr2
                           withLeftIndexOfArr1:arr1Left
                           withLeftIndexOfArr2:arr2Left
                                      withKmin:kmin - removeCount
                                    withResult:result];
}

+(NSInteger)getKminInArr:(NSMutableArray *)arr1
           withArr2:(NSMutableArray *)arr2
withLeftIndexOfArr1:(NSInteger)arr1Left
withLeftIndexOfArr2:(NSInteger)arr2Left
           withKmin:(NSInteger)kmin {
    
    // arr1已经遍历完,则直接从arr2当前索引开始取第kmin小数即可
    if (arr1Left >= arr1.count) {
        return [arr2[arr2Left + kmin - 1] integerValue];
    }
    
    if (arr2Left >= arr2.count) { // 同上
        return [arr1[arr1Left + kmin - 1] integerValue];
    }
 
    // 当kin为1时,直接比较arr1和arr2当前索引的元素大小即可得到要求的小数
    if (kmin == 1) {
        if ([arr1[arr1Left] integerValue] <= [arr2[arr2Left] integerValue]) {
            return [arr1[arr1Left] integerValue];
        } else {
            return [arr2[arr2Left] integerValue];
        }
    }
        
    // 比较arr1和arr2 从当前索引开始的第kmin/2个元素大小
    NSInteger removeCount = kmin / 2;
    
    // 第kmin/2个元素超出arr1的范围,则将removeCount直接设为arr1剩余元素的个数
    if (removeCount + arr1Left > arr1.count) {
        removeCount = arr1.count - arr1Left;
    } else if (removeCount + arr2Left > arr2.count) { // 同上
        removeCount = arr2.count - arr2Left;
    }
    
    if ([arr1[removeCount + arr1Left - 1] integerValue] <= [arr2[removeCount + arr2Left - 1] integerValue]) {
        // arr1比较小,将arr1Left右移继续递归
        arr1Left = arr1Left + removeCount;
    } else {
        // arr2比较小,将arr2Left右移继续递归
        arr2Left = arr2Left + removeCount;
    }
    
    return [MediaOfTwoArr getKminInArr:arr1
                     withArr2:arr2
          withLeftIndexOfArr1:arr1Left
          withLeftIndexOfArr2:arr2Left
                     withKmin:kmin - removeCount];
}

@end

这里为了方便观察,我把数组个数是奇数和偶数两种情况分开处理了,奇数时,只需要获取第k小数即可;偶数时,需要获取第k小数和第k+1小数。

相关文章

网友评论

      本文标题:算法题之--《寻找两个有序数组的中位数》

      本文链接:https://www.haomeiwen.com/subject/jizlyctx.html