内容简介:二分查找也称折半查找(Binary Search),二分查找针对的是有序的线性表,并且线性表要采用顺序存储结构,满足这个条件的就是数组这种结构了。查找过程首先,假设表中元素是按升序排列,将表中间位置的值与查找目标值字比较,如果两者相等,则查找成功;否则利用中间位置将表分成前、后两个子表,如果中间位置的值大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。 - 百度百科
二分查找也称折半查找(Binary Search),二分查找针对的是有序的线性表,并且线性表要采用顺序存储结构,满足这个条件的就是数组这种结构了。
查找过程
首先,假设表中元素是按升序排列,将表中间位置的值与查找目标值字比较,如果两者相等,则查找成功;否则利用中间位置将表分成前、后两个子表,如果中间位置的值大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。 - 百度百科
简单二分查找
简单的二分查找, 只要查到等于目标值的索引, 代码非常直接简单
function bSearch(arr, target) { let left = 0 //左指针 let right = arr.length - 1 //右指针 while (left <= right) { let mid = (right - left) >> 1 + left if (arr[mid] === target) { return mid } else if (arr[mid] < target) { left = mid + 1 } else { right = mid - 1 } } return -1 } 复制代码
三个容易出错的地方
-
循环退出条件,注意是
left<=right
,而不是left < right
-
mid的取值
实际上,
mid=(left+right)/2
这种写法是有问题的。因为如果left和right比较大的话,两者之 和就有可能会溢出。改进的方法是将mid的计算方式写成left+(right-left)/2
。更进一步,如果 要持性能优化到扱致的话,我们可以将这里的除以2操作转化成位运算left+((right-left)>>1)
-
left和right的更新
left=mid+1
,right=mid-1
。注意这里的+1和-1 ,如果直接写成left=mid或者right=mid , 就可能会发生死循环。比如,当right=k , left=k时,如果a[k]不等于target, 就会导致死循环。
二分查找的变形
上面的简单二分查找是有序且不重复的数组,我们现在来考察有序但是有重复数据的数组 我们来看看四个变形问题
1. 查找第一个等于目标值的元素索引
function bSearch1(arr, target) { let left = 0 let right = arr.length - 1 while (left <= right) { let mid = (right - left) >> 1 + left if (arr[mid] === target) { if (mid === 0 || arr[mid - 1] !== target) { return mid } else { right = mid - 1 } } else if (arr[mid] < target) { left = mid + 1 } else { right = mid - 1 } } return -1 } 复制代码
注意现在数组里面有重复的元素,所以当满足 arr[mid] === target
, mid不一定是第一个等于目标值的元素, 因此还需要一个条件分支判断 mid === 0 || arr[mid - 1] !== target
, 如果mid是0,那么是第一个元素,肯定满足条件,或者当前位置的前一个元素不等于目标值,那么这个位置一定就是我们要找第一个等于目标值的元素的索引
如果不满足这个条件,说明这个位置前面还有等于目标值的元素,那么需要更新右边的指针 high = mid - 1
;
变形1理解了,后面的三个变形问题也是同样的思路, 聪明的你肯定可以举一反三
2. 查找最后一个等于目标值的元素索引
function bSearch2(arr, target) { let left = 0 let right = arr.length - 1 while (left <= right) { let mid = (right - left) >> 1 + left if (arr[mid] === target) { if (mid === arr.length - 1 || arr[mid + 1] !== target) { return mid } else { left = mid + 1 } } else if (arr[mid] < target) { left = mid + 1 } else { right = mid - 1 } } return -1 } 复制代码
3. 查找第一个大于等于目标值的元素索引
function bSearch3(arr, target) { let left = 0 let right = arr.length - 1 while (left <= right) { let mid = (right - left) >> 1 + left if (arr[mid] >= target) { if (mid === 0 || arr[mid - 1] < target) { return mid } else { right = mid - 1 } } else { left = mid + 1 } } } 复制代码
4. 查找最后一个小于等于目标值的元素索引
function bSearch4(arr, target) { let left = 0 let right = arr.length - 1 while (left <= right) { let mid = (right - left) >> 1 + left if (arr[mid] <= target) { if (mid === arr.length - 1 || arr[mid + 1] > target) { return mid } else { left = mid + 1 } } else { right = mid - 1 } } } 复制代码
leetcode相关题解
最后来看看leetcode的33题
题目描述:
假设按照升序 排序 的数组在预先未知的某个点上进行了旋转。 ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。 搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。 你可以假设数组中不存在重复的元素。 你的算法时间复杂度必须是 O(log n) 级别。
示例 1:
输入: nums = [4,5,6,7,0,1,2], target = 0 输出: 4
示例 2:
输入: nums = [4,5,6,7,0,1,2], target = 3 输出: -1 搜索旋转排序数组, 要求时间复杂度是O(logn)
解题思路:
将数组一分为二,其中一定有一个是有序的,另一个可能是有序,也能是部分有序。 此时有序部分用二分法查找。无序部分再一分为二,其中一个一定有序,另一个可能有序,可能无序。就这样循环直到找到目标值或者左边界超出右边界,退出循环
规律: 中间值比最右值小,则右半部有序递增 中间值比最右值大,则左半部有序递增
代码
function search(nums, target) { let left = 0 let right = nums.length - 1 let mid while (left <= right) { mid = left + ((right - left) >> 1) // 注意位运算符打括号 if (nums[mid] == target) { return mid } //右半部有序递增 if (nums[mid] < nums[right]) { if (nums[right] >= target && nums[mid] < target) { left = mid + 1 } else { right = mid - 1 } } else { if (nums[left] <= target && nums[mid] > target) { right = mid - 1 } else { left = mid + 1 } } } return -1 } 复制代码
这个算法也可以用递归实现,我用递归写的代码在leetcode上通不过,老是报栈溢出错误,所以我这里就不贴了,有兴趣的可以尝试写一下递归代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
ANSI Common Lisp
Paul Graham / Prentice Hall / 1995-11-12 / USD 116.40
For use as a core text supplement in any course covering common LISP such as Artificial Intelligence or Concepts of Programming Languages. Teaching students new and more powerful ways of thinking abo......一起来看看 《ANSI Common Lisp》 这本书的介绍吧!