1.基本思想
说明:元素必须是有序的,如果是无序的则要先进行排序操作。
二分查找也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表(是左还是右),这样递归进行,直到查找到或查找结束发现表中没有这样的结点。
复杂度分析:最坏情况下,关键词比较次数为log2(n+1),且期望时间复杂度为O(log2n);
注:折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再变化,折半查找能得到不错的效率但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。
2.算法步骤
待搜索的数据集一定是顺序的,若不是顺序的,则先进行排序!!!
无论是迭代法还是递归法,二分查找的基本思想都是一样的!
3.代码实现
3.1.算法实现
3.1.1.迭代法
int BinarySearch_iteration(int a[], int value, int n)
{
int low, high, mid=0;//这里的low、high、mid是索引值
low = 0;
high = n - 1;
while (low <= high)//当low == high 为最后一次检查,如果还不相同则返回
{
mid = (low + high) / 2;//如果low + high等于奇数,向下取整。也就是左边
if (a[mid] == value)//匹配到直接返回
return mid;
if (a[mid] > value)//如果是最后一次,即left==right,则high<low
high = mid - 1;//跳出while大循环
else
low = mid + 1;
}
return -1;
}
3.1.2.递归法
int BinarySearch_recursive(int a[], int value, int low, int high)
{
int mid = low + (high - low) / 2;//mid = (low + high) / 2表达式变种
if (a[mid] == value)//递归的终止条件,前提是value在数组中存在
return mid;
if (low == high)//这已经是最后一次查找了,这一次value还不相同,则返回
{//由于只会往某种特定的方向递归,而不会出现先左递归回溯再后右递归回溯,所以low==high 是递归的最后一次。由于上面一个语句已经检查了,如果没有直接返回,那么就意味着value不相同,直接返回-1即可
return -1;
}
if (a[mid] > value)//只会往某种特定的方向递归,而不会出现先左递归后右递归
return BinarySearch_recursive(a, value, low, mid - 1);
else
return BinarySearch_recursive(a, value, mid + 1, high);
}
3.2.测试程序
void main()
{
int a[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};//数组一定是顺序的
//待搜索的数据集中包含value值
int recv_iter = BinarySearch_iteration(a,5,16);//找到返回索引值,否则-1
int recv_recu = BinarySearch_recursive(a,5, 0, 16 - 1);
if (recv_iter !=-1)
{
printf("\nrecv_iter offset position is :%d", recv_iter);
}
else
{
printf("\nrecv_iter can not find value \n");
}
if (recv_recu != -1)
{
printf("\nrecv_recu offset position is :%d", recv_recu);
}
else
{
printf("\nrecv_recu can not find value \n");
}
//待搜索的数据集中没有包含value值
int recv_iter_1 = BinarySearch_iteration(a,16,16);//找到返回索引值,否则-1
int recv_recu_1 = BinarySearch_recursive(a,16, 0, 16 - 1);
if (recv_iter_1 !=-1)
{
printf("\nrecv_iter_1 offset position is :%d", recv_iter_1);
}
else
{
printf("\nrecv_iter_1 can not find value \n");
}
if (recv_recu_1 != -1)
{
printf("recv_recu_1 offset position is :%d", recv_recu_1);
}
else
{
printf("recv_recu_1 can not find value \n");
}
system("pause");
}
4.代码细节
4.1.迭代和递归终止条件
无论是迭代法还是递归法,终止条件都是low==high
但是为什么要加上一个等号呢?如果不加等号不可以吗?
答案:加上等号是为了防止在搜索的结尾处两端的数据漏掉
这个地方就很妙,大家可以用下面的例子:
0 1 2 3 和 0 1 2 3 4 这两个为例子进行分析,之所以用两个是因为数据集的长度既可以为奇数也可以为偶数。
4.2.递归终止条件奇异原因
在递归法中,如果运行到if(low == high)
说明这已经是最后一次查找了。
但是由于该句的上一句已经对mid处的key和value的值进行比对,如果比对的结果相同,那就意味着直接返回,不会运行到该句。如果没有直接返回,那么就意味着value不相同,直接返回-1即可。
因此如果该条件成立if(low == high)
,那么就意味着在数据集中找不到待搜索的元素,又由于程序只会往某种特定的方向递归,而不会出现先左递归回溯再后右递归回溯,所以
当在数据集中找不到待搜索的元素,这个语句就是程序的唯一出口!!!