数组arr中任意2个数的和的绝对值最小值是多少?
提示:华为面试题原题
题目
数组arr,有正负,有0,无序的,请问你任意2个数的和,该和的绝对值最小值是多少?
一、审题
实例嘛,自然就是引导:
(1)-7 -3 -1 0 2 4 9
任选俩数,显然-1 和0 加起来绝对值是1应该是最小的
当然-3+4也是1
2-1=1也是1
-3+2=也是1
二、解题
我今天刚刚学的一个题,求子序列和的sum%m的最大值是多少?
差不多的思想:也是分开寻答案:
(1)答案可能来自所有的负数区【显然是最大的俩负数的和的绝对值】,比如-1-2绝对值为3
(2)答案可能来自所有的非负数区【显然是最小的俩整数的和的绝对值】,比如0+1=1
(3)答案可能同时来自负数区,可能来自非负数区,-1+0=1
因此呢,我们遇到一个负数x,需要找非负数区的最接近于|x|的的那个数
——敏感度来了,我之前做华为的笔试题,里面讲CPU的资源分配,也是将最接近于CPUcount的那台机器,这里就很重要了,这俩都是华为的题目,牛啊,都是靠二分查找的?
先说本题:
需要找非负数区的最接近于|x|的的那个数 L–R范围内,找最接近于k的那个位置,使劲往左找,比如<=k的那个数位置是j,那j+1也可能是最接近k的,要比较一下,谁更接近。
找到最接近k的j位置的代码:
public static int mostApproach(int[] arr, int L, int R, int k){
if (L > R) return -1;
if (L == R) return arr[L];
//R>L
int j = L;
while (L <= R){
int mid = L + ((R - L) >> 1);
if (arr[mid] <= k) {
j = mid;//先记下它
L = mid + 1;//继续往右找,看看能否找到==k的
}else {
//arr[mid] > k,过大,需要往左找
R = mid - 1;
}
}
//如果没有找到,说明全部都大于k呗,那最接近k的只能是L位置了
if (j == R) return j;
else return k - arr[j] <= k - arr[j + 1] ? j : j + 1;
//返回的是最接近的,可能j+1会更接近哦
}
然后算法大流程:
先排序,得到有序的arr,既然有0,则:
(1)先找到0那个位置,然后把俩负数拿过来,一个正数拿来,初始化一个max
(2)遍历所有的负数,找非负数区中最接近每一个负数的数,拿来求和,然后取max
public static int minAbsoluteValueOfTwoNum(int[] arr){
if (arr == null || arr.length < 2) return 0;
//先排序
Arrays.sort(arr);//默认升序
int N = arr.length;
int min = 0;
int j0 = mostApproach(arr, 0, N - 1, 0);//找到0处的位置
//看看0左边有几个负数?
if (j0 == 0) {
min = Math.abs(arr[1]);//没有负数,只能是非负区的最小俩数
return min;//没有负数,不说啥了,答案就是这个
} else if (j0 == 1) min = Math.abs(arr[0]);//就一个负数,就一个最大负数+0
else min = Math.abs(arr[j0 - 1] + arr[j0 - 2]);//超过2个负数,就最大俩负数先
//然后遍历负数区,找0--N-1上最接近x的那个位置j
for (int i = 0; i < j0; i++) {
int j = mostApproach(arr, j0, N - 1, Math.abs(arr[i]));//k==arr[i]
min = Math.min(min, Math.abs(arr[i] + arr[j]));
}
return min;
}
测试:
public static void test(){
int[] arr = {-7, -3, -1, 0, 2, 4,9};
int[] arr3 = {-1, 0, 2, 4,9};
int[] arr2 = {-7, -3, -1, 0, 1};
System.out.println(minAbsoluteValueOfTwoNum(arr));
System.out.println(minAbsoluteValueOfTwoNum(arr2));
System.out.println(minAbsoluteValueOfTwoNum(arr3));
}
public static void main(String[] args) {
test();
}
总结
提示:重要经验:
1)二分查找,有序数组中小于等于k的最右的位置j,最接近k的位置j,这都是要熟悉的
2)华为那个最近进cpuCount的那个玩意,也要好好理解。