有趣的题目-二

给你N个数,要求在O(n)时间复杂度内,求出在有序情况下,两个相邻数的最大差值。【原题差不多是对于任意一个数都有和他最接近的数,要求你求这种最大的距离】

我们来一点一点地思考:

1.枚举:对于每一个数,扫描一遍所有的数,得到离他最近的数,更新最大差值,复杂度O(N^2)。

2.排序:整个数组快速排序一遍,再线性扫一遍,更新最大插值,复杂度O(NlogN)。

3.思想依旧是从排序出发,虽然基于比较的排序算法时间复杂度是不可能低于O(NlogN)的,但是我们也有不基于比较的排序,他们的复杂度和数据状况有关-基数排序、计数排序、桶排序。

我们需要的值是在有序情况下才能得出的,我们可以不排序,但是得借助一些有序的东西-桶。

让我们先做一些假设,n个数,数据范围是[min,max]。

A.假设这n个数在范围区间上均匀分布,并且(max-min)%n==0。

那么我们可以借用分块的思想,block=(max-min)/n,(index-min)/block就可以得到index位置的元素是归属于哪个桶的。然后因为这些元素是均匀分布的,所以一个桶内指挥归属一个元素。线性扫一遍将元素归位后,线性扫一遍更新最大差值。

B.让我们把假设弱化。假如n个数不会均匀的分布呢。假设不均匀分布,那么至少会有两个元素会在归位后并在一个桶里,此时会产生一个空桶。我们对桶维护两个值,min-桶内元素的最小值,max-桶内元素的最大值,我们可以简单分析后得知:

1)第i号桶的max和第i+1号桶的min在排序后一定是相邻的

2)由于有一个空桶,那么这个最大的值一定不会是一个桶内的元素产生,因为桶内元素的最大差值是<=block-1的,而空桶左右两个桶,前一个的max的后一个的min的差值是一定>=block的。

3)此方法一定有解:无解的情况只能是所有的元素都在一个桶内,但是由于我们是把整个规模区间划分成n份的,min在第一个个桶,max在第n个桶,因此不可能满足所有的元素都在一个桶内。

4)最大的元素值并不一定是空桶左右两个最近非空桶的差值,假设是一个空桶,那么他左右非空桶的邻近元素元素的最小差值为block,但是也可能存在这种情况,两个相邻的非空桶,各只有一个数,左边的桶内存放的是这个桶对应的区间的最小值,右边的桶内存放的是这个桶对应的区间的最大值,那么他们作为两个相邻元素,差值为2*block-1。

小总结:空桶的存在是为了去除同一个桶内存在最大差值的可能性。

因此,当元素归位后,我们遍历所有的桶,用前一个非空桶的max和后一个非空桶的min的差值更新最大差值即可。

C.这是最后一个弱化了,(max-min)%n!=0,也就是说,最后一个桶的跨度区间可能会超过block。那么在这种情况下,有可能在最后一个桶里,会出现两个相邻元素,并且他们的差值是超过block的,所以此时即使前面有空桶都不能免去此种情况的可能性。

办法:原本分配N个桶改成分配N+1个桶,但是block依旧保持block=(max-min)/n,于是,最后一个桶内装的是以max为右端值的,且桶内元素跨度严格<block的桶,并且由于是N+1个桶,因此一定是在第1和第n+1号桶中间至少会有一个空桶,而空桶的存在除去了同一个桶内有最大差值的可能性。

代码:

#include<cstdio>
#include<algorithm>
using namespace std;
int n,Max=0xffffffff,Min=0x7fffffff,Arr[105],large[105],small[105],to,lastMax,Maxdif=0xffffffff;
bool isNone[105]; 
int belong(int value){
    return (value-Min)*(n+1)/(Max-Min);
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&Arr[i]);
        Min=min(Min,Arr[i]);
        Max=max(Max,Arr[i]);
    } 
    if(Min==Max){//最大值和最小值相等 
        printf("0");
        return 0;
    }
    large[0]=small[0]=Min;
    large[n]=small[n]=Max;
    isNone[0]=isNone[n]=true;
    for(int i=1;i<=n;i++){
        to=belong(Arr[i]);
        if(!isNone[to]){//没有元素 
            isNone[to]=true;
            large[to]=small[to]=Arr[i];
        }else{
            large[to]=max(large[to],Arr[i]);
            small[to]=max(small[to],Arr[i]);
        }
    }
    lastMax=large[0]; 
    for(int i=1;i<=n;i++){
        if(isNone[i]){
            Maxdif=max(Maxdif,small[i]-lastMax);
            lastMax=large[i];
        }
    }
    printf("%d",Maxdif);
    return 0;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值