二分查找的各种情况实现以及一些注意点

转载请注明出处,谢谢:

http://blog.csdn.net/u014285517/article/details/45341741

 

说真的自己开始也认为二分查找实在太简单了,不屑一顾,可是上礼拜阿里实习面试真是教会我做人要踏实啊!上礼拜面试的事等下有时间再写一篇文章细谈,现在谈谈二分查找,建议大家先自己写写,然后再看下面的文章,效果更好。

  让我们先看看一个朴素版的二分查找(其实也不算太朴素,不过因为这部分网上以及编程之美里都有详细描述以及代码实现,所以也就显得比较朴素了,注意事项会在代码注释里说明): 

 

/*
最朴素的二分查找:找数组a的下标s到下标e查找元素t 
*/
#include<stdio.h>

int binarySearch(int a[],int s,int e,int t) {
	int i = s,j = e,mid;
	while(i <= j) {
	 /* 
	(1)写成(i+j)/2,可能会导致求和中间结果的溢出,因为两个32位整数的和是有可能超过32位整数的范围的嘛 
	(2)因为四则运算符的优先级高于移位运算符>>,所以可以把mid = i+((j-i)>>1)简化下,不过注意不要写成mid = i+(j-i)>>1哈 
	(3)因为移位运算符比直接除快嘛,所以用>>1代替除2 
	*/
		mid = i+(j-i>>1);//(i+j)/2,写成这样可能会溢出 
		if(a[mid] == t) {
			return mid;
		} else {
			if(a[mid] > t) {
				j = mid-1;
			} else {
				i = mid+1;
			}
		}
	}
	return -1;		
}

int main() {
	int a[10] = {1,3,5,6,7,8,9,12,13,14};
	int t;
	if((t = binarySearch(a,0,9,7)) == -1) {
		printf("不存在\n");
	} else {
		printf("存在,下标为%d\n",t);
	}
}

 

上面的代码还有什么问题呢?大家可以先想想,再往下看哈。

 

显然如果要查找的数在数组中存在着重复元素,而且我们还想返回该数的最小或最大下标,显然上面的程序是无法做到的,因为它一旦找到一个满足的就退出了。

下面我们来想想怎么解决这个问题。 最容易想到的办法就是在数组中找到某一个数后,直接不断循环减(加),来寻找满足条件的最小下标(最大下标)。相应代码如下:

 

#include<stdio.h>

int binarySearch(int a[],int s,int e,int t) {
	int i = s,j = e,mid;
	while(i <= j) {
		mid = i+(j-i>>1);
		if(a[mid] == t) {//关键部分代码(查找数组中等于t的数的最小下标,最大下标同理)
			while(a[--mid] == t) {
				
			}
			return mid+1;
		} else {
			if(a[mid] > t) {
				j = mid-1;
			} else {
				i = mid+1;
			}
		}
	}
	return -1;		
}

int main() {
	int a[10] = {1,3,5,9,9,9,9,12,13,14};
	int t;
	if((t = binarySearch(a,0,9,9)) == -1) {
		printf("不存在\n");
	} else {
		printf("存在,下标为%d\n",t);
	}
}


现在可以想想上面的代码存在什么问题吗?

 

 

 

 

显然如果重复的元素过多,二分查找搞不好就直接退化成暴力查找了,所以是不太好的。大家可以自己先写写解决代码,然后后看下面的内容

 

其实找最小下标和找最大下标可以用一套代码解决,我们可以在在[low, high)中搜索第一个大于或等于target的位置,这样找最大下标的时候只要搜索第一个大于或等于target+1的位置,然后减1即可,不过需要检查下ix是否在合法范围内,以及对应的值是否是target。具体代码如下:

 

#include<stdio.h>

int binarySearch(int a[],int s,int e,int t) {
	int i = s,j = e,mid;
	while(i < j) {
		mid = i+(j-i>>1);
		if(a[mid] >= t) {
			j = mid;
		} else {
			i = mid+1;
		}
	}
	return i;
}

int main() {
	int a[10] = {1,3,9,9,9,9,9,9,13,14};
	int ix, target = 9;

	//找最小下标
	ix = binarySearch(a,0,10,target);
	if(ix == -1 || ix == 10 || a[ix] != target) {
		printf("最小下标不存在\n");
	} else {
		printf("存在,最小下标为%d\n",ix);
	}

	ix = binarySearch(a,0,10,100);
	if(ix == -1 || ix == 10 || a[ix] != target) {
		printf("最小下标不存在\n");
	} else {
		printf("存在,最小下标为%d\n",ix);
	}

	ix = binarySearch(a,0,10,-100);
	if(ix == -1 || ix == 10 || a[ix] != target) {
		printf("最小下标不存在\n");
	} else {
		printf("存在,最小下标为%d\n",ix);
	}

	//找最大下标
	ix = binarySearch(a,0,10,target+1);
	ix -= 1;
	if(ix == -1 || ix == 10 || a[ix] != target) {
		printf("最大下标不存在\n");
	} else {
		printf("存在,最大下标为%d\n",ix);
	}

	ix = binarySearch(a,0,2,-100);
	ix -= 1;
	if(ix == -1 || ix == 10 || a[ix] != target) {
		printf("最大下标不存在\n");
	} else {
		printf("存在,最大下标为%d\n",ix);
	}

}

 


