赞
踩
本篇采用的计算机语言是c语言,过程中会出现c++的部分函数,函数功能都会有所提示,如果你不会c语言也没关系,代码数量不多,主要是理解算法的实现过程。
我想,大家都玩过猜数字的游戏吧:从1~100中想一个数字,玩家进行猜测,每次猜测都要给玩家一个提示:大了、小了。如果我们从1开始猜,就会出现一下场景:
A:1
B:小了
A:2
B:小了
A:3
B:小了
……
这样,我们最多可能会猜测99次!!这是个很麻烦的事,但是如果我们这样猜:
A:50(1-100的中间数)
B:小了
A:75(50-100的中间数)
B:大了
A:63(50-75的中间数)
B:大了
A:57
B:对了!
这,就是二分查找,你已经学会这个算法的理论部分了!
每次猜测可以排除的数字个数如下:
100个元素-(第一步)-> 50-(第二步)->25-(第三步)->13-(第四步)->7-(第五步)->4-(第六步)->2-(第七步)->1(每次使用二分查找时都可以排除一半的数字)
这样,不管我心里想的是哪个数字,你都能在七次以内猜到,因为每次猜测都可以排除很多数字!
如果我们要从一本包含240000个字的字典里找到一个汉字,如果采用传统的连续查找最多需要240000次!!!(我想谁都不会这么无聊)。
所以我们如果采用二分查找的方法,最多查找多少次呢?
240000-120000-60000-30000-15000-7500-3750-1875-937-468-234-117-58-29-14-7-3-1
不用数了,我已经帮你数好了!最多需要17步就可以找到你想要的那个字了!
由此可见,二分查找极大的减少了我们查找的次数,这放在计算机上,就会极大的优化我们程序的运行时间!
运行时间:O(n)->O(log2n)
大家都知道log2n会随着n的增长逐渐与n拉开差距吧!
那么问题来了
Q:我们如果想找一个有序数列中的一个数怎么办呢?
A:我们应该先知道这个数列有多少位数字,通过计算(下标为:头部下标加尾部下标/2)找到中间的那个数字(后面我们称中间数),再比较需要找的数字和中间数的大小,如果需要找的数字大于中间数,那么中间数就要更改为:(前任中间数下标+1(因为我们已经知道前任中间数不是我们要找的数,所以我们从它的下一个数开始找)+尾部下标)/2,理所当然我们的头部下标就要更改为:前任中间数的下标+1。反之,我们把尾部下标改为前任中间数下标-1。我们重复这个过程,直到找到那个数字,或者找了一遍都没有找到这个数字,此时头部和尾部下标应该重合。
可能你看了上面那段文字仍旧不够理解,那么接下来请看代码部分!
我们通过从某一个数组内查找某一个数并返回为例子:
题目如下:
输入一个数字n表示一个数组的大小,下一行来输入n个数字(数字用空格隔开)表示数组的数据,接下来输入一个数字k,表示要查找的数字。
如果这个数字存在,则返回YES,否则返回NO。
Input
5
1 2 3 4 5
3
Output
YES
Input
5
1 2 3 4 5
9
Output
NO
#include<stdio.h> #include<algorithm>//排序函数所在的函数库 using namespace std;//排序函数要用到的 int a[1000000]; int bs(int tail,int num){//bs是二分查找英文缩写,这个你可以根据自己的喜好来自定义你的函数名称。首先给函数一个尾部下标tail,表示一个数组的大小,num为你要查找的数字。 int head = 0;//我们默认数组下标从0开始 while(tail>=head){//每次查找玩如果不是想查找的那个数字,都要更新中间数,我们通过更新尾部和头部下标来更新中间数,直到头部和尾部重合都没找到这个数字,说明这个数字不存在。 int mid = head+(tail-head)/2;//当然我们可以用mid=(head+tail)/2,这是另外一种写法,防止head+tail太大而导致程序无法运行。 if(num==a[mid]){ return 1;//如果我们要找的数字找到了,就返回1; } else if(num>a[mid]){ head = mid + 1;//我们要找的数字比中间数大,所以我们也就把头部下标移动到中间数+1位置,将下次计算的中间数增加。 } else{ tail = mid - 1;//反之,我们要减小中间数。 } } return 0;//如果tail==head了,说明这个数组里没有我们要找的那个数字,返回0 } int main(){ int n; scanf("%d",&n);//输入n for(int i=0;i<n;i++){ scanf("%d",&a[i]);//以此输入数组内容 } sort(a,a+n);//排序函数,为了防止输入的数据不是有序数列,若无序数列我们将无法找到中间数。 int k; scanf("%d",&k);//输入要找的数 int flag=bs(n-1,k);//我们输入n个数,由于下标是从0开始,所以最后一个尾部下标应该是n-1. if(flag==1){ printf("YES\n"); } else{ printf("NO\n"); } return 0; }
课下作业
输入一个数字n表示一个数组的大小,下一行来输入n个数字(数字用空格隔开)表示数组的数据,接下来输入一个数字k,表示要查找的数字。
如果这个数字存在,则输出这个数,否则输出离它最近的数字。
Input
5
1 2 3 4 5
3
Output
3
Input
5
1 2 3 4 5
9
Output
5
个人建议先跟着文章中的代码敲一遍,再去自己敲一遍,核心代码为函数bs部分,如果看到此处还不理解请在文章下方留言或私信我,我将为你一一解答。
答案:
#include<stdio.h> #include<algorithm> using namespace std; int a[1000000]; int bs(int tail,int num){ int head = 0; int mid; while(tail>=head){ mid = head+(tail-head)/2; if(num==a[mid]){ break; } else if(num>a[mid]){ head = mid + 1; } else{ tail = mid - 1; } } return mid; } int main(){ int n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } sort(a,a+n); int k; scanf("%d",&k); int flag=bs(n-1,k); printf("%d\n",a[flag]); return 0; }
博主也在努力的学习算法,希望我们可以共同进步!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。