二分是一个比较基础的算法,花了一点时间实现了它。
简单介绍一下题目的要求,输入一个整数n,然后输入n个数,再输入你要查找的数,判断一下是否在这些数里面,在的话输出Yes,否则输出No。
代码:
#include<cstdio>
#include<iostream>
#include<stdlib.h>
#include<string.h>
#include<algorithm>
using namespace std;
int get_number[105];
int main()
{
int i,j;
int n; //length
int wanna; //the number want to find
while(scanf("%d",&n)!=EOF)
{
memset(get_number,0,sizeof(get_number));
for(i=1;i<=n;i++)
{
scanf("%d",&get_number[i]);
}
sort(get_number+1,get_number+n+1);
scanf("%d",&wanna);
bool is_found=false;
int right=n,left=1;
int mid=(right+left)/2;
if(wanna==get_number[right] || wanna==get_number[left])
{
printf("Yes\n");
continue;
}
while(left+1!=right)
{
if(get_number[mid]==wanna)
{
is_found=true;
break;
}
if(get_number[mid]>wanna)
{
right=mid;
mid=(right+left)/2;
}
else if(get_number[mid]<wanna)
{
left=mid;
mid=(right+left)/2;
}
}
if(is_found)printf("Yes\n");
else printf("No\n");
}
return 0;
}
/*
7
131 2 31 283 221 39 18
*/
while循环的条件判断是left+1!=right
,因为我在二分过程中直接把mid赋给left或right。
另外一种的二分:
bool bSearch(int a[],int num,int wanna)
{
int left=1,right=num,mid;
while(left<=right)
{
mid=(left+right)>>1;//右移一位 /=2;
if(a[mid]==wanna)return true;
else if(a[mid]<wanna)left=mid+1;
else right=mid-1;
}
return false;
}
需要注意的一点是,mid=(left+right)>>1
,利用右移的方法更新mid。
参阅了一些朋友的博客,发现这个算法还有可以优化的地方,如下。
- 1.首先, 如果序列中有多个相同的元素时,查找的时候不见得每次都会返回第一个元素的位置, 比如考虑一种极端情况:序列中都只有一个相同的元素,那么去查找这个元素时,显然返回的是中间元素的位置.
- 2.其次, 前面给出的算法中,每次循环体中都有三次情况,两次比较,有没有办法减少比较的数量进一步的优化程序?
解决代码:
int search4(int array[], int n, int v)
{
int left, right, middle;
left = -1, right = n; //开区间
while (left + 1 != right) //防止死循环
{
middle = left + (right - left) / 2; //防止溢出
if (array[middle] < v)
{
left = middle;
}
else
{
right = middle;
}
}
if (right >= n || array[right] != v)
{
right = -1;
}
return right;
}