二分查找简介
二分查找是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。
时间复杂度为O(log n)
例如对于{1,3,4,6,7,8,10,13,14}构成的有序序列对应的查找树:
算法实现
最简单的二分查找算法:
即找到返回下标,找不到返回-1,(坐标范围:[0~nums.size() - 1])
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> #include <sstream> #include <cstring> #include <cmath> using namespace std; int BinarySearch(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] < target) { left = mid + 1; } else { return mid; } } return -1; } int main() { vector<int>vec{1,3,5,5,5,7,8}; cout << BinarySearch(vec, 3); return 0; }
二分查找的变种
查找第一个与target相等的元素位置,找不到返回-1
int BinarySearchLeft(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] >= target) { right = mid - 1; } else { left = mid + 1; } } if (left <= (int)nums.size() - 1 && nums[left] == target) { return left; } return -1; }
查找最后一个与target相等的元素位置,找不到返回-1
int BinarySearchRight(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] > target) { right = mid - 1; } else { left = mid + 1; } } if (right >= 0 && nums[right] == target) { return right; } return -1; }
查找最后一个小于key的元素
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> #include <sstream> #include <cstring> #include <cmath> using namespace std; int BinarySearch(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (nums[mid] >= target) { right = mid - 1; } else if(nums[mid] < target) { left = mid + 1; } } return right; } int main() { vector<int>vec{1,3,5,5,5,7,8}; cout << BinarySearch(vec, 7); return 0; }
查找第一个大于key的元素
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> #include <sstream> #include <cstring> #include <cmath> using namespace std; int BinarySearch(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (target >= nums[mid]) { left = mid + 1; } else if(target < nums[mid]) { right = mid - 1; } } return left; } int main() { vector<int>vec{1,3,5,5,5,7,8}; cout << BinarySearch(vec, 5); return 0; }如果返回nums.size()则说明不存在。
查找第一个等于或者小于key的元素
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> #include <sstream> #include <cstring> #include <cmath> using namespace std; int BinarySearch(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (target <= nums[mid]) { right = mid - 1; } else { left = mid + 1; } } if (nums[left] != target) { return left - 1; } return left; } int main() { vector<int>vec{1,3,5,5,5,7,8}; cout << BinarySearch(vec, 5); return 0; }
查找最后一个等于或者大于key的元素
#include <iostream> #include <vector> #include <set> #include <algorithm> #include <string> #include <sstream> #include <cstring> #include <cmath> using namespace std; int BinarySearch(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; while(left <= right) { int mid = left + ((right - left) >> 1); if (target >= nums[mid]) { left = mid + 1; } else { right = mid - 1; } } if (nums[right] != target) { return right + 1; } return right; } int main() { vector<int>vec{1,3,5,5,5,7,8}; cout << BinarySearch(vec, 5); return 0; }
STL中的二分查找
STL中与二分查找相关的函数有4个,分别是lower_bound, upper_bound, equal_range和binary_search,下面通过一个简单的例子说明各个函数的使用方法。
#include <cstdio> #include <algorithm> #include <vector> using namespace std; int main() { int a[6] = {1, 2, 2, 2, 3, 4}; //已排序 vector<int> v(a, a + 6); typedef vector<int>::iterator It; //迭代器类型 It st = v.begin(), ed = v.end(); //起始迭代器 It p1 = lower_bound(st, ed, 2);//>=2的第一个位置 printf("%d ", p1 - st); // 1 It p2 = upper_bound(st, ed, 2);//>2的第一个位置 printf("%d ", p2 - st); // 4 pair<It, It> p = equal_range(st, ed, 2); //==2的位置 printf("%d-%d ", p.first - st, p.second - st); // 1-4 bool exist = binary_search(st, ed, 2); //2是否存在 printf("%d ", exist); //1 (是) }其中每个函数实现的功能如下:
binary_search:查找某个元素是否出现。
lower_bound:查找第一个大于或等于某个元素的位置。
upper_bound:查找第一个大于某个元素的位置。
equal_range:查找某个元素出现的起止位置。注意,终止位置为最后一次出现的位置加一。
其中lower_bound和upper_bound的功能可能比较别扭,可以这样看:对于已排序的数组1 2 2 2 3 4,元素2出现了3次,第一次出现的位置为1,最后一次的位置为3,lower_bound即为这个第一次出现的位置,而upper_bound本意是要标志最后一次出现的位置,但STL中习惯使用左闭右开的区间,于是upper_bound返回的结果应该为最后一次出现的位置的下一个位置。当查找的元素一次都未出现时,二者返回的结果都是第一个大于该元素的位置。
参考:http://www.cnblogs.com/ider/archive/2012/04/01/binary_search.html