二分法类型总结
来源:互联网 发布:沈阳市网络金融 编辑:程序博客网 时间:2024/06/05 17:12
二分法
能用二分法处理的问题都有一个共同特征即题目具有单调性。
二分法的题目类型如下:
1. 基本二分:对有序数组进行查询
2. 查找下届:有序数组中如果要查找的数存在则返回第一次出现的位置,否则返回比该数大的第一个数的位置,STL的lower_bound实现了此功能。
1) SGI STL源码:
int* LowerBound(int* first,int* end,int num){int mid,size = end - first;while( size > 0 ){mid = size >> 1;if(*(first + mid) < num) {first += (mid + 1);size -= (mid + 1);} else {size = mid;}}return(first);}
此版本代码最经典,但是理解很困难
2) 我实现版本:
int LowerBound(int a[],int n,int key){//特判key大于a中所有数的特殊情况if(key > a[n - 1]){return(n);}int s,e,mid;s = 0; e = n - 1;while(e - s > 1){mid = (s + e) >> 1;if(a[mid] < key){s = mid + 1;}else{e = mid;}}if(a[s] >= key){return(s);}return(e);}
3. 查找上届:和查找下届类似
4. 查找轮转后的有序数组
轮转后的有序数组(Rotated Sorted Array):有序数组,取其中某一个数为轴,将其之前的所有数都轮转到数组的末尾所得。比如{7, 11, 13, 17, 2, 3, 5}就是一个轮转后的有序数组。用二分查找思路如下:mid将数组分为两部分,其中一个是单调的,另一个是范围更小的轮转数组,在单调的那一部分,判断查找的数是否在范围内即可确定是否选择之。因为mid将数组只分成了两部分,如果不选择单调部分,那么必然选择另一部分,这样一直二分下去即可完成查找。注意:输入数据中不能存在相同元素,否则查询可能失败,原因如下图:当a[s] = a[mid] = a[e]时,没法确定单调区间和轮转区间,导致二分失败。
代码如下:
//代码能运行的前提是数组中不能存在相同的元素
// int a[N]数组是全局的
int Search(int num,int n){int s,e,mid;s = 0; e = n - 1;while(s <= e){mid = (s + e) / 2; if(a[mid] == num){return(mid);}if(a[s] <= a[mid]) // [s,mid]单调{if(a[s] <= num && a[mid] >= num) // num在[s,mid-1]之间{e = mid - 1;}else //num在[mid+1,e]之间{s = mid + 1;}}else if(a[mid] <= a[e]) //[mid,e]单调{if(a[mid] <= num && a[e] >= num) //num在[mid+1,e]之间{s = mid + 1;}else //num在[s,mid-1]之间{e = mid - 1;}}else if(a[s] == a[mid] && a[mid] == a[e]) // a[s] = a[mid] = a[e]时不能确定区间单调性{// printf("%d %d %d %d \n",s,mid,e,a[s]);cout << "测试数据出现奇异性" << endl;return(-1);}}
5. 找出轮转后的有序数组的突变点
思路:二分时不选择单调区间,选择轮转区间。
代码://保证数组中无相同元素
int getSkip(int n){if(n == 1){cout << "n必须大于1" << endl;return(-1);}int s,e,mid;s = 0;e = n - 1;while(e - s > 1){ mid = (s + e) / 2;//寻找非单调区间进行迭代if(a[s] >= a[mid]) // [s,mid]是非单调区间{ e = mid;}else{s = mid;}}return(a[s] > a[e] ? e : 0);}
6. 找出两个有序数组中第K个数:假设有长度分为为m和n的两个升序数组A和B,在A和B两个数组中查找第K大的数,即将A和B按升序合并后的第K个数。
1) 用归并排序的合并函数,直接合并成有序数组C,即可。复杂度:O(k)
2) 二分:二分A数组,将A[mid]利用lower_bound查询A[mid]z在B中的位置r1,那么A[0]…..A[mid]合并到B数组后,A[mid]位置在:r1 + mid + 1,比较r1 + mid + 1和k的大小,选择二分的区间即可,可以思考下为什么可以这样二分(从开篇第一句话着手吧)?。复杂度:lg(n)*lg(m)
3) 更优的二分:
int FindKthElm(int A[], int aBeg, int aEnd, int B[], int bBeg, int bEnd, int k) { if (aBeg > aEnd) { return B[bBeg + k - 1]; } if (bBeg > bEnd) { return A[aBeg + k - 1]; } //取中间位置 int aMid = aBeg + (aEnd - aBeg)/2; int bMid = bBeg + (bEnd - bBeg)/2; //从A和B的开始位置到两个数组中间位置的元素个数 int halfLen = aMid - aBeg + bMid - bBeg + 2; if (A[aMid] < B[bMid]) { if (halfLen > k) { // 此时在合并的数组中A[aBeg...aMid]和元素一定在B[bMid]的左侧, // 即此时第k大的元素一定比B[bMid]这个元素小(严格来说不大于) // 故以后没有必要搜索 B[bMid...bEnd]这些元素 return FindKthElm(A, aBeg, aEnd, B, bBeg, bMid - 1, k); } else { // 此时在合并的数组中A[aBeg...aMid]元素一定在B[bMid]的左侧,所以前K个元//素中一定包含A[aBeg...aMid](可以使用反证法来证明这点)但是无法判断//A[amid+1...aEnd]与B[bBeg...bEnd]之间的关系,//帮需要对他们进行判断此时K就剩//下除去A[aBeg...aMid]这些元素,个数为k - (aMid - aBeg + 1) return FindKthElm(A, aMid + 1, aEnd, B, bBeg, bEnd, k - (aMid - aBeg + 1)); } } else { if (halfLen > k) { return FindKthElm(A, aBeg, aMid - 1, B, bBeg, bEnd, k); } else { return FindKthElm(A, aBeg, aEnd, B, bMid + 1, bEnd, k - (bMid - bBeg + 1)); } } }
4) 和3)一样的大神级代码
//return the value of kth element in union of two sorted arrayint findKthElement(int A[], int m, int B[], int n, int k) { int i = int(double(m)/(m+n)*(k -1)); int j = (k-1) - i; //A[i] or B[j] is the Kth element, return it 详细注释见解释部分 if ((j <= 0 || B[j-1] < A[i]) && (j >= n || A[i] < B[j])) return A[i]; if ((i <= 0 || A[i-1] < B[j]) && (i >= m || B[j] < A[i])) return B[j]; //A[i] is too small, get rid of lower part of A and higher part of B if (0 < j && A[i] < B[j-1]) return findKthElement(A+i+1, m-i-1, B, j, k-i-1); //B[j] is too small, get rid of higher part of A and lower part of B else //if(i > 0 && B[j] < A[i-1]) return findKthElement(A, i, B+j+1, n-j-1, k-j-1);}
解释如下:代码始终维护i + j = k – 1,所以数组A中0-i的元素加上数组B中0-j的元素一共有(i+1)+(j+1) == k+1个,因为数组A和B都是有序的,所以我们知道A[i] > A[0…i-1]都大,B[j] > B[0…j-1]。如果B[j-1] < A[i] < B[j],那么将A[0…i]和B[0…j]合并后的新数组元素为:….B[j-1],A[i],B[j],因为A[0…i]和B[0…j]共有(i+1)+(j+1)==k+1个元素,故第K个元素是A[i].
7. 方程根求解,求平方根。
综上所述:二分变形很多,但是万变不离其宗,一点就是要利用二分法必须需找问题的单调性,如果单调性不能满足则不能用二分法。
- 二分法类型总结
- 二分法学习总结
- leetcode中的二分法总结
- LeetCode总结,二分法一般性总结
- LeetCode总结,二分法一般性总结
- LeetCode总结,二分法一般性总结
- 【算法总结】Binary search 二分法
- JAVA-二分法查找算法总结
- 用二分法对引用类型进行查找
- 二分法~~
- 二分法
- 二分法
- 二分法
- 二分法
- 二分法
- 二分法
- 二分法
- 二分法
- 注册表删除桌面快捷方式小箭头
- Linux Mysql 客户端
- JQUERY取节点值
- UVa 280 Vertex ( dfs搜索 水题 )
- iOS开发之Xcode篇
- 二分法类型总结
- HDU4006(The kth great number)优先级队列/SBT
- Android内存泄漏简介
- 关于类型转换的一件小事
- 第一次上机实验报告
- stl中map用法详解
- JAVA语言学习_02(关于static)
- Servlet详解
- 二、Mongodb实战之——Mongodb Shell实现简单插入和查询