鹰潭网站设计,网站订单系统模板下载,app那个网站开发比较好,哈尔滨seo整站优化一、什么是二分查找#xff1f;
二分查找针对的是一个有序的数据集合#xff0c;每次通过跟区间中间的元素对比#xff0c;将待查找的区间缩小为之前的一半#xff0c;直到找到要查找的元素#xff0c;或者区间缩小为0。
二、时间复杂度分析#xff1f;
1.时间复杂度 …一、什么是二分查找
二分查找针对的是一个有序的数据集合每次通过跟区间中间的元素对比将待查找的区间缩小为之前的一半直到找到要查找的元素或者区间缩小为0。
二、时间复杂度分析
1.时间复杂度 假设数据大小是n每次查找后数据都会缩小为原来的一半最坏的情况下直到查找区间被缩小为空才停止。所以每次查找的数据大小是nn/2n/4…n/(2k)…这是一个等比数列。当n/(2k)1时k的值就是总共缩小的次数也是查找的总次数。而每次缩小操作只涉及两个数据的大小比较所以经过k次区间缩小操作时间复杂度就是O(k)。通过n/(2^k)1可求得klog2n所以时间复杂度是O(logn)。 2.认识O(logn) ①这是一种极其高效的时间复杂度有时甚至比O(1)的算法还要高效。为什么 ②因为logn是一个非常“恐怖“的数量级即便n非常大对应的logn也很小。比如n等于2的32次方也就是42亿而logn才32。 ③由此可见O(logn)有时就是比O(1000)O(10000)快很多。
三、如何实现二分查找
1.循环实现 代码实现
public int binarySearch1(int[] a, int val){
int start 0;
int end a.length - 1;
while(start end){
int mid start (end - start) / 2;
if(a[mid] val) end mid - 1;
else if(a[mid] val) start mid 1;
else return mid;
}
return -1;
}注意事项 ①循环退出条件是startend而不是startend。 ②mid的取值使用midstart (end - start) / 2而不用mid(start end)/2因为如果start和end比较大的话求和可能会发生int类型的值超出最大范围。为了把性能优化到极致可以将除以2转换成位运算即start ((end - start) 1)因为相比除法运算来说计算机处理位运算要快得多。 ③start和end的更新start mid - 1end mid 1若直接写成start midendmid就可能会发生死循环。 2.递归实现
public int binarySearch(int[] a, int val){
return bSear(a, val, 0, a.length-1);
}
private int bSear(int[] a, int val, int start, int end) {
if(start end) return -1;
int mid start (end - start) / 2;
if(a[mid] val) return mid;
else if(a[mid] val) end mid - 1;
else start mid 1;
return bSear(a, val, start, end);
}四、使用条件应用场景的局限性
1.二分查找依赖的是顺序表结构即数组。 2.二分查找针对的是有序数据因此只能用在插入、删除操作不频繁一次排序多次查找的场景中。 3.数据量太小不适合二分查找与直接遍历相比效率提升不明显。但有一个例外就是数据之间的比较操作非常费时比如数组中存储的都是长度超过300的字符串那这是还是尽量减少比较操作使用二分查找吧。 4.数据量太大也不是适合用二分查找因为数组需要连续的空间若数据量太大往往找不到存储如此大规模数据的连续内存空间。
五、四种常见的二分查找变形问题
1.查找第一个值等于给定值的元素 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low high) {int mid low ((high - low) 1);if (a[mid] value) {high mid - 1;} else if (a[mid] value) {low mid 1;} else {if ((mid 0) || (a[mid - 1] ! value)) return mid;else high mid - 1;}}return -1;
}2.查找最后一个值等于给定值的元素 如果 a[mid]这个元素已经是数组中的最后一个元素了那它肯定是我们要找的如果 a[mid]的后一个元素 a[mid1]不等于 value那也说明 a[mid]就是我们要找的最后一个值等于给定值的元素。 如果我们经过检查之后发现 a[mid]后面的一个元素 a[mid1]也等于 value那说明当前的这个 a[mid]并不是最后一个值等于给定值的元素。我们就更新 lowmid1因为要找的元素肯定出现在[mid1, high]之间。 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low high) {int mid low ((high - low) 1);if (a[mid] value) {high mid - 1;} else if (a[mid] value) {low mid 1;} else {if ((mid n - 1) || (a[mid 1] ! value)) return mid;else low mid 1;}}return -1;
}3.查找第一个大于等于给定值的元素 如果 a[mid]小于要查找的值 value那要查找的值肯定在[mid1, high]之间所以我们更新 lowmid1。 对于 a[mid]大于等于给定值 value 的情况我们要先看下这个 a[mid]是不是我们要找的第一个值大于等于给定值的元素。如果 a[mid]前面已经没有元素或者前面一个元素小于要查找的值 value那 a[mid]就是我们要找的元素。 如果 a[mid-1]也大于等于要查找的值 value那说明要查找的元素在[low, mid-1]之间所以我们将 high 更新为 mid-1。 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low high) {int mid low ((high - low) 1);if (a[mid] value) {if ((mid 0) || (a[mid - 1] value)) return mid;else high mid - 1;} else {//肯定low mid 1;}}return -1;
}4.查找最后一个小于等于给定值的元素 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low high) {int mid low ((high - low) 1);if (a[mid] value) {if ((mid 0) || (a[mid - 1] value)) return mid;else high mid - 1;} else {low mid 1;}}return -1;
}六、适用性分析
1.凡事能用二分查找解决的绝大部分我们更倾向于用散列表或者二叉查找树即便二分查找在内存上更节省但是毕竟内存如此紧缺的情况并不多。 2.求“值等于给定值”的二分查找确实不怎么用到二分查找更适合用在”近似“查找问题上。比如上面讲几种变体。
五、思考
1.如何在1000万个整数中快速查找某个整数 我们的内存限制是 100MB每个数据大小是 8 字节最简单的办法就是将数据存储在数组中内存占用差不多是 80MB符合内存的限制。我们可以先对这 1000 万数据从小到大排序然后再利用二分查找算法就可以快速地查找想要的数据了。 ①1000万个整数占用存储空间为40MB占用空间不大所以可 以全部加载到内存中进行处理 ②用一个1000万个元素的数组存储然后使用快排进行升序排序时间复杂度为O(nlogn) ③在有序数组中使用二分查找算法进行查找时间复杂度为O(logn) 2.如何编程实现“求一个数的平方根”要求精确到小数点后6位
public static double sqrt(double x, double precision) {
if (x 0) {
return Double.NaN;
}
double low 0;
double up x;
if (x 1 x 0) {
/** 小于1的时候*/
low x;
up 1;
}
double mid low (up - low)/2;
while(up - low precision) {
if (mid * mid x ) {//TODO mid可能会溢出 改成mid x / mid
up mid;
} else if (mid * mid x) {
low mid;
} else {
return mid;
}
mid low (up - low)/2;
}
return mid;
}3.如何快速定位出一个IP地址的归属地 [202.102.133.0, 202.102.133.255] 山东东营市 [202.102.135.0, 202.102.136.255] 山东烟台 [202.102.156.34, 202.102.157.255] 山东青岛 [202.102.48.0, 202.102.48.255] 江苏宿迁 [202.102.49.15, 202.102.51.251] 江苏泰州 [202.102.56.0, 202.102.56.255] 江苏连云港 假设我们有 12 万条这样的 IP 区间与归属地的对应关系如何快速定位出一个IP地址的归属地呢 如果 IP 区间与归属地的对应关系不经常更新我们可以先预处理这 12 万条数据让其按照起始 IP 从小到大排序。如何来排序呢我们知道IP 地址可以转化为 32 位的整型数。所以我们可以将起始地址按照对应的整型值的大小关系从小到大进行排序 。然后这个问题就可以转化为我刚讲的第四种变形问题“在有序数组中查找最后一个小于等于某个给定值的元素”了。 当我们要查询某个 IP 归属地时我们可以先通过二分查找找到最后一个起始 IP 小于等于这个 IP 的 IP 区间然后检查这个 IP 是否在这个 IP 区间内如果在我们就取出对应的归属地显示如果不在就返回未查找到。
笔记整理来源 王争 数据结构与算法之美