请构造长度为N的数组arr,使得任意位置i<j<k,都有arr[i]+arr[k]不等于2arr[j]

请构造长度为N的数组arr,使得任意位置i<j<k,都有arr[i]+arr[k]不等于2arr[j]

提示:见多识广,题目见多了,就不奇怪,不难了,套路就那么多


题目

请构造长度为N的数组arr,使得任意位置i<j<k,都有arr[i]+arr[k]不等于2arr[j]


一、审题

示例:比如arr=1 5 9
1+9!=2*5=10
arr=1,肯定也满足,因为就不会有三个数


奇数偶数特性,奇数加偶数=奇数,2倍数绝对是偶数,两者不可能相等

这个题充分利用奇数偶数的特点!

重点基础知识:
当a,b,c满足a+c!=2b时,同时将abc转化为下面的奇数与偶数,也会满足的a+c!=2b的
(1)仨奇数:abc=2a-1,2b-1,2c-1
(2)仨偶数:abc=2a,2b,2c

为毛呢?
因为:
在这里插入图片描述
在这里插入图片描述
这是很简单的知识,你记住了

有了arr=a,b,c,N长度数组
然后,咱们怎么生成一个2N长度的数组,且仍然满足:i+k!=2j的条件呢?
很简单,有了上面达标的arr,直接扩展2N的数组
左边N是2arr-1,右边N是2arr就行了
arr2= abc=2a-1,2b-1,2c-1,2a,2b,2c
为毛呢?
很简单呀!
下图arr2,左边是arr的奇数倍段,右边是arr的偶数倍段
(1)若干ijk都在左边,或者ijk都在右边,没啥可说的,上面咱们就说过了,奇数段,偶数段各自必然满足i+k!=2j
在这里插入图片描述
(2)如果j在奇数段,也就是左边,那i<j<k的话,i必然在左边,排除(1)的k,那k必然在右边段
显然,i+k必然是一个奇数,因为奇数+偶数=奇数
而2j必然是一个偶数,故偶数不可能等于奇数,所以i+k!=2j
(3)如果j在偶数段,也就是右边,那i<j<k的话,k必然在右边,排除(1)的i,那i必然在左边段
显然,i+k必然是一个奇数,因为奇数+偶数=奇数
而2j必然是一个偶数,故偶数不可能等于奇数,所以i+k!=2j,这跟(2)一样的证明方法
所以呢
上面(1)(2)(3)三种情况都是满足i+k!=2j条件,arr2就是一个很好的东西

反过来,如果有了arr是N长,你咋确定这N个就非常非常地达标呢?
很简单,递归去求(N+1)/2长度的数组,每次分一半,一直到N=1时,arr=1天然就达标
为啥事N+1再除2呢?
因为N是偶数时,折断一半很简单
在这里插入图片描述

但是N是奇数时,比如5吧,你折断只有2,肯定需要将5+1再折断为3,早基础达标的arr
然后再拼奇数段+偶数段组成6长度的数组
我们取前5个数据即可:
在这里插入图片描述


递归构造(N+1)/2长度的达标数组,最后用奇数段拼偶数段达成N长度的数组

所以,定义:f(N)要构造长度为N的数组arr,使得任意位置i<j<k,都有arr[i]+arr[k]不等于2arr[j]

(1)进到f(N)第一步,先利用f()去构造(N+1)/2的达标数组,
(2)然后再利用上面的奇数段+偶数段的方法生成N长度的达标数组
(0)遇到N=1时,arr=1天然达标

手撕代码:

    //复习
    //所以,定义:**f(N)要构造长度为N的数组arr,使得任意位置i<j<k,都有arr[i]+arr[k]不等于2arr[j]**
    public static int[] makeStandardArr(int N){
        if (N == 1) return new int[] {1};//(0)遇到N=1时,arr=1天然达标

        //(1)进到f(N)第一步,先利用f()去构造(N+1)/2的达标数组,
        int mid = (N + 1) >> 1;
        int[] arr = makeStandardArr(mid);//折断一半的数组,达标的arr,用来生成N的arr2
        //(2)然后再利用上面的奇数段+偶数段的方法生成N长度的达标数组
        //前面半段是奇数,全要了,后面可能要不了
        int[] ans = new int[N];
        int i = 0;//索引
        for (; i < mid; i++) {
            ans[i] = 2 * arr[i] - 1;//奇数段
        }
        for (int j = 0; j < mid && i < N; i++, j++) {
            //i不能越界N,提前结束,j就跟着over
            ans[i] = 2 * arr[j];//偶数段
        }

        return ans;
    }

非常简单吧:
测试一把:

   public static void test(){
        int[] res = makeStandard(5);
        int[] res2 = makeStandard(5);
        for (Integer i : res) System.out.print(i +" ");
        System.out.println();
        for (Integer i : res2) System.out.print(i +" ");
    }

    public static void main(String[] args) {
        test();
    }

问题不大

1 5 3 2 6 
1 5 3 2 6 

如何计算复杂度:
还是老样子,递归用master公式求:
【1】递归思想求arr中最大值,递归函数先调用处理几部分,然后整理和归并信息返回
本题中
代码递归调用一次a=1,就是去求N+1/2数据规模的达标arr那,只调用这么一次递归
规模N/b=N+1/2,则b=2
除了递归,还要生成奇数段+偶数段的数据,N长度,o(n)=o(n^d),即d=1
log(b,a)=log(2,1)=0<d=1
所以满足下面master的第一个条件,故本题复杂度o(n)
在这里插入图片描述


总结

提示:重要经验:

1)递归代码的复杂度计算方法:master公式,记好了
2)本题用的就是奇数偶数的知识,奇数+偶数绝不可能等于偶数,同时abc:a+c不等于2b,则abc扩充为奇数段2arr-1,和偶数段2arr,两边各自达标,组合ijk也达标。
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值