给定一个有序数组a(从小到大排列),数组中的数据有正有负,找出这个数组中的绝对值最小的元素。最先到的自然是从头到尾依次遍历数组中的每个元素,找出绝对值最小的元素。这是最简单的方法,不过它并没有用到数组有序这个特性,现在我们来看看有没有更好的方法。题目要求在数组中查找元素,并且此数组有序,那么可以想到用二分法来处理。
首先我们先看一下如果数组中元素全部为正数或者全部为负数的情况:
- 如果a[0]>0,那么数组中所有元素均为正数,则a[0]为绝对值最小的元素
- 如果a[len-1]<0,那么数组中所有元素均为负数,则a[len-1]为绝对值最小的元素
当数组中元素有正有负时,绝对值最小的元素在正负数的交界点处,这是只需要比较交界点相邻两数绝对值的大小,返回绝对值小的即可。设a[mid]为数组的中间元素,那么可以以如下步骤进行查找:
- 如果a[mid]<0,因为数组是升序,说明绝对值最小的数不会出现在a[mid]左边,需要在mid以右的区间进行查找
- 如果a[mid]>0,因为数组是升序,说明绝对值最小的数不会出现在a[mid]右边,这里同时判断与a[mid]相邻且在其左侧的a[mid-1]元素的正负,如果为负数,那么说明这两个数是数组中正负交界点,返回这两个数的绝对值较小的,如果a[mid-1]不为负,那么需要在mid以左的区间进行查找
- 如果a[mid] == 0,那么a[mid]即为绝对在最小的元素
- 区间范围改变,对区间中的mid重新赋值,重复上述步骤
根据上面的步骤写出代码:
1 int FindMinAbs(int a[], int len)
2 {
3 //如果a数组第一个元素>0,则a[0]之后的数均>0,且都比a[0]大
4 if(a[0]>0)
5 return a[0];
6 //如果a数组最后一个元素<0,则a[len-1]之前的数均<0,且都比a[len-1]小,所以abs(a[len-1])最小
7 else if(a[len-1]<0)
8 return a[len-1];
9
10 int left=0, right=len-1, mid=(left+right)/2;
11 int i=0;
12
13 //如果a[0]<0,a[len-1]>0,那么绝对值最小的数一定出现在正负交界点
14 while(true)
15 {
16 cout<<"mid="<<mid<<", a[mid]="<<a[mid]<<", left="<<left<<", right="<<right<<endl;
17 if(a[mid]<0)
18 {
19 left = mid+1;
20 }
21 else if(a[mid]>0)
22 {
23 //如果a[mid]和a[mid-1]一正一负,所以只需判断他俩的abs大小
24 if(a[mid]*a[mid-1] <= 0)
25 return -a[mid-1] < a[mid]? a[mid-1]:a[mid];
26 right = mid-1;
27 }
28 else
29 return a[mid];
30
31 mid = (left+right)/2;
32 }
33 }