二分查找讲解

原创地址:http://www.cnblogs.com/segeon/archive/2012/07/27/2612361.html

很多书上都会讲到二分查找(数据结构与算法教材、《编程之美》、《编程珠玑》),这也是一个经典的面试题。尽管它很常见,大家也很熟悉,但是却不一定能够完美地写出来。今天自己整理了一下,把三种二分查找算法(找最后一次出现的某值v,找第一次出现的某值v和普通的二分查找)进行了梳理。

问题描述:有一个按非降序排列的有序数组a[0...n-1]和一个数v

1. 求数组a中最后一次出现的数v的下标

设l为左边界,h为右边界,mid =(l+h)/2,那么,根据mid的取值,分三种情况:

(1) a[mid] < v,说明v如果在数组中,应该出现在mid右侧,则调整左边界,l = mid + 1

(2) a[mid] > v,说明v如果在数组中,应该出现在mid左侧,则调整右边界,h = mid - 1

(3) a[mid] == v,中间值等于v,而要求的是最后一次出现的v。最后出现的v值一定在mid的右边或者就是mid位置的这个值,所以我们应该调整左边界,l = mid。

以上三步是循环体中的关键步骤,但容易出错的地方不在这三步中,而在于循环结束的条件。

为了更清楚地说明问题,下面讨论一下当循环体内l和h之间(包含l和h)最后只剩3个和2个元素时的情况,初始元素个数超过3个的最后都会转化为这两种情况之一。

只剩3个元素时,可能出现2中情况,一种是其中里面含有v,另一种是不含v。假设v = 2,那么含有v的可能出现以下几种情况:

(1) 若v只出现一次,可能出现的最终状态将如下图所示:

初始状态:   最终状态   l = h

初始状态:    最终状态     这种情况需要特别注意,因为此时l = mid,h = l + 1, 如果继续循环,l和h的值将不会改变

初始状态:   最终状态   ,l = h

(2)若v出现两次,可能出现的最终状态如下:

初始状态: 最终状态   h = l + 1

初始状态:    最终状态  h = l + 1,这种情况也很特殊,因为此时l和h位置的值都是v。

(3)若v出现三次,可能出现的最终状态如下:

初始状态:  最终状态  ,h = l + 1,此时l和h位置的值也都是v

如果数组中不含v,那么可能会出现以下两种情况:

(1) 中间元素小于v

初始状态:   最终状态    l = h

(2) 中间元素大于v

初始状态:  最终状态    , l = h

若数组中只剩两个元素,那么这两个元素中可能含v,也可能不含v。如果含有v,有两种情况:

(1) a[l] = v

初始状态:   最终状态   h = l + 1

(2) a[l] != v

初始状态:     最终状态   , l = h

如果两个元素中不含v,也有两种情况:

(1) a[l] < v

初始状态:    最终状态   l = h

(2) a[l] > v

初始状态: 最终状态   h = l - 1

从上面的分析可以得出结论,循环结束的条件应该是 l + 1< h,结束以后,需要检查l + 1位置的值是否和v相等,相等,则l + 1是所求的位置,否则,检查l位置的值是否和v相等,若相等,则l是所求的位置,否则,说明原数组中不存在v,返回-1。


代码如下:

/*查找给定非递减序列中给定关键字的最后一次出现的位序*/
int binarySearchLast(int *a, int l, int h, int key){
	if(!a || l>h)
		return -1;
	int mid;
	while(l+1 < h){   //循环结束条件 
		mid=(l+h)/2;
		if(a[mid] > key)
			h=mid-1;
		else if(a[mid] < key)
			l=mid+1;
		else
			l=mid;
	}
	if(a[l+1] == key)
		return l+1;
	else if(a[l] == key)
		return l;
	else 
		return -1;
}


2.查找给定非递减序列中给定关键字第一次出现的位序,如有返回位序,否则返回-1.

代码如下:

/*找给定非递减序列中给定关键字第一次出现的位序*/
int binarySearchFirst(int *a, int l, int h,int key){
	if(!a || l>h) 
		return -1;
	int mid;
	while( l< h){  //循环结束条件 
		mid=(l+h)/2;
		if(a[mid] > key)
			h=mid-1;
		else if(a[mid] < key)
			l=mid+1;
		else
			l=mid;
	}
	if(a[l] == key)
		return l;
	else
		return -1;
}


3.查找给定非递减序列中是否存在给定关键字,如有返回位序,否则返回-1.

标准二分非递归查找。

代码如下:

int binarySearch(int *a, int l, int h,int key){
	if(!a || l>h)
		return -1;
	int mid;
	while( l< h){  //循环结束条件 
		mid=(l+h)/2;
		if(a[mid] == key)
			return mid;
		else if(a[mid] > key)
			h=mid-1;
		else
			l=mid+1;
	}
	if(a[l] == key)
		return l;
	else
		return -1;
}

另外有两个相似的查找算法,给定一个非递减有序数组a,求一个最小的i使得a[i]大于key,若找不到则返回-1.

贴出代码:

/*查找给定非递减序列中第一个大于的给定值*/
int findSmallestBiggerNumber(int *a, int l, int h, int key){
	if(!a || l>h)
		return -1;
	int mid;
	while(l < h){
		mid=(l+h)/2;
		if(a[mid] <= key)
			l=mid+1;
		else
			h=mid;
	}
	if(a[l] > key)
		return l;
	else
		return -1;
} 


/*给定一个非降序数组arr, 求一个最大的i使得arr[i]小于v,若找不到则返回-1*/ 
int findLargetSmallerNumber(int *a, int l, int h, int v)
{
    if(!a || l > h)
        return -1;
    if(l == h)
    {
        if(a[l] < v)
            return l;
        else
            return -1;
    }
    int mid;
    while (l + 1 < h)
    {
        mid = l + (h-l)/2;
        if(a[mid] >= v)
            h = mid - 1;
        else
            l = mid;
    }
    if(a[l+1] < v)
        return l+1;
    if(a[l] < v)
        return l;
    return -1;
}

原文地址:http://www.cnblogs.com/segeon/archive/2012/07/27/2612361.html

你可能感兴趣的:(二分查找)