【44期】盘点那些必问的数据结构算法题之二分查找算法
0 概述
1 二分查找基础
/**
* 基本二分查找算法
*/
int binarySearch(int a[], int n, int t)
{
int l = 0, u = n - 1;
while (l <= u) {
int m = l + (u - l) / 2; // 同(l+u)/ 2,这里是为了防溢出
if (t > a[m])
l = m + 1;
else if (t < a[m])
u = m - 1;
else
return m;
}
return -(l+1);
}
http://blog.csdn.net/ssjhust123/article/details/7798737
2 查找有序数组中数字第一次出现位置
/**
* 二分查找第一次出现位置
*/
int binarySearchFirst(int a[], int n, int t)
{
int l = -1, u = n;
while (l + 1 != u) {
/*循环不变式a[l]
int m = l + (u - l) / 2; //同(l+u)/ 2
if (t > a[m])
l = m;
else
u = m;
}
/*assert: l+1=u && a[l]
int p = u;
if (p>=n || a[p]!=t)
p = -1;
return p;
}
a[-1] < t <= a[n
],但是我们并不会去访问这两个元素,因为(l+u)/2 > l=-1
, (l+u)/2 < u=n
。循环不变式为la[l] && t<=a[u]
。循环退出时必然有l+1=u, 而且a[l] < t <= a[u]
。举个例子:对于数组a={1, 2, 3, 3, 5, 7, 8},我们如果查找t=3,则可以得到p=u=2,如果查找t=4,a[3] =n, 比如t=9,则u=7,此时也是设置p=-1.特别注意的是,l=-1,u=n这两个值不能写成l=0,u=n-1。虽然这两个值不会访问到,但是如果改成后面的那样,就会导致二分查找失败,那样就访问不到第一个数字。如在a={1,2,3,4,5}中查找1,如果初始设置l=0,u=n-1,则会导致查找失败。
扩展
/**
* 二分查找最后一次出现位置
*/
int binarySearchLast(int a[], int n, int t)
{
int l = -1, u = n;
while (l + 1 != u) {
/*循环不变式, a[l] <= t < a[u]*/
int m = l + (u - l) / 2;
if (t >= a[m])
l = m;
else
u = m;
}
/*assert: l+1 = u && a[l] <= t < a[u]*/
int p = l;
if (p<=-1 || a[p]!=t)
p = -1;
return p;
}
/**
* 二分查找第一次和最后一次出现位置
*/
int binarySearchFirstAndLast(int a[], int n, int t, int firstFlag)
{
int l = 0;
int u = n - 1;
while(l <= u) {
int m = l + (u - l) / 2;
if(a[m] == t) { //找到了,判断是第一次出现还是最后一次出现
if(firstFlag) { //查询第一次出现的位置
if(m != 0 && a[m-1] != t)
return m;
else if(m == 0)
return 0;
else
u = m - 1;
} else { //查询最后一次出现的位置
if(m != n-1 && a[m+1] != t)
return m;
else if(m == n-1)
return n-1;
else
l = m + 1;
}
}
else if(a[m] < t)
l = m + 1;
else
u = m - 1;
}
return -1;
}
3 旋转数组元素查找问题
题目
分析
/**
* 旋转数组查找-两次二分查找
*/
int binarySearchRotateTwice(int a[], int n, int t)
{
int p = findRotatePosition(a, n); //找到旋转位置
if (p == -1)
return binarySearchFirst(a, n, t); //如果原数组有序,则直接二分查找即可
int left = binarySearchFirst(a, p+1, t); //查找左半部分
if (left != -1)
return left; //左半部分找到,则直接返回
int right = binarySearchFirst(a+p+1, n-p-1, t); //左半部分没有找到,则查找右半部分
if (right == -1)
return -1;
return right+p+1; //返回位置,注意要加上p+1
}
/**
* 查找旋转位置
*/
int findRotatePosition(int a[], int n)
{
int i;
for (i = 0; i < n-1; i++) {
if (a[i+1] < a[i])
return i;
}
return -1;
}
如果左边是有序的,若
ta[l]
, 则 u=m-1;其他情况,l =m+1;如果右边是有序的,若
t> a[m] && t 则 l=m+1;其他情况,u =m-1;
/**
* 旋转数组二分查找-一次二分查找
*/
int binarySearchRotateOnce(int a[], int n, int t)
{
int l = 0, u = n-1;
while (l <= u) {
int m = l + (u-l) / 2;
if (t == a[m])
return m;
if (a[m] >= a[l]) { //数组左半有序
if (t >= a[l] && t < a[m])
u = m - 1;
else
l = m + 1;
} else { //数组右半段有序
if (t > a[m] && t <= a[u])
l = m + 1;
else
u = m - 1;
}
}
return -1;
}
推荐阅读:
微信扫描二维码,关注我的公众号
朕已阅
评论