二分法变种
2016-05-06 18:11
302 查看
前几天在论坛上看到有统计说有80%的程序员不能够写对简单的二分法。二分法不是很简单的吗? 这难道不是耸人听闻?
其实,二分法真的不那么简单,尤其是二分法的各个变种。 最最简单的二分法,就是从一个排好序的数组之查找一个key值。 如下面的程序:
点击(此处)折叠或打开
int search(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = left + ((right - left) << 1);
if (arr[mid] == key) return mid;
else if(arr[mid] > key) right = mid - 1;
else left = mid + 1;
}
return -1;
}
这个程序,相信只要是一个合格的程序员应该都会写。 稍微注意一点, 每次移动left和right指针的时候,需要在mid的基础上+1或者-1, 防止出现死循环, 程序也就能够正确的运行。
但如果条件稍微变化一下, 你还会写吗?如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标;更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。 这些,虽然只有一点点的变化,实现的时候确实要更加的细心。 下面列出了这些二分检索变种的实现。
1. 找出第一个与key相等的元素
点击(此处)折叠或打开
int searchFirstEqual(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid - 1;
else if(arr[mid] < key) left = mid + 1;
}
if( left < n && arr[left] == key) return left;
return -1;
}
2. 找出最后一个与key相等的元素
点击(此处)折叠或打开
int searchLastEqual(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] > key) right = mid - 1;
else if(arr[mid] <= key) left = mid + 1;
}
if( right>=0 && arr[right] == key) return right;
return -1;
}
3. 查找第一个等于或者大于Key的元素
点击(此处)折叠或打开
int searchFirstEqualOrLarger(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid-1;
else if (arr[mid] < key) left = mid+1;
}
if(left>=n)return -1;
return left;
}
4. 查找第一个大于key的元素
点击(此处)折叠或打开
int searchFirstLarger(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] > key) right = mid-1;
else if (arr[mid] <= key) left = mid+1;
}
if(left>=n) return -1;
return left;
}
5. 查找最后一个等于或者小于key的元素
点击(此处)折叠或打开
int searchLastEqualOrSmaller(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int m = (left+right)/2;
if(arr[m] > key) right = m-1;
else if (arr[m] <= key) left = m+1;
}
return right;
}
6. 查找最后一个小于key的元素
点击(此处)折叠或打开
int searchLastSmaller(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid-1;
else if (arr[mid] < key) left = mid+1;
}
return right;
}
下面是一个测试的例子:
点击(此处)折叠或打开
int main(void)
{
int arr[17] = {1,
2, 2, 5, 5, 5,
5, 5, 5, 5, 5,
5, 5, 6, 6, 7};
printf("First Equal : %2d \n", searchFirstEqual(arr, 16, 5));
printf("Last Equal : %2d \n", searchLastEqual(arr, 16, 5));
printf("First Equal or Larger : %2d \n", searchFirstEqualOrLarger(arr, 16, 5));
printf("First Larger : %2d \n", searchFirstLarger(arr, 16, 5));
printf("Last Equal or Smaller : %2d \n", searchLastEqualOrSmaller(arr, 16, 5));
printf("Last Smaller : %2d \n", searchLastSmaller(arr, 16, 5));
system("pause");
return 0;
}
最后输出结果是:
点击(此处)折叠或打开
First Equal : 3
Last Equal : 12
First Equal or Larger : 3
First Larger : 13
Last Equal or Smaller : 12
Last Smaller : 2
很多的时候,应用二分检索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分检索的各个变种,熟练掌握了这些变种,当你再次使用二分检索的检索的时候就会感觉的更加的得心应手了。
本文来源于:http://blog.chinaunix.net/uid-1844931-id-3337784.html
其实,二分法真的不那么简单,尤其是二分法的各个变种。 最最简单的二分法,就是从一个排好序的数组之查找一个key值。 如下面的程序:
点击(此处)折叠或打开
int search(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = left + ((right - left) << 1);
if (arr[mid] == key) return mid;
else if(arr[mid] > key) right = mid - 1;
else left = mid + 1;
}
return -1;
}
这个程序,相信只要是一个合格的程序员应该都会写。 稍微注意一点, 每次移动left和right指针的时候,需要在mid的基础上+1或者-1, 防止出现死循环, 程序也就能够正确的运行。
但如果条件稍微变化一下, 你还会写吗?如,数组之中的数据可能可以重复,要求返回匹配的数据的最小(或最大)的下标;更近一步, 需要找出数组中第一个大于key的元素(也就是最小的大于key的元素的)下标,等等。 这些,虽然只有一点点的变化,实现的时候确实要更加的细心。 下面列出了这些二分检索变种的实现。
1. 找出第一个与key相等的元素
点击(此处)折叠或打开
int searchFirstEqual(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid - 1;
else if(arr[mid] < key) left = mid + 1;
}
if( left < n && arr[left] == key) return left;
return -1;
}
2. 找出最后一个与key相等的元素
点击(此处)折叠或打开
int searchLastEqual(int *arr, int n, int key)
{
int left = 0, right = n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] > key) right = mid - 1;
else if(arr[mid] <= key) left = mid + 1;
}
if( right>=0 && arr[right] == key) return right;
return -1;
}
3. 查找第一个等于或者大于Key的元素
点击(此处)折叠或打开
int searchFirstEqualOrLarger(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid-1;
else if (arr[mid] < key) left = mid+1;
}
if(left>=n)return -1;
return left;
}
4. 查找第一个大于key的元素
点击(此处)折叠或打开
int searchFirstLarger(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] > key) right = mid-1;
else if (arr[mid] <= key) left = mid+1;
}
if(left>=n) return -1;
return left;
}
5. 查找最后一个等于或者小于key的元素
点击(此处)折叠或打开
int searchLastEqualOrSmaller(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int m = (left+right)/2;
if(arr[m] > key) right = m-1;
else if (arr[m] <= key) left = m+1;
}
return right;
}
6. 查找最后一个小于key的元素
点击(此处)折叠或打开
int searchLastSmaller(int *arr, int n, int key)
{
int left=0, right=n-1;
while(left<=right) {
int mid = (left+right)/2;
if(arr[mid] >= key) right = mid-1;
else if (arr[mid] < key) left = mid+1;
}
return right;
}
下面是一个测试的例子:
点击(此处)折叠或打开
int main(void)
{
int arr[17] = {1,
2, 2, 5, 5, 5,
5, 5, 5, 5, 5,
5, 5, 6, 6, 7};
printf("First Equal : %2d \n", searchFirstEqual(arr, 16, 5));
printf("Last Equal : %2d \n", searchLastEqual(arr, 16, 5));
printf("First Equal or Larger : %2d \n", searchFirstEqualOrLarger(arr, 16, 5));
printf("First Larger : %2d \n", searchFirstLarger(arr, 16, 5));
printf("Last Equal or Smaller : %2d \n", searchLastEqualOrSmaller(arr, 16, 5));
printf("Last Smaller : %2d \n", searchLastSmaller(arr, 16, 5));
system("pause");
return 0;
}
最后输出结果是:
点击(此处)折叠或打开
First Equal : 3
Last Equal : 12
First Equal or Larger : 3
First Larger : 13
Last Equal or Smaller : 12
Last Smaller : 2
很多的时候,应用二分检索的地方都不是直接的查找和key相等的元素,而是使用上面提到的二分检索的各个变种,熟练掌握了这些变种,当你再次使用二分检索的检索的时候就会感觉的更加的得心应手了。
本文来源于:http://blog.chinaunix.net/uid-1844931-id-3337784.html
相关文章推荐
- 面向项目(四)—— 可移植性的考虑
- JConsole观察分析Java程序
- java注释@interface的用法
- 第九周项目2-深复制体验
- 评价意见整合
- block和delegate的使用
- 收集SAP函数Function
- mongodb (1)
- ranch 源码分析(一)
- macbook pro上安装虚拟机
- u-boot移植3:支持 nandflash 的硬件 ECC
- Python中threading的问题
- STL学习笔记— —迭代器
- DDR 速率MT/s(GT/s) 与 MHz的关系
- Redis数据库启动脚本
- 几种软件工程用图
- Houdini 中字符串参数使用Python表达式
- nginx上传文件大小限制修改
- Linux下自动备份Oracle数据库并删除指定天数前的备份
- 用ul、li做横向导航