二分查找法
来源:互联网 发布:西装牌子 知乎 编辑:程序博客网 时间:2024/05/20 17:07
----------------siwuxie095
二分查找法
二分查找(Binary Search),也称折半查找(Half-Interval Search),
是一种在有序数组中查找某一特定元素的搜索算法
「或称二分搜索,折半搜索」
正如定义所示,二分查找法有一定的限制:对于有序数列,才能使用二分查找法
由此可知,排序算法在很多时候是作为其它算法的一个子过程。例如:如果使用
二分查找法,就要先使用一次排序算法,对要查找的内容进行一次排序
之所以进行这次排序,是因为处理有序数组,要比处理无序数组容易很多
具体查找过程:
要在一个有序数组中查找某个元素,就先看这个数组的中间元素v 与要查找的
元素,二者的大小的关系
如果中间元素v 正好是要查找的元素,即 二者相等,非常好,直接就找到了该
元素,查找结束
否则,整个数组就被中间元素 v 分成了两部分:小于 v 的部分和大于 v 的部分
1)如果要查找的元素比v 小,就在小于v 的这部分继续查找即可
2)如果要查找的元素比v 大,就在大于 v 的这部分继续查找即可
「前提:整个数组是有序的」
不难想象,整个查找过程,感觉上构造出了一棵树,即是一个树形问题,
整个二分查找法的时间复杂度是lgn 级别的
二分查找法的思想非常简单,而且这个思想在很早的时候就被提出来了
二分查找法的思想在 1946年提出,但有意思的是,第一个没有bug 的
二分查找法在 1962 年提出
程序 1:迭代的二分查找法
BinarySearch.h:
#ifndef BINARYSEARCH_H
#define BINARYSEARCH_H
//用迭代的方式写二分查找法
//
//二分查找法,在有序数组arr中,查找target
//如果找到target,返回相应的索引index
//如果没有找到target,返回-1
template<typename T>
int binarySearch(T arr[],int n, T target)
{
//在arr[l...r]之中查找target
int l =0, r = n - 1;
while (l <= r)
{
//l+r都是int型,如果过大,相加则会有溢出的风险
//int mid = (l + r)/2;
//(在归并排序中也有相同的问题)
//
//改为如下形式即可(无bug版):
int mid = l + (r - l) /2;
if (arr[mid] == target)
{
return mid;
}
//在arr[l...mid-1]之中查找target
//或arr[mid+1...r]之中查找target
if (arr[mid] > target)
{
r = mid -1;
}
else
{
l = mid +1;
}
}
return -1;
}
#endif
main.cpp:
#include"BinarySearch.h"
#include <iostream>
#include <cassert>
#include <ctime>
using namespace std;
int main()
{
int n =1000000;
int* a =newint[n];
for (int i =0; i < n; i++)
{
a[i] = i;
}
//测试非递归二分查找法(迭代)
clock_t startTime = clock();
for (int i =0; i < 2 * n; i++)
{
int v = binarySearch(a, n, i);
if (i < n)
{
assert(v == i);
}
else
{
assert(v == -1);
}
}
clock_t endTime = clock();
cout <<"Binary Search (Without Recursion): " <<double(endTime - startTime)
/ CLOCKS_PER_SEC <<" s" << endl;
delete []a;
system("pause");
return0;
}
运行一览:
程序 2:递归的二分查找法
BinarySearch.h:
#ifndef BINARYSEARCH_H
#define BINARYSEARCH_H
template<typename T>
int __binarySearch(T arr[],int l, int r, T target)
{
if (l > r)
{
return -1;
}
int mid = (l + r) /2;
if (arr[mid] == target)
{
return mid;
}
else if (arr[mid] > target)
{
return __binarySearch(arr,0, mid - 1, target);
}
else
{
return __binarySearch(arr, mid +1, r, target);
}
}
//用递归的方式写二分查找法
template<typename T>
int binarySearch(T arr[],int n, T target)
{
return __binarySearch(arr,0, n - 1, target);
}
//递归实现通常思维起来更容易,因为每一次不需要考虑全局,
//只需要考虑一个子问题
//
//想好它们的递归关系,想清楚在最基础的层面是怎么做的,
//就能写出这个函数来,不过递归也存在一些缺点:相比于
//迭代,递归在性能上会略差(这种差异是常数级的)
//
//不管是递归还是迭代,二分查找法的时间算法复杂度都是
//O(lgn)级别的
#endif
main.cpp:
#include"BinarySearch.h"
#include <iostream>
#include <cassert>
#include <ctime>
using namespace std;
int main()
{
int n =1000000;
int* a =newint[n];
for (int i =0; i < n; i++)
{
a[i] = i;
}
//测试递归的二分查找法
clock_t startTime = clock();
for (int i =0; i < 2 * n; i++)
{
int v = binarySearch(a, n, i);
if (i < n)
{
assert(v == i);
}
else
{
assert(v == -1);
}
}
clock_t endTime = clock();
cout <<"Binary Search (Recursion): " <<double(endTime - startTime)
/ CLOCKS_PER_SEC <<" s" << endl;
delete []a;
system("pause");
return0;
}
运行一览:
二分查找法的变种
二分查找法的变种有两个非常重要的、也是应用非常广的函数,
分别叫做floor 和ceil
「有的地方也叫做lower_bound 和 upper_bound」
之前实现的二分查找法,通常都是假设数组中没有重复元素
当然,即使数组中有重复元素,对于一个排好序的数组来说,依然能找到
相应元素的索引,只不过,该元素在这个数组中可能会出现很多次,之前
实现的二分查找法并不能保证找到的这个元素的索引,具体是哪个索引
但floor 和 ceil 这两个函数,却能保证:
1)调用 floor 来找元素 v,可以找到v 在整个数组中第一次出现的位置
2)调用ceil 来找元素 v,可以找到 v 在整个数组中最后一次出现的位置
这两个函数还有一个优势,即当在数组中查找元素,如果这个元素
不存在,之前实现的二分查找法直接返回了-1,但 floor 和 ceil 的
返回值却有所不同
具体如下:
如果要在数组中查找元素 42,可以看到 42在这个数组中并不存在,
那么floor 返回的就是最后一个 41 的元素,而 ceil 返回的就是第一
个 43的元素
【made by siwuxie095】
- 查找----二分查找法
- 二分查找法,查找string
- 二分查找法
- 二分查找法
- 二分查找法
- 二分查找法
- java 二分查找法
- 二分查找法
- 二分查找法
- 二分查找法
- 二分查找法
- java:二分查找法
- C++ 二分查找法
- 二分查找法
- 【二分查找法】
- php二分查找法
- 二分查找法
- 二分查找法
- 第6章 Docker私有仓库Registry的搭建验证
- leetcode->Algorithm->9. Palindrome Number
- Android性能优化
- 这里是记录我自己JAVA学习的一个开始
- 串口通信中的检测机制
- 二分查找法
- [LeetCode] 231. Power of Two(判断整数是否是2的幂)
- LeetCode@HashTable_575_DistributeCandies
- 第三十四天:js总结+重写js的日历
- python字典
- 归并排序
- var, let, const,、 命令的理解和区别;
- django 1.11.X初学指南(1)
- CSS知识部分总结(不含CSS3)