二分搜索算法适用于有序数组。如果按照暴力搜索算法,那么需要从头到尾遍历数组元素,时间复杂度为 O(n),而如果使用二分搜索,那么其时间复杂度为 O(logn),根据时间复杂度曲线图可知,二分搜索的算法效率要优于线性查找。
二分搜索的实现可以分为非递归和递归两种。
1. 非递归版本
如果使用非递归版本来实现二分搜索,那么有两种写法:
- 左闭右闭区间
- 左闭右开区间
1.1 左闭右闭区间
如果是左闭右闭区间,即 [left, right]
,那么需要遵循以下两点:
- 循环条件为
while(left <= right)
; if (nums[middle] > target)
中right
要赋值为mid - 1
;
例如,在下图所示的数组中查找目标元素 5:
二叉查找的实质其实就是将有序数组转化为一个二叉搜索树,如下图所示:
每一次搜索其实就是从二叉树的根节点开始向下搜索,如果目标值大于当前节点,则搜索右子树,如果目标值小于当前节点,则搜索左子树,直到搜索到叶子节点为止。所以二分搜索的时间复杂度其实就是有序数组转化为二叉搜索树后的高度。
由于二叉搜索树第一层有 \(2^{(1-1)}=2^0\) 个节点,第二层有 \(2^{(2-1)}=2^1\) 个节点,第三层有 \(2^{(3-1)}=2^2\) 个节点,故第 n 层有 \(2^{(n-1)}\) 个节点,假设数组长度为 \(m\),那么有:
\[2^0+2^1+2^2+...+2^{n-1}=m \]根据数列求和公式,可得:
\[2^n-1=m \]那么就有:
\[n\approx logm \]即为二叉搜索的时间复杂度。
其代码实现如下:
int binearySearch(const vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
1.2 左闭右开区间
如果是左闭右开区间,即 [left, right)
,那么需要遵循以下两点:
- 循环条件为
while(left < right)
; if (nums[middle] > target)
中right
要赋值为mid
;
例如,在下图所示的数组中查找目标元素 5:
其代码实现如下:
int binearySearch(const vector<int>& nums, int target) {
int left = 0, right = nums.size();
while (left < right) {
int mid = left + ((right - left) / 2);
if (nums[mid] > target) {
right = mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
return mid;
}
}
return -1;
}
2. 递归版本
递归版本的相关代码如下:
int binearySearch(const vector<int>& nums, int start, int end, int target) {
if (start > end) {
return -1;
}
int mid = (start + end) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
return binearySearch3(nums, start, mid - 1, target);
} else {
return binearySearch3(nums, mid + 1, end, target);
}
}
标签:二分,right,target,nums,int,mid,搜索算法,left
From: https://www.cnblogs.com/tuilk/p/16989473.html