题目:给定一个整数数组A,已知这个数组的任意相邻的两个元素差的绝对值为1。现在给定一个数值t,给出一个算法查找t是否在数组中,若在则给出t所在的位置。
解析:
对于无序数组中的查找问题,最直接也是唯一的方法就是将数组遍历一遍,找看t是否在数组中。但是这里的数组有一个特性,就是相邻的元素之间的差值绝对值为1,也就是说如果当前的数值为x,则其下一个数不是x+1就是x-1。那么我们应该如何利用这个信息来加速算法呢?
假定我们要在A[i..j]这个区域查找数值t,那么如何能够快速的判断t是否在这个区间中?根据数组A的特性,即A[i]和A[j]的关系会有四种情况:
1.A[i]<A[j] && A[j]-A[i] == j-i
A[i..j]这个区间是一个严格递增的区间,那么这个区间的最大值max_v = A[j], 最小值min_v = A[i];
2.A[i]>A[j] && A[i]-A[j] == j-i
A[i..j]这个区间是一个严格递减的区间,那么这个区间内最大值max_v = A[i],最小值min_v = A[j];
3.A[i] <= A[j] && A[j]-A[i] < j-i
此时A[i..j]这个区间的值的变化有升有降,那么此时这个区间内元素的最大以及最小值是多少呢?首先看最大值max_v,将A[i..j]分为两个区间A[i..k]和A[k..j],那么如果A[i..k]这个区间严格递增,而区间A[k..j]严格递减,那么最大值则会是max(A[i]+k-i, A[j])(之所以是A[i]+k-i和A[j]之间的最大值,是因为有可能区间i..j不够长,使得A[i]+k-i小于A[j])。那么k值该如何求解呢?对于这个k值需要满足下面这个等式:
A[i]+k-i - (j-k) = A[j]
求得k = (A [j]+j+i-A[i])/2,因此此时区间A[i..j]所能达到的最大值max_v = max(A[j], A[i] + (A[j]+j-i-A[i])/2);
对于这个区间内所能达到的最小值与最大值的求解类似,不过此时区间分为两个区间A[i..k]和A[k..j]之后,A[i..k]严格递减,而区间A[k..j]严格递增。同理可以求得k = (A[i]+A[j]+j+i)/2,
则min_v = min(A[i], A[i] - (A[i]+A[j] + j - i)/2);
4.A[i]>=A[j] && A[i]-A[j] < j-i
此时区间A[i..j]的值同样有升有降,与第三种情况类似。用同样的方法可以求得此时区间A[i..j]能够达到的最大和最小值分别为:
max_v = max(A[i], A[i] + (A[j]+i-i-A[i])/2)
min_v = min(A[j], A[i] -(A[i]+A[j]+j-i)/2)
根据以上四种情况,我们便能够确定一个给定的区间内所能达到的最大和最小值了。有了区间的取值范围,我们便能够利用二分搜索进行查找。不过这里的二分跟通常的二分有一点区别就是,当确定数值在这个区间内之后,我们取该区间中间的那个元素,若这个元素不是目标值,则我们需要对原区间划分为前后两个子区间,此时对于通常的二分查找,目标值只可能出现在一个自区间,而这里的情况是目标值可能同时出现在两个子区间中。
分析至此,代码编写则不难了。
分析有不到位的地方,望各位不吝赐教!