在有序表中高效查找元素的常用方法是二分查找,所谓二分即是折半,遵循分治的思想,每次将元序列划分成数量相等的两个子序列,然后查找,最终定位到目标元素。以下是代码(假设data序列按递增序排列):
int binary_search(int *data, int size, int value)
{
int mid;
int left = 0;
int right = size - 1;
while(left < right)
{
mid = left + (right-left)/2; // 确保中点靠近区间的起点
if(data[mid] == value) return mid; //找到了
else if(data[mid] > value) right = mid; // 中间值较大,则查找值应在左半部分
else left = mid + 1; //否则在右半部分
}
return -1; //未找到,返回-1;
}
上述代码存在一个问题,就是当序列中存在多个value的时候,binary_search会返回最中间的那一个元素,有时候这不是我们想要的结果,换句话说这样就无法确定值等于value的完整区间,所以还需要对上面的代码进行改进,使得其可以查找出value出现的第一个位置和最后一个位置,其实就是对等于情况的讨论.
/*
该函数当value存在时返回它出现的第一个位置,
如果value不存在则返回位置i,满足在该位置插入value后该序列仍然有序
*/
int lower_bound(int *data, int size, int value)
{
int mid;
int left = 0;
int right = size - 1;
while(left < right)
{
mid = left + (right-left)/2;
if(data[mid] >= value) right = mid; //这两句代码对比上面的其实只对有相同value的区间有影响
else left = mid + 1;
}
return left;
}
/*
该函数当value存在时返回它出现的最后一个位置的后面一个位置,因为起点会移动到中点加一
如果value不存在则返回位置i,满足在该位置插入value后该序列仍然有序
*/
int upper_bound(int *data, int size, int value)
{
int mid;
int left = 0;
int right = size - 1;
while(left < right)
{
mid = left + (right-left)/2;
if(data[mid] > value) right = mid; //这两句代码对比上面的其实只对有相同value的区间有影响,返回的是最后一个value的后一个位置
else left = mid + 1;
}
return left;
}
所以,实现了前面两个函数,就可以使用它们来找出value的出现范围,假设lower_bound和upper_bound的返回值分别为L和R,那么value出现的子序列为[L,R),注意是前闭后开的一个区间,其实当value不存在时也成立,此时L等于R,即区间为空。