求k个数组包含每个数组至少一个元素的最小范围(待字闺中,备忘)

17 篇文章 1 订阅
7 篇文章 0 订阅

有k个有序的数组,请找到一个最小的数字范围。使得这k个有序数组中,每个数组都至少有一个数字在该范围中。

例如:


1: 4, 10, 15, 24, 26

2: 0, 9, 12, 20

3: 5, 18, 22, 30


所得最小范围为[20,24],其中,20在2中,22在3中,24在1中。


这是待字闺中的一道面试题,就个人经验来看一般有序数组的题目都会让人联想到有折半查找来解决,到有序数组为多个时,一般会联想到归并排序的思想,那么这道题就不例外。


以下引自待字闺中的分析:

那这个题目选择哪个方法继续尝试呢?那我们再分析一下要解决的问题。找到一个最小的范围,每一个有序数组中,都至少有一个元素在这个范围中。找到这样一个范围并不难,可是如何确定最小的范围呢?最终得到的最小的范围,至少包含三个元素,并且在所有数组整体的排序中,是相邻的。假设最小范围是[a, b, c],a < b < c。 c-a是最小的。并且,a,b,c来自不同的有序数组。还有一种情况是[a,b,d,c],a,b,c不是紧邻的,中间有一个d即: a< d < c。这时,d只能是来自b所在的数组,如下分析:

  1. d来自a所在的数组,那么应有更短的范围c - d

  2. d来自c所在的数组,那么应有更短的范围d - a

  3. d来自b所在的数组,范围大小是不变的,就是无论是考虑d,还是考虑b。都没有影响。

从上面的分析,我们得出,只需要考虑在最终的排序中,考虑邻近的、并且来自不同有序数组的元素,作为备选的范围。那么该怎么样做到只考虑临近的、并且来自不同的有序数组的元素呢?这里就用到了归并排序的思想。以原题中的例子为例,假设有三个指针指,p1,p2,p3,分别指向三个数组的第一个元素:

步骤指针当前值最大值最小值min_range_value移动指针
14,0,5505p2
24,9,5945p1
310,9,51055p3
410,9,181899p2
510,12,1818108p1
615,12,1818126p2
715,20,1820155p1
824,20,1824186p3
924,20,2224204p2
end

结束是因为第二个数组已经没有元素可以再进行遍历了。最终得到最小的min_range_value为4,即为题目例子的答案。

上面这个方法,通过归并排序的思想,确保每次都是k个来自不同的数组的元素进行比较,得到最大值、最小值。就可以得到一个范围,包含了所有数组中的数字。

这个题的著名变种是从网页中产生包含所有查询词的最小的摘要。如果你 面过Google,你应该听说过这题。

个人的理解:

其实就是利用归并的思想,三个数组或更多个数组做归并排序,排在最小的元素向前移动,计算最大最小元素之间的间隔,这样当一个数组用尽的时候,记录的最小间隔即为所求。

/*************************************************************************
        > File Name: mindiff.c
        > Author: desionwang
        > Mail: wdxin1322@qq.com 
        > Created Time: Wed 23 Oct 2013 02:57:44 PM CST
 ************************************************************************/


#include<stdio.h>


int mindiff(int a[], int size_a, int b[], int size_b, int c[], int size_c){
        int i = 0, j = 0 ,k = 0;
        int minnum;
        int mindiff = 65535;
        if(a == NULL || b == NULL || c == NULL){
                return -1;
        }
        if(size_a <= 0 || size_b <= 0 || size_c <= 0){
                return -1;
        }
        int max, min, diff;
        int index;
        while(i < size_a && j < size_b && k < size_c){
                printf("a:%d\tb:%d\tc:%d\n", a[i], b[j], c[k]);
                if(a[i] >= b[j]){
                        if(a[i] >= c[k]){
                                max = a[i];
                        }else{
                                max = c[k];
                        }
                        if(b[j] <= c[k]){
                                min = b[j];
                                index = j;
                                j++;
                        }else{
                                min = c[k];
                                index = k;
                                k++;
                        }
                }else{
                        if(b[j] >= c[k]){
                                max = b[j];
                        }else{
                                max = c[k];
                        }
                        if(a[i] <= c[k]){
                                min = a[i];
                                index = k;
                                i++;
                        }else{
                                min = c[k];
                                index = i;
                                k++;
                        }
                }


                printf("max:%d\tmin:%d\n", max, min);
                //printf("a:%d\tb:%d\tc:%d\n", a[i], b[j], c[k]);
                diff = max - min;
                if(diff < mindiff){
                        mindiff = diff;
                }
        }
        //printf("a:%d\tb:%d\tc:%d\n", a[i], b[j--], c[k]);
        printf("mindiff:%d\n", mindiff);
        return mindiff;


}


int main(){
        int a[5] = {4, 10, 15, 24, 26};
        int b[] = {0, 9, 12, 20};
        int c[] = {5, 18, 22, 30};
        mindiff(a, 5, b, 4, c , 4);
}



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值