活动地址:CSDN21天学习挑战赛
学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。
一.什么是二分查找?
二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。
二.基本思想
首先用要查找的关键字key与中间位置的结点的关键字相比较,这个中间结点把线性表分成了两个子表,若比较结果相等则查找完成;若不相等,再根据k与该中间节点关键字的的比较大小来去确定下一步查找哪个子表(如果我们把一个线性表想成是水平的,如果key值大于中间节点,则在右边的子表继续查找;如果key值小于中间值,则在左边的子表继续查找),这样递归下去,直到找到满足条件的节点或者该线性表中没有这样的节点。
图解:
1.

2.

3.

三.二分查找的优缺点
优点:
比较次数少,查找速度快,平均性能好。
缺点:
必须要求待查表为有序表(若为无序数组,分成两份查找没有意义,排序本身也要耗费时间);插入删除困难(曾删操作需要移动大量的节点)
四.时间以及空间复杂度
1.时间复杂度分析:
最坏的情况下:两种方式时间复杂度一样:O(log2 N)
最好情况下:O(1)
2.空间复杂度分析:
算法的空间复杂度并不是计算实际占用的空间,而是计算整个算法的辅助空间单元的个数
1.循环方式:
由于辅助空间是常数级别的所以:
空间复杂度是O(1);
2. 递归方式:
递归的次数和深度都是log2 N,每次所需要的辅助空间都是常数级别的:
空间复杂度:O(log2N )
五.二分查找的注意事项
1.while 中的循环条件:left < right 还是 left <= right
2.对于右侧边界的处理:right = middle 还是 right =middle-1对于以上细节的区分处理主要取决于查找区间的形式:左闭右闭区间 还是 左闭右开区间
3.间值mid的取法可能是学会二分法后,几乎是必遇的一个问题。很多人往往是用上述伪代码中的mid=(low+high)/2来取,这样的写法在low和high的值很大的时候就可能存在溢出的问题,像C,C++等,int、long这些都是有范围的,这样写轻则得不到结果,重则死循环程序崩溃,虽然这种情况在业务操作中极少出现,但还是建议用mid=low+(high-low)/2来取。
六.使用条件
因为二分查找需要方便定位查找的区域,所以其存储结构必须具有随机存取的特性,即该查找方式仅适用于线性表的顺序存储结构,不适用于链式存储结构,且元素要求按关键字或对象的某一属性有序排列。
六.代码实现:
流程
先判断为否为有顺序的表,然后想好是找位置还是找次数,最后判断使用递归还是非递归,记得确定好中值(mid),不要越界。
查找某数的位置(或存在性)
1.非递归
- 1 //返回"-1"表示为找到
- 2 //否则返回目标的下标(若有多个,只是其中一个)
- 3 int binary_searchs(int * arr, int x, int l, int r)
- 4 {
- 5 int lt = l, rt = r;
- 6 while (lt <= rt)
- 7 {
- 8 int mid = (lt + rt) >> 1;
- 9 if (arr[mid] == x) return mid;
- 10 else if (arr[mid] < x)
- 11 lt = mid + 1;
- 12 else
- 13 rt = mid - 1;
- 14 }
- 15 return -1;
- 16 }
2.递归
- int binary_searchs(int *arr, int target, int l, int r)
- {
- if (l > r) return -1;
- int mid = (l + r) >> 1;
- if (arr[mid] == target)
- return mid;
- else if (arr[mid] > target)
- binary_search(arr, target, l, mid - 1);
- else
- binary_search(arr, target, mid + 1, r);
- }
- //返回"-1"表示为找到
- //否则返回目标的下标(若有多个,只是其中一个)
查找某数出现的次数
1.非递归
- 1 int binary_searchs(int *arr, int target,int l, int r)
- 2 {
- 3 int res = 0;
- 4 while (l <= r)
- 5 {
- 6 int mid = (l + r) >> 1;
- 7 if (arr[mid] == target)
- 8 {
- 9 if (l == r)
- 10 {
- 11 res++;
- 12 break;
- 13 }
- 14 for (int i = mid + 1; i <= r; i++)
- 15 if (arr[mid] == arr[i])
- 16 res++;
- 17 for (int i = mid - 1; i >= l; i--)
- 18 if (arr[mid] == arr[i])
- 19 res++;
- 20 res++;
- 21 break;
- 22 }
- 23 else if (arr[mid] < target)
- 24 l = mid + 1;
- 25 else if (arr[mid] > target)
- 26 r = mid - 1;
- 27 }
- 28 return res;
- 29 }
2.递归
- 1 //返回target的出现次数
- 2 //返回0意味着不存在
- 3 int binary_search(int *arr, int target, int l, int r)
- 4 {
- 5 if (l > r) return 0;
- 6 int mid = (l + r) >> 1;
- 7 if (arr[mid] == target)
- 8 return 1 + binary_search(arr, target, l, mid - 1) + binary_search(arr, target, mid + 1, r);
- 9 else if (arr[mid] > target)
- 10 binary_search(arr, target, l, mid - 1);
- 11 else
- 12 binary_search(arr, target, mid + 1, r);
- 13 }