下面我们再来思考两个类似问题,加深理解。

 

 

在一个非递减整数数组中,求最小的下标i,使得a[i] > num,存在返回下标i,不存在返回-1。 

 

其实等价于找使a[i]大于等于num+1的最小下标,直接用binarySearch即可,实现代码如下:

 

#include<stdio.h>

int binarySearch(int a[],int s,int e,int t) {
	int i = s,j = e,mid;
	while(i < j) {
		mid = i+(j-i>>1);
		if(a[mid] >= t) {
			j = mid;
		} else {
			i = mid+1;
		}
	}
    return i;
}

int main() {
    int a[10] = {1,3,9,9,9,9,9,9,13,14};
    int ix, num, target;
	
	//找最小下标
    num = 9;
    target = num+1;
    ix = binarySearch(a,0,10,target);
    if(ix == -1 || ix == 10) {
        printf("最小下标不存在\n");
    } else {
        printf("存在,最小下标为%d\n",ix);
    }

    num = 100;
    target = num+1;
    ix = binarySearch(a,0,10,target);
    if(ix == -1 || ix == 10) {
        printf("最小下标不存在\n");
    } else {
        printf("存在,最小下标为%d\n",ix);
    }

    num= -100;
    target = num+1;
    ix = binarySearch(a,0,10,target);
    if(ix == -1 || ix == 10) {
        printf("最小下标不存在\n");
    } else {
        printf("存在,最小下标为%d\n",ix);
    }

}


另一个类似问题:

 

在一个非递减整数数组中,求最大的下标i,使得a[i] < num,存在返回下标i,不存在返回-1。 

 

其实等价于找使a[i]大于等于num的最小下标,再减1即可。

实现代码如下:

 

# 在一个非递减整数数组中,求最大的下标i,使得a[i] < num,存在返回下标i,不存在返回-1。 


#include<stdio.h>

int binarySearch(int a[],int s,int e,int t) {
	int i = s,j = e,mid;
	while(i < j) {
		mid = i+(j-i>>1);
		if(a[mid] >= t) {
			j = mid;
		} else {
			i = mid+1;
		}
	}
    return i;
}

int main() {
    int a[10] = {1,3,9,9,9,9,9,9,13,14};
    int ix, num, target;
	
    num = 9;
    target = num;
    ix = binarySearch(a,0,10,target);
	ix -= 1;
    if(ix == -1 || ix == 10) {
        printf("最大下标不存在\n");
    } else {
        printf("存在,最大下标为%d\n",ix);
    }

    num = 100;
    target = num;
    ix = binarySearch(a,0,10,target);
	ix -= 1;
    if(ix == -1 || ix == 10) {
        printf("最大下标不存在\n");
    } else {
        printf("存在,最大下标为%d\n",ix);
    }

    num= -100;
    target = num;
    ix = binarySearch(a,0,10,target);
	ix -= 1;
    if(ix == -1 || ix == 10) {
        printf("最大下标不存在\n");
    } else {
        printf("存在,最大下标为%d\n",ix);
    }

}


如果把上面两个问题改成下面这样: 

 

1.在一个非递减数组中(就是数组中存在相等的元素),求最**大**的下标i,使得a[i] **>** t,存在返回下标i,不存在返回-1。 

2.在一个非递减数组中(就是数组中存在相等的元素),求最**小**的下标i,使得a[i] **<** t,存在返回下标i,不存在返回-1。 

大家稍微想下应该能发现这种问题意义不大,我这里就不细说了。 

 

注:上面代码都是自己写的,如果有bug欢迎指出。上面说的一些问题如果有错误也欢迎指出,谢谢。

 

补充java写的一个递归实现代码:

 

    //递归实现
    public void binarySearch(int[] a, int x, int beginIndex, int endIndex) {
        int len = endIndex - beginIndex + 1;
        if (endIndex < beginIndex) {//防止进入无限递归
            System.out.println("not find");
            return;
        }
        if (a[beginIndex + len / 2] == x) {
            System.out.println("find index is:" + (beginIndex + len / 2));
            return;
        } else {
            if (x > a[beginIndex + len / 2]) {
                binarySearch(a, x, beginIndex + len / 2 + 1, endIndex);
            } else {
                binarySearch(a, x, beginIndex, beginIndex + len / 2 - 1);
            }
        }
    }

 

 

 

 

 

 

 

 


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值