二分查找
归纳
- 优点:比较次数少、查找速度快、平均性能好
- 缺点:待查找表为有序表、插入删除困难
- 时间复杂度:O(logN)
- 实用场景:有序数组
思路
假设表为升序排列,中间元素和待查元素比较,如果中间元素和待查元素相等找到了;如果小于则在前半段找;否则在后半段找。
递归
int BiSearch(int *a, int begin, int end, int value) { if (a == NULL || begin > end) return -1; if (end >= begin) { int mid = (begin + end) / 2; if (a[mid] == value) return mid; else if(a[mid] > value) return BiSearch(a, begin, mid-1, value); else return BiSearch(a, mid+1, end, value); } return -1; }
迭代
int BiSearch(int *a, int len, int value) { if (a == NULL && len <= 0) return -1; int beg = 0, end = len-1; while (end >= begin) { int mid = (begin + end) / 2; if (a[mid] == value) return mid; else if(a[mid] > value) end = mid - 1; else begin = mid + 1; } return -1; }
案例一
在有序数组1 2 3 3 3 3 4 5中3出现的次数为4,1出现的次数为1。
分析:有序数组查找——二分查找。二分找到被查找的元素,再两边扩展,直到找全为止。
参考代码
#include <iostream> using namespace std; int findMinSpan(int *a, int beg, int end, int val) { if (beg > end) return 0; else { int mid = beg + (end - beg) / 2; if (a[mid] == val) { int count = 1; for (int i = mid-1; i >= beg; --i) { if (a[i] == val) ++count; else break; } for (int i = mid+1; i <= end; ++i) { if (a[i] == val) ++count; else break; } return count; } else if (val < a[mid]) return findMinSpan(a, beg, mid-1, val); else return findMinSpan(a, mid+1, end, val); } } int MultiNum(int *a, int size, int val) { if(a == NULL || size <= 0) return 0; int span = findMinSpan(a, 0, size-1, val); return span; } int main() { int a[] = {1, 2, 3, 3, 3, 3, 4, 5}; int val = 3; int size = sizeof(a) / sizeof(*a); cout << MultiNum(a, size, val) << endl; //4 }
案例二
求旋转数组的最小元素(把一个数组最开始的若干个元素搬到数组的末尾,称之为数组的旋转。输入一个排好序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3, 4, 5, 1, 2}为{1, 2, 3, 4, 5}的一个旋转,该数组的最小值为1)
思路:有序数组——二分查找。
1 2 3 4 5 6 红底为中间位置元素,绿色为四个元素(a[beg] a[mid-1] a[mid+1], a[end])中的最小值
2 3 4 5 6 1
3 4 5 6 1 2
4 5 6 1 2 3
5 6 1 2 3 4
6 1 2 3 4 5
中间位置是mid,把数组分成左右两部分
左部分的最小值left = min(a[beg], a[min-1])
右部分的最小值right=min(a[mid+1], a[end])
- 如果a[mid]最小,则最小值为a[mid]
- 如果left最小,则最小值在beg~mid-1
- 如果right最小,则最小值在mid+1~end
参考代码
int findMin(int *a, int len) { if (a == NULL || len <= 0) return -1; int beg = 0, end = len-1, mid; while (beg <= end) { if (a[beg] < a[end] || beg == end) return beg; if (beg + 1 == end) return a[beg] < a[end] ? beg : end; mid = beg + (end - beg) / 2; if (a[beg] <= a[mid]) beg = mid; else end = mid; } }
测试
#include <iostream> #include <stdlib.h> using namespace std; int findMin(int *a, int len) { if (a == NULL || len <= 0) return -1; int beg = 0, end = len-1, mid; while (beg <= end) { if (a[beg] < a[end] || beg == end) return beg; if (beg + 1 == end) return a[beg] < a[end] ? beg : end; mid = beg + (end - beg) / 2; if (a[beg] <= a[mid]) beg = mid; else end = mid; } } int main() { int a[] = {1, 2, 3, 4, 5}; int len = sizeof(a) / sizeof(int); cout << findMin(a, len) << endl; int b[] = {2, 3, 4, 5, 1}; len = sizeof(b) / sizeof(int); cout << findMin(b, len) << endl; int c[] = {3, 4, 5, 1, 2}; len = sizeof(c) / sizeof(int); cout << findMin(c, len) << endl; int d[] = {4, 5, 1, 2, 3}; len = sizeof(d) / sizeof(int); cout << findMin(d, len) << endl; int e[] = {5, 1, 2, 3, 4}; len = sizeof(e) / sizeof(int); cout << findMin(e, len) << endl; int f[] = {5}; len = sizeof(f) / sizeof(int); cout << findMin(f, len) << endl; system("pause"); }