1、算法概念。
2、算法思想。
①搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;
②如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。
③如果在某一步骤数组为空,则代表找不到。
这种搜索算法每一次比较都使搜索范围缩小一半。
3、实现思路。
①找出位于数组中间的值,并存放在一个变量中(为了下面的说明,变量暂时命名为temp);
②需要找到的key和temp进行比较;
③如果key值大于temp,则把数组中间位置作为下一次计算的起点;重复① ②。
④如果key值小于temp,则把数组中间位置作为下一次计算的终点;重复① ② ③。
⑤如果key值等于temp,则返回数组下标,完成查找。
4.算法的java实现(分为递归和非递归方式)
4.1非递归实现
代码如下:
public static int binarySearch(int[] nums, int target) {
if(nums==null||nums.length==0){
return 0;
}
int start=0;
int end=nums.length-1;
while(start<=end){
int mid=(start+end)/2;
if(nums[mid]==target){
return mid;
}
else if(nums[mid]>target){
end=mid-1;
}
else{
start=mid+1;
}
}
return 0;
}
4.2递归实现
代码如下:
public static int BinarySearch(int Array[],int low,int high,int key)
{
if (low<=high)
{
int mid = (low+high)/2;
if(key == Array[mid])
return mid;
else if(key<Array[mid])
return BinarySearch(Array,low,mid-1,key);
else if(key>Array[mid])
return BinarySearch(Array,mid+1,high,key);
}
return 0;
}
5.二分查找性能分析
二分查找过程可用二叉树来描述:把当前查找区间的中间位置上的结点作为根,左子表和右子表中的结点分别作为根的左子树和右子树。由此得到的二叉树,称为描述二分查找的判定树(Decision Tree)或比较树(Comparison Tree)。(注意:判定树的形态只与表结点个数n相关,而与输入实例中R[1..n].keys的取值无关。)
下面介绍一个例子,画出数组R[1...11]的判定树。如下图所示:
如图所示:不难发现,找到有序表中任一记录的过程就是走了一条从根节点到该记录相应的节点的路径,和给定值进行不叫的关键字个数恰为该节点在判定树上的层次数。因此,折半查找算法在查找成功时进行比较的关键字个数最多不超过树的深度,而具有n个节点的判定树的深度为「log2n」+1,log2n向下取整加1,所以二分查找法在查找成功是和给定值进行比较的关键字个数至多为「log2n」+1;二分查找在查找失败时所需比较的关键字个数不超过判定树的深度,在最坏情况下查找成功的比较次数也不超过判定树的深度。即为: 。
6.LeetCode中关于二分查找类型的题目
6.1搜索旋转排序数组
假设有一个排序的按未知的旋转轴旋转的数组(比如,0 1 2 4 5 6 7 可能成为4 5 6 7 0 1 2)。给定一个目标值进行搜索,如果在数组中找到目标值返回数组中的索引位置,否则返回-1。
你可以假设数组中不存在重复的元素。
样例
给出[4, 5, 1, 2, 3]和target=1,返回 2
给出[4, 5, 1, 2, 3]和target=0,返回 -1
public class Solution {
/**
*@param A : an integer rotated sorted array
*@param target : an integer to be searched
*return : an integer
*/
public int search(int[] A, int target) {
if(A.length==0||A==null){
return -1;
}
int start=0,end=A.length-1;
int mid=0;
while(start<=end){
mid=(start+end)/2;
if(A[mid]==target){
return mid;
}
// 左半区间是递增的
if(A[mid]>=A[start]){
if(target<A[mid]&&target>=A[start]){// 目标在左半区间且在mid左边
end=mid-1;
}
else{
start=mid+1;
}
}
else{// 右半区间是递增的
if(A[mid]<target&&target<=A[end]){ // 目标在右半区间且在mid右边
start=mid+1;
}
else{
end=mid-1;
}
}
}
return -1;
}
}
6.2搜索旋转排序数组 II
跟进“搜索旋转排序数组”,假如有重复元素又将如何?
是否会影响运行时间复杂度?
如何影响?
为何会影响?
写出一个函数判断给定的目标值是否出现在数组中。
给出[3,4,4,5,7,0,1,2]和target=4,返回 true
public class Solution {
/**
* param A : an integer ratated sorted array and duplicates are allowed
* param target : an integer to be search
* return : a boolean
*/
public boolean search(int[] num, int target) {
if(num==null||num.length==0){
return false;
}
int mid=0;
int start=0,end=num.length-1;
while(start<=end){
mid=(start+end)/2;
if(num[mid]==target){
return true;
}
if(num[mid]>num[start]){
if(target<num[mid]&&num[start]<=target){
end=mid-1;
}
else{
start=mid+1;
}
}
else if(num[mid]<num[start]){
if(num[mid]<target&&target<=num[end]){
start=mid+1;
}
else{
end=mid-1;
}
}
else{
start++;
}
}
return false;
}
}
6.3寻找旋转排序数组中的最小值
假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。
你需要找到其中最小的元素。
你可以假设数组中不存在重复的元素。
给出[4,5,6,7,0,1,2] 返回 0
public class Solution {
/**
* @param num: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] A) {
if(A==null||A.length==0){
return 0;
}
int res=0;
int length=A.length;
int start=0;
int end=length-1;
//int mid=start;
while(A[start]>=A[end]){
if(end-start==1){
res=end;
break;
}
int mid=(start+end)/2;
if(A[mid]>=A[start]){
start=mid;
}
else if(A[mid]<=A[end]){
end=mid;
}
}
return A[res];
}
}
6.4寻找旋转排序数组中的最小值 II
假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。
你需要找到其中最小的元素。
数组中可能存在重复的元素。
样例
给出[4,4,5,6,7,0,1,2] 返回 0
public class Solution {
/**
* @param num: a rotated sorted array
* @return: the minimum number in the array
*/
public int findMin(int[] num) {
if(num==null||num.length==0){
return 0;
}
int length=num.length;
int res=0;
int start=0;
int end=length-1;
while(num[start]>=num[end]){
if(end-start==1){
res=end;
break;
}
int mid=(start+end)/2;
if(num[mid]==num[start]&&num[mid]==num[end]){//满足这个条件时,只能从头到尾开始遍历
return findMIN(num,start,end);
}
if(num[mid]>=num[start]){
start=mid;
}
else if(num[mid]<=num[end]){
end=mid;
}
}
return num[res];
}
private static int findMIN(int[] num,int start,int end){
int min=num[start];
for(int i=start+1;i<=end;i++){
if(num[i]<min){
min=num[i];
}
}
return min;
}
}
6.5寻找峰值
你给出一个整数数组(size为n),其具有以下特点:
- 相邻位置的数字是不同的
- A[0] < A[1] 并且 A[n - 2] > A[n - 1]
假定P是峰值的位置则满足A[P] > A[P-1]
且A[P] > A[P+1]
,返回数组中任意一个峰值的位置。
给出数组[1, 2, 1, 3, 4, 5, 7, 6]
返回1
, 即数值 2 所在位置, 或者6
, 即数值 7 所在位置
class Solution {
/**
* @param A: An integers array.
* @return: return any of peek positions.
*/
public int findPeak(int[] A) {
if(A.length==0||A==null){
return 0;
}
int start=0,end=A.length-1;
int mid=0;
while(start<=end){
if(start==end){
return start;
}
mid=(start+end)/2;
if(mid<A.length-1&&A[mid]<A[mid+1]){
start=mid+1;
}
else if(mid<A.length-1&&A[mid]>A[mid+1]){
end=mid;
}
}
return 0;
}
}