二分查找
刚刚入手了《算法图解》,打算自己按书实现一下代码。这是本不错的算法入门书,有兴趣可以自己看看。
原书使用python,我用Java实现。
下面介绍一下二分法
思考这样一个问题:
你的朋友想要测试你们的心有灵犀度,告诉你:她心中在1-100之间想了一个数,每猜一次,会告知你正误还有你报的数比她想的是大了还是小了。想看你几次能猜中。(这搞不好就是道送命题)当然如果你们很有默契,一次猜中也不是不可能。那如果没信心一次就中呢?
我们的目的是:用尽可能少的次数猜到它。
这里有两种常见方法:
简单查找:简单粗暴,直接猜,随便猜,一共就100个数,100次内一定解决
二分查找: 二分查找是一种算法,其输入是一个有序的元素列表,必须有序,如果要查找的元素包含在列表中,二分查找返回位置,否则返回null。
简单理解一下,就是1-100,我先猜测100,再猜50,你告诉我了大(小),我一次就排除了50个数。如果是大了,再猜25,如果还是大了,就猜13,猜7,猜3......这样我们就能7步找出结果,大大减少猜测的次数,提高效率。
对一个有序的有n个元素的列表而言,使用简单查找最多需要n步。而使用二分查找最多就要logn(以2为底n的对数)步。
代码:
package SimpleDichotomy;
import java.util.ArrayList;
import javax.swing.text.Highlighter.Highlight;
//简单二分法查找
public class dichotomy {
private static int aim;
private static int guess;
private static int low;
private static int high;
public static void main(String[] args) {
//private ArrayList<Integer> my_list=new ArrayList<Integer>();
int [] mylist={2,4,6,8,78,88,99,101,109,110};//10 number
aim=2;
int ans=dichotomy(mylist,aim);
System.out.println(ans);
}
private static int dichotomy(int[] mylist, int aim2) {
low=0;
high=mylist.length-1;
guess=high;
while(mylist[guess]!=aim2){
guess=(low+high)/2;
if(mylist[guess]>aim2){
high=guess;
}else if(mylist[guess]<aim2){
low=guess;
}
}
return guess;
}
}
以上代码是我看完二分法的定义后乱实现的。emmm没想到吧。但是这里存在bug---就是如果要查找的数字不存在不会返回null,而是会不输出。所以在此基础上,我又添加了判断是序列里是否有的目标数的循环。然后问题又出现了:我数组定义类型是整型,也就意味着返回不了null.
2.0
package SimpleDichotomy;
import java.util.ArrayList;
import javax.swing.text.Highlighter.Highlight;
//简单二分法查找
public class dichotomy {
private static int aim;
private static int guess;
private static int low;
private static int high;
//static:
public static void main(String[] args) {
//private ArrayList<Integer> my_list=new ArrayList<Integer>();
int [] mylist={2,4,6,8,78,88,99,101,109,110};//10 number
aim=110;
dichotomy(mylist,aim);
//System.out.println(ans);
}
private static void dichotomy(int[] mylist, int aim2) {
low=0;
high=mylist.length-1;
guess=high;
int count=0;
for(int i=0;i<mylist.length;++i){
if((aim2!=mylist[i])&&(count==mylist.length)){
System.out.println(""+null);
}
count++;
}
while(mylist[guess]!=aim2){
guess=(low+high)/2;
if(mylist[guess]>aim2){
high=guess;
}else if(mylist[guess]<aim2){
low=guess;
}
}
System.out.println(""+guess);
}
}
刚刚测试了自己的代码,发现有个致命的bug,就是当目标数不在数组时,程序会死在while循环里,因为我while循环的跳出条件是mylist[guess]=aim.如果一直找不到和aim相等的,就会一直在while循环里不出来。而且二分法的思路其实也不是最开始就要判断数组中是否存在目标数,而是在不断缩小范围中,若是找不到就是不存在。
而当我改变while判断条件为(low<=high)时,当|(high-low)|=1时,也会无法跳出循环。
实现之后,发现它丑(后来发现它还不对emm)。一个码农还是要有审美的。所以我写了书上的代码。
package SimpleDichotomy;
import java.util.ArrayList;
import javax.swing.text.Highlighter.Highlight;
//简单二分法查找
public class dichotomy {
private static int aim;
private static int guess;
private static int low;
private static int high;
private static int mid;
//static:
public static void main(String[] args) {
int [] mylist={2,4,6,8,78,88,99,101,109,110};//10 number
aim=101;
String s=dichotomy1(mylist,aim);
System.out.println(s);
}
private static String dichotomy1(int[] mylist, int aim2) {
// 原书代码
low=0;//low和high用于跟踪要在其中查找的列表部分
high=mylist.length-1;//low和high用于跟踪要在其中查找的列表部分
while(low<=high){//只要范围没有缩小到只包含一个元素
mid=(low+high)/2;//就检查中间的元素
guess=mylist[mid];
if(guess==aim2)//找到元素
return (""+mid);
if(guess>aim2){//猜测的数大了
high=mid-1;
}
else //猜测的数小了
low=mid+1;
}
return null; //不存在要查找的数
}
所以说啊,经典的还是经典。但自己去实现一次,也会有收获,知道自己怎么错的,比知道如何正确,更有意义。
以上.END.