二分查找的多种实现

本文深入探讨二分查找算法的关键技术和实现细节,包括溢出问题的解决方案、不同区间的使用方式及递归实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 折半查找(即二分查找)

二分搜索算法:给定排好序的n个元素arr[0:n-1],在这n个元素中找出一特定元素x. 


首先比较容易想到的办法是用顺序搜索方法,逐个比较a[0:n-1]中的元素 ,直至找出元素x或搜索遍整个数组后确定x不在其中,这个方法没有很好的利用n个元素已排好序这个条件,因此在最坏情况下,顺序搜索的方法需要O(n)次比较。


 而二分搜素搜的基本思想是将n个元素分成个数大致相同的两半,取arr[n/2]与x进行比较,如果x=arr[n/2],则找到x,算法终止。如果x<arr[n/2],则只要在数组arr的左半部继续搜索x。如果x>arr[n/2],则只要在数组arr的右半部继续搜索x。最坏情况下,也只需O(logn)次比较。

众所周知,二分查找是非常重要的查找方法,无论是笔试还是面试的过程中,都是如此,下面让我们一起来研究一下二分查找算法中的难点和考点。


1,溢出问题(迭代实现)

//代码1
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int bin_search(int arr[],int key,int sz)
{
    int i=0;
int left=0;
<span style="color:#ff0000;">int right=sz-1</span>;
while(<span style="color:#ff0000;">left<=right</span>)
{
<span style="color:#ff0000;">int mid=(left+right)/2;</span>  //如果left和right都很大,则mid将产生溢出,导致错误
    if(key==arr[mid])
{
    return mid;
}
else if(key<arr[mid])
{
    <span style="color:#ff0000;">right=mid-1</span>;
}
else
{
    left=mid+1;
}
}
   <span style="color:#ff0000;"> return -1</span>;
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9};
int key=5;
int sz=sizeof(arr)/sizeof(arr[0]);
int ret=bin_search(arr,key,sz);
if(ret==-1)
{
    printf("没找到所查找元素\n");
}
else
{
    printf("所查找元素的下标为:%d\n",ret);
}
system("pause");
return 0;
} 

注意:红色标注的都是最喜欢考到的地方

 正如代码中注释的那样,
//如果left和right都很大,则mid将产生溢出,导致错误这样的话,我们就不得不想出一个办法解决溢出问题,可以联系一下日常的爬楼梯。

//代码2:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int bin_search(int arr[],int key,int sz)
{
    int i=0;
int left=0;
int right=sz-1;
while(left<=right)
{
int mid=(right-left)/2+left;  //防止数据发生溢出
    if(key==arr[mid])
{
    return mid;
}
else if(key<arr[mid])
{
    right=mid-1;
}
else
{
    left=mid+1;
}
}
    return -1;
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9};
int key=5;
int sz=sizeof(arr)/sizeof(arr[0]);
int ret=bin_search(arr,key,sz);
if(ret==-1)
{
    printf("没找到所查找元素\n");
}
else
{
    printf("所查找元素的下标为:%d\n",ret);
}
system("pause");
return 0;
}

除了以上两种方法,还能不能有其他防止溢出的方法呢,

//代码3:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int bin_search(int arr[],int key,int sz)
{
    int i=0;
int left=0;
int right=sz-1;
while(left<=right)
{
int mid=(left&right)+((left^right)>>1);  //防止数据发生溢出
    if(key==arr[mid])
{
    return mid;
}
else if(key<arr[mid])
{
    right=mid-1;
}
else
{
    left=mid+1;
}
}
    return -1;
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9};
int key=1;
int sz=sizeof(arr)/sizeof(arr[0]);
int ret=bin_search(arr,key,sz);
if(ret==-1)
{
    printf("没找到所查找元素\n");
}
else
{
    printf("所查找元素的下标为:%d\n",ret);
}
system("pause");
return 0;
} 

第一种最简单,但是存在风险,第二种就比较常用了,第三种一般想不到,所以建议学会理解第二种防止溢出的办法。


2,区间问题

如果仔细观察,你就会发现上面几个代码用的都是前闭后闭区间,因为right是从sz-1开始取值的,那么如果是前闭后开区间,代码又会发生怎样的变化呢?让我们拭目以待。

<pre name="code" class="cpp">#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int bin_search(int arr[],int key,int sz)
{
    int i=0;
int left=0;
<span style="color:#ff0000;">int right=sz;</span>
while(<span style="color:#ff0000;">left<right</span>)
{
int mid=(left&right)+((left^right)>>1);  //防止数据发生溢出
    if(key==arr[mid])
{
    return mid;
}
else if(key<arr[mid])
{
   <span style="color:#ff0000;"> right=mid;</span>
}
else
{
    left=mid+1;
}
}
    return -1;
}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9};
int key=8;
int sz=sizeof(arr)/sizeof(arr[0]);
int ret=bin_search(arr,key,sz);
if(ret==-1)
{
    printf("没找到所查找元素\n");
}
else
{
    printf("所查找元素的下标为:%d\n",ret);
}
system("pause");
return 0;
} 



注意:红色标注是改变的地方

3,递归实现

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

int bin_search(int arr[],int left,int right,int key)
{
	while(left<right)
	{
	    int mid=(left&right)+((left^right)>>1);  //防止数据发生溢出
		if(key==arr[mid])
	{
		return mid;
	}
	else if(key<arr[mid])
	{
		return bin_search(arr,left,mid,key);
	}
	else
	{
		return bin_search(arr,mid+1,right,key);
	}
	}
		return -1;
	}
int main()
{
int arr[]={1,2,3,4,5,6,7,8,9};
int key=9;
int sz=sizeof(arr)/sizeof(arr[0]);
int ret=bin_search(arr,0,sz,key);
if(ret==-1)
{
    printf("没找到所查找元素\n");
}
else
{
    printf("所查找元素的下标为:%d\n",ret);
}
system("pause");
return 0;
} 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值