二分查找
来源:互联网 发布:淘宝质量鉴定什么意思 编辑:程序博客网 时间:2024/06/05 04:07
Binary search is easiest difficult algorithm to get it right.
将通常见到的两种二分查找的方式纪录在这里。
关于第二种方式,会涉及到类似STL库中的lower_bound, upper_bound的实现。
Problem Statement
Given a sorted array of N distinct elements. Find a key in the array using least number of comparisons. (Do you think binary search is optimal to search a key in sorted array?)
前提:数组是有序(从小到大)。
最简单易思考的方式
没有理论,直接上经典的binary search算法。
// 返回key的位置; -1表示没找到int BinarySearch(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { mid = low + (high-low)/2; //经典溢出问题的改进 if (nums[mid] == key) { //第一次比较 return mid; } else if (nums[mid] > key) { //第二次比较 high = mid; } else { low = mid + 1; } } return -1;}
最坏情况下,上述binary_search需要logN + 1次比较:
每次进入while循环都需要进行两次比较,直到最后一次。
关于nums.size()函数返回vector<int>::size_type
类型:
对于size()函数,如果定义返回值是一个int或者unsigned类型,编译器会给出警告,提示精度缺失问题。
其实,size()函数返回vector<int>::size_type
类型,是一个无符号类型的值,而且能够存放下任何vector对象的大小。所有拥有存放vector<int>
类的size函数返回值的变量,都应该是vector<int>::size_type
类型的
在c++中,string类以及大多数标准库类型都定义了几种配套的类型。这些配套类型体现了标准库类型谕机器无关的特性,类型size_type即是其中的一种。
在具体使用的时候,通过作用域操作符来表明名字size_type是在类string中定义的:
string::size_type
但是
在vector中,要是用
size_type
,需首先指定它是由哪种类型定义的。vector对象的类型总是包含着元素的类型:
vector<int>::size_type //rightvector::size_type //wrong
当然,在c++11中可以直接:
auto n = nums.size(); //n的类型为vector::size_type
这样就能理解为什么用了(int)nums.size()
了。
问题及改进
如果数组中有多个元素值都等于key
,上面的函数返回的是哪一个的下标呢?
中间那个
有时,当我们需要找出所有等于key
的完整区间时,想要先找到第一个元素(毕竟有序数组,等于k
的值会在一起),该如何做呢?
在C++ STL库中,有lower_bound和upper_bound函数,这里类似的实现一下。不过,在下面程序中,需要注意的是:
- 当key存在时,返回它出现的第一个位置;
- 当key不存在时,返回这样一个下标i:在此处插入key后(原来nums[i], nums[i+1]…全部后移)仍然有序;
也就是说,当调用该函数后,去查看返回位置的元素大小是否等于key:
如果相等,即为数组中存在该元素,且当前位置即为其出现的第一个位置;
如果不相等,即数组中不存在该元素
#include <iostream>#include <vector>using namespace std;int lower_bound(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { mid = low + (high-low)/2; if (nums[mid] >= key) { high = mid; } else { low = mid+1; } } return low;}
这里需要好好理解一下:
程序查找区间为:[low, high),返回值的候选区间为:[low, high]
你可以去跑跑程序看一下。
关于nums[mid]和key的各种关系:
nums[mid] = key
至少找到一个,而左边可能还有,因此区间变为[low, mid]
nums[mid] > key
当前元素大于key,所找元素在位置mid之前,因此区间为[x, mid]
nums[mid] < key
当前元素小于key,所找元素在位置mid之后,因此区间为[mid+1, high]
类似的,还可以写一个upper_bound函数:
- 当存在与key值想等的元素时,返回它出现的最后一个位置的后面一个位置
- 如果不存在,存在一个这样的下标:在此处插入key后(原来nums[i], nums[i+1]…全部后移)仍然有序;
int upper_bound(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { //upper_bound mid = low + (high-low)/2; if (nums[mid] <= key) { low = mid+1; } else { high = mid; } } return low;}
改进后的总结
已经有了lower_bound和upper_bound函数,即有了求解范围统计问题的“上下界”函数。
设两个函数的返回值分别为L和R,那么:
- 如果数组中有同key值相同的元素,则其出现的子序列为[L, R);
- 如果不存在,此时L == R,区间为空。
到此,二分查找算是比较完整了。
供直接测试
code
#include <iostream>#include <vector>using namespace std;int BinarySearch(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { mid = low + (high-low)/2; //经典溢出问题的改进 if (nums[mid] == key) { //第一次比较 return mid; } else if (nums[mid] > key) { //第二次比较 high = mid; } else { low = mid + 1; } } return -1;}int lower_bound(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { mid = low + (high-low)/2; if (nums[mid] >= key) { high = mid; } else { low = mid+1; } } return low;}int upper_bound(vector<int> nums, int key){ int n = (int)nums.size(); int low = 0, high = n, mid; while (low < high) { //upper_bound mid = low + (high-low)/2; if (nums[mid] <= key) { low = mid+1; } else { high = mid; } } return low;}int main() { vector<int> v{1, 2, 2, 2, 3, 4}; cout << lower_bound(v, 2) << endl; cout << upper_bound(v, 2) << endl << endl; cout << BinarySearch(v, 2) <<endl; return 0;}
result
143
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 二分查找
- 例题课本例题3-1转换==整数从大到小排序
- 初步进行JDBC的步骤
- HDU 5875 Function(单调栈+在线倍增法)
- Swift 3.0 重大版本发生了哪些变化,各位看官请
- 《Java编程思想第四版》笔记---14章 附:包装类--装箱和拆箱
- 二分查找
- 64. Minimum Path Sum DP经典问题
- 创新科技,只为尊重音乐原声:dFiM睿妙深度剖析
- Java之wait()/sleep()和notify()/notifyAll()
- java 链表实现多项式加法!
- TabBar 的selecedImage无法显示设置图片问题 和item文字颜色选中设置
- 基于netty的网络聊天室
- 浅谈iOS10及Xcode8
- xcoj1227-电梯