二分查找实现及其应用
来源:互联网 发布:linux 统计登录次数 编辑:程序博客网 时间:2024/05/01 07:45
1、二分查找实现
迭代:
template<typename T>int BinarySearch(T *array,T low,T high,T value){ while(low<=high) { int mid=(low+high)/2; if(array[mid]==value) return mid; else if(array[mid]>value) high=mid-1; else low=mid+1; } return -1;}
递归:
template<typename T>int BinarySearch(T *array,int low,int high,T value){ if(low<=high) { int mid=(low+high)/2; if(array[mid]==value) return mid; else if(array[mid]>value) { return BinarySearch(array,low,mid-1,value); } else { return BinarySearch(array,mid+1,right,value); } } return -1;}
2、二分查找应用
1、求=target的first位置
int search(int A[], int n, int target) { int low = 0, high = n-1,pos=-1; while(low <= high) { // 注意:若使用(low+high)/2求中间位置容易溢出 int mid = low+((high-low)>>1); if(A[mid] == target) { pos=mid; if(mid > 0 && A[mid-1] == target) high = mid-1; } else if(A[mid] < target) low = mid+1; else // A[mid] > target high = mid-1; } return pos; }
2、求=target的last位置
int search(int A[], int n, int target) { int low = 0, high = n-1,pos=-1; while(low <= high) { // 注意:若使用(low+high)/2求中间位置容易溢出 int mid = low+((high-low)>>1); if(A[mid] == target) { pos=mid; if(mid <n-1 && A[mid+1] == target) low= mid+1; } else if(A[mid] < target) low = mid+1; else // A[mid] > target high = mid-1; } return pos; }
有了上面这两个方法,求有序数组中数字出现的次数,以及求小于target最大的一个(最小target位置减一),以及大于target最小的一个(最大target位置加一)就迎刃而解了!
3、计算有序数组中数字出现的次数
在给定的一个已经排好序的数组中,找出指定数字出现的次数。例如数组[1,2,3,3,3,4,5]中3出现的次数为3次。
此问题可以在二分法的基础上进行改进。假设数组array为递增的数列,需要查找的数字为num,可以分别查找num在数组array中出现的起始位置和最后一次的位置,通过二者的差计算出数字num在数组array中出现的次数。
template<typename T>int BinarySearch(T *array,T size,T value,bool isLeft){ int low=0; int high=size-1; int pos=-1; while(low<=high) { int mid=(low+high)/2; if(array[mid]==value) { pos=mid; if(isLeft) //查找这个数最左的位置 { if(mid>0&&array[mid-1]==target) high=mid-1; } else //查找这个数最右的位置 { if(mid<n-1&& array[mid+1]==target) low=mid+1; } } else if(value<array[mid]) high=mid-1; else low=mid=1; } return pos;}//使用left = FindCntofNum(a,10,4,true);right = FindCntofNum(a,10,4,false);int num=right-left+1;
4、二分查找排序
直接插入排序的思路是对于无序序列的第一个元素,从后至前进行顺序查找 扫描有序序列寻找合适的插入点。改进后的二分插入排序算法使用二分查找在有序序列中查找插入点,将插入排序的比较次数降为O(log2n)。这个思路的实现代码为:
/*二分查找函数,返回插入下标*/template <typename T>int BinarySearch(T *array, int start, int end, T k){ while (start <= end) { int middle = (start + end) / 2; int middleData = array[middle]; if (middleData > k) { end = middle - 1; } else start = middle + 1; } return start;}//二叉查找插入排序template <typename T>void InsertSort(T *array, int length){ if (array == NULL|| length < 0) return; int i, j; for (i = 1; i < length; i++) { if (array[i]<array[i - 1]) { int temp = array[i]; int insertIndex = BinarySearch(array, 0, i, array[i]);//使用二分查找在有序序列中进行查找,获取插入下标 for (j = i - 1; j>=insertIndex; j--) //移动元素 { array[j + 1] = array[j]; } array[insertIndex] = temp; //插入元素 } }}
5、旋转数组中的最小数字
问题描述:把一个有序数组最开始的若干个元素搬到数组的末尾,我们称为数组的旋转,输入一个递增排序数组的一个旋转,输出旋转数组的最小元素。例如数组{4,5,6,1,2,3}为数组 {1,2,3,4,5,6}的一个旋转,最小旋转元素为1.
解决思路:
寻找数组中最小的元素,我们可以遍历数组,时间复杂度为O(n)。但是借助二分查找的思想,我们能够在O(logn)的时间复杂度内找到最小的元素。旋转数组的特点是数组中两个部分都分别是有序的,以{4,5,6,1,2,3}为例,{4,5,6}是非递减的,{1,2,3}也是非递减的:
我们可以定义两个索引:start指向第一部分的起始位置;end指向第二部分的最后一个元素,如图所示。由于数组在旋转前整体有序,故array[start]>=array[end],而中间值array[middle]满足:
- 如果array[middle]大于array[start] ,则array[middle]属于第一部分。此时令start= middle ;
- 如果array[middle]小于array[start] ,则array[middle]属于第二部分。此时令end = middle ;
end所指元素便是最小元素。
这个寻址的过程有一个隐喻的要求:中间这个元素必须能够判断它是属于第一部分还是第二部分。在有些输入下,这个要求不能满足,例如数组:{0,1,1,1,1},它的两个选择数组为:
{1,0,1,1,1}
{1,1,1,0,1}
此时因array[middle] == array[start] == array[end] 而无法判断array[middle]属于哪一部分,我们只能进行顺序查找找出最小元素。
此时无法确定array[middle]是属于第一部分还是第二部分,这种情况下我们需要进行顺序查找。
因此,我们的代码实现为:
//顺序查找函数int Min(int array[], int length){ int result = array[0]; for (int i = 1; i < length; i++) { if (array[i] < result) result = array[i]; } return result;}int MinInRotation(int array[],int length){ int result = -999; if (array == nullptr || length < 0) return result; int start = 0; int end = length - 1; int middle = start; while (start < end) { if (start + 1 == end) { result = array[end]; break; } middle = (start + end)/2; //如果遇上特殊情况,则需要进行顺序查找 if (array[middle] == array[start] && array[middle] == array[end]) return Min(array,length); //调用顺序查找函数 //否则;中间元素属于第一部分 if (array[middle]>=array[start]) { start = middle; }//中间元素属于第二部分 else if (array[middle] <= array[start]) { end = middle; } } return result;}
参考资料:
数据结构图文解析之:二分查找及与其相关的几个问题解析
二分法计算有序数组中数字出现的次数
二分查找问题全集OK
- 二分查找实现及其应用
- 二分查找及其应用
- 二分查找法及其应用
- 二分查找及其变化应用
- 二分查找及其变形
- 二分查找及其变体
- 二分查找及其扩展
- 二分查找及其拓展
- 巩固基础篇:经典二分查找模型及其应用
- 20、二分查找的实现与应用
- 二分查找算法的递归、循环实现及其缺陷
- 二分查找及其相关算法的java实现
- 二分查找的非递归、递归实现及其优化
- 二分查找算法原理及其java循环实现
- 二分查找及其变形算法
- 二分查找及其变形总结
- 二分查找算法及其变种
- 二分查找法及其扩展
- HDU 3068 最长回文子串(manacher算法)
- visual studio项目调错 LINK : fatal error LNK1104: 无法打开文件“IlmImf.lib”
- VC 6.0 调试快捷键说明.
- c#23中设计模式
- Class内存小记
- 二分查找实现及其应用
- 【web】上机实验一
- web开发模式+三层架构与MVC
- HTML 4.01的标准和规范以及如何使用中文
- python库
- Filter Initiated I/O
- 常见的编译错误
- Android 从源码角度分析消息处理机制(Handler,Looper,Message)
- 单链表-用尾插法创建单链表