请构造长度为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,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。