引言
二分查找在算法界也是鼎鼎有名的,在初中课本中老师就已经提到过这一查找方法-二分查找。在这里什么是二分查找就不做阐述了。
所有源码均已上传至github: 链接
在解答如何求一个整数的平方根,并且精确到小数点第六位?之前,先举个例子,在0-100范围的数,随机指定一个67数,猜几次可以猜中?这个常规思路是对半猜:
- 第一次是 (0 + 100)/2 = 50, 67>50,则区间变成(50,100];
- 第二次是(50 + 100)/2 = 75,67<75,则区间变成(50,75);
- 第三次是(50 + 75)/2 = 62(这里向下取整),67>62,则区间变成(62,75);
- 第四次是(62 + 75)/2 = 68(这里向下取整),67<68,则区间变成(62,68);
- 第五次是(62 + 68)/2 = 65,67>65,则区间变成(65,68);
- 第六次是(65 + 68)/2 = 66这里向下取整),67>66,则区间变成(66,68);
- 第六次是(66 + 68)/2 = 67,67=67,猜中结果;
- 只需要七次即可(实际上100范围内最多七次).
二分查找它是一个极其高效的查找算法,它的时间复杂度只有O(n),理论上讲一般的O(1)查找都不一定能赶得上它,但是要注意这么几个问题:
- 数据结构一定要是数组(为什么不能是链表呢?因为数组是通过下标随机访问元素的,时间复杂度只有0(1),链表查找元素时间复杂度为O(n))
- 数组必须有序
二分查找
在一个有序不重复数组查找指定元素
在一个有序不重复数组[0,100]中查找指定元素67,具体实现如下
递归
low和high的取值一定要注意,不能相等,会造成死循环
private int bSearch(int[] arrays, int low, int high, int value) {
if (low > high) return -1;//默认-1未找到
//low + high如果足够大,相加之和容易溢出,因为换一种写法,并将其改成位运算
int mid = low + ((high - low) >> 1);
System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
if (arrays[mid] > value) {
return bSearch(arrays, low, mid - 1, value);
} else if (arrays[mid] < value) {
return bSearch(arrays, mid + 1, high, value);
}
return mid;
}复制代码
非递归
private int bSearch(int[] arrays, int value) {
int low = 0;
int high = arrays.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
System.out.println("(" + low + " + " + high + ") / 2 =" + mid);
if (arrays[mid] > value) {
high = mid - 1;
} else if (arrays[mid] < value) {
low = mid + 1;
} else {
return mid;
}
}
return -1;
}复制代码
测试结果
ps:上面呢个问题是查找有序不重复数组,实现起来比较简单,呢如果是查找一个有序的由重复数据的数组呢?
查找一个有序数组中第一次出现给定值的元素
这里的实现和查找有些类似就不做阐述了,直接上代码。具体实现如下
法一:
private int bSFindFirst(int[] arrays, int value) {
int low = 0;
int high = arrays.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
if (arrays[mid] >= value) {//将之前满足等于的放在左边
high = mid - 1;
} else if (arrays[mid] < value) {
low = mid + 1;
}
}
if (arrays[low] == value) return low;
return -1;
}复制代码
法二:
关键点:mid == 0 || arrays[mid - 1] != value
private int bSFindFirstT(int[] arrays, int value) {
int low = 0;
int high = arrays.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
if (arrays[mid] > value) {
high = mid - 1;
} else if (arrays[mid] < value) {
low = mid + 1;
} else {
if (mid == 0 || arrays[mid - 1] != value)
return mid;
else
high = mid - 1;
}
}
return -1;
}复制代码
测试结果
查找一个有序数组中最后一次出现给定值的元素
具体实现如下
private int bSFindLast(int[] arrays, int value) {
int low = 0;
int high = arrays.length - 1;
while (low <= high) {
int mid = low + ((high - low) >> 1);
System.out.println("(" + low + " + " + high + ") / 2 = " + mid);
if (arrays[mid] > value) {
high = mid - 1;
} else if (arrays[mid] < value) {
low = mid + 1;
} else {
if ((mid == arrays.length - 1) || (arrays[mid + 1] != value))
return mid;
else
low = mid + 1;
}
}
return -1;
}复制代码
测试结果
二分查找的变化非常灵活,查第一个小于的,第一个大于的等等等等,关键还是在于while循环里else里的if-else语句。
如何求一个整数的平方根,并且精确到小数点第六位?
(Alt + 41420打出根号√)
思路:以√2 = 1.41421为例,先分再平方
2/2=1 ,1*1 <2,
(1+2)/2 = 1.5,1.5*1.5=2.25
...以此类推。
实现代码如下
private double mySqrt(int num, double low, double high, double range) {
double mid = (low + high) / 2f;
// System.out.println("mid = " + mid);
if (num - mid * mid < 0 ) {
return mySqrt(num,low,(mid + high)/2f,range);
}else{
if (num - mid * mid > range){
return mySqrt(num,(low + mid)/2f,high,range);
}
String str = String.format("%.6f",mid);
return Double.valueOf(str);
}
}复制代码
测试结果
end
您的点赞和关注是对我最大的支持,谢谢!