朋友最近面试某公司,被一道笔试题难住,最后被无情pass了。题目大致是:int a[] 由2个已序且范围无交叉的序列组成,比如4,5,6,7,1,2,3,从中查找指定的数。要求时间复杂度小于O(N)。
算法思路
首先想到的是折半查找,时间复杂度应该为O(log N)。假设数组起始位置为x和y,折半位置为h,2个序列边界为*,查找数的位置为n。
数组表示为:
|---------->|---------->|
x h y
由a[h]与a[x],a[y]比较可知,序列边界*在h的左边还是右边。分为两种情况:
- 当a[h] < a[x]时,序列边界*在h的左边
|-----><----->|---------->|
x * h y
此时位置n分三种情况,只有当a[n] > a[h] 且 a[n] <= a[y]时下一次将在h的右边开始查找。
- 当a[h] > a[x]时,序列边界*在h的右边
|---------->|-----><----->|
x h * y
此时位置n分三种情况,只有当a[n] >= a[x] 且 a[n] < a[h]时下一次将在h的左边边开始查找。
代码实现
int find_in_two_sort_part(int a[], int len, int n)
{
int begin = 0;
int end = len - 1;
while (begin <= end)
{
// |---------->|---------->|
// x h y
int half = (begin + end) >> 1;
if (n == a[half])
{
return half;
}
// |-----><----->|---------->|
// x * h y
if (a[half] < a[end])
{
// |------><----->|-----*----->|
// x * h n y
if ((n > a[half]) && (n <= a[end]))
{
begin = half + 1;
}
else
{
end = half;
}
}
// |---------->|-----><----->|
// x h * y
else if (a[half] > a[begin])
{
// |----*----->|-----><----->|
// x n h * y
if ((n >= a[begin]) && (n < a[half]))
{
end = half;
}
else
{
begin = half + 1;
}
}
else
{
return -1;
}
}
return -1;
}