版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
二分查找问题全集1,原始二分查找题目:给定一个有序(非降序)数组A,求任意一个i使得A[i]等于target,不存在则返回-1例如:[2,4,6,8,9]找(4)位置11.1递归版[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
bSearch(int
a[],
int
low,
int
high,
int
target){
if(low
>
high)
return
-1;
int
mid
=
(low
+
high)/2;
if(target<a[mid])
return
bSearch(a,low,mid-1,target);
else
if(target>a[mid])
return
bSearch(a,mid+1,high,target);
//if(target
==
a[mid])
return
mid;
}
1.2迭代版[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
return
mid;
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-1;
}
1.3返回插入位置给定一个有序(非降序)数组A,若target在数组中出现,返回位置,若不存在,返回它应该插入的位置。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
return
mid;
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-(low+1);
}
之所以返回-(low+1)而不是直接返回-low是因为low可能为0,如果直接返回-low就无法判断是正常返回位置0还是查找不成功返回的0。2,含重复元素,求=target的最小一个问题:给定一个有序(非降序)数组A,可含有重复元素,求最小的i使得A[i]等于target,不存在则返回-1例如:A[2,4,6,8,8,8,9]求8得最小位置3[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
{
if(mid
>
0
&&
A[mid-1]
==
target)
high
=
mid-1;
else
return
mid;
}
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-(low+1);
}
3,含重复元素,求=target的最大一个问题:给定一个有序(非降序)数组A,可含有重复元素,求最大的i使得A[i]等于target,不存在则返回-1例如:A[2,4,6,8,8,8,9]求8得最大位置5[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
{
if(mid
<
n-1
&&
A[mid+1]
==
target)
low
=
mid+1;
else
return
mid;
}
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-(low+1);
}
4,含重复元素,求<target的最大一个问题:给定一个有序(非降序)数组A,可含有重复元素,求最大的i使得A[i]小于target,不存在则返回-1
例如:A[2,4,6,8,8,8,9]求9得最大位置5问题转化:含重复元素,求2【=target的最小一个】的前一个。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
{
if(mid
>
0
&&
A[mid-1]
==
target)
high
=
mid-1;
else
return
(mid==0)?-1:mid-1;
}
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-(low+1);
}
5,含重复元素,求>target的最小一个问题:给定一个有序(非降序)数组A,可含有重复元素,求最小的i使得A[i]大于target,不存在则返回-1例如:A[2,4,6,8,8,8,9]求6的最小位置3问题转化:含重复元素,求3【=target的最大一个】的后一个。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
search(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
//
注意:若使用(low+high)/2求中间位置容易溢出
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
{
if(mid
<
n-1
&&
A[mid+1]
==
target)
low
=
mid+1;
else
return
(mid==n-1)?-n:mid+1;
}
else
if(A[mid]
<
target)
low
=
mid+1;
else
//
A[mid]
>
target
high
=
mid-1;
}
return
-(low+1);
}
6,含重复元素,求=target的出现次数问题:给定一个有序(非降序)数组A,可含有重复元素,求target在数组中出现的次数。例如:A[2,4,6,8,8,8,9]求8的出现次数3求出第一次出现位置和最后一次出现位置。由于前面都已实现,这里不多解释。请参考实现代码与注释[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
count(int
A[],
int
n,
int
target)
{
int
firstPos
=
searchFirstPos(A,
n,
target);
//
第一次出现位置
if(firstPos
==
-1)
return
0;
int
lastPos
=
searchLastPos(A,
n,
target);
//
最后一次出现位置
return
lastPos-firstPos+1;
//
出现次数
}
7,含重复元素,求绝对值最小的元素问题:给定一个有序(非降序)数组A,可含有重复元素,求绝对值最小的元素的位置例如:A[-4,-2,-1,2,3,8,9]求结果为2[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
searchMinAbs(int
A[],
int
n)
{
int
low
=
0,
high
=
n-1;
while(low
<
high)
{
int
mid
=
low+((high-low)>>1);
if(A[mid]
<
0)
low
=
mid+1;
else
//
A[mid]
>=
0
high
=
mid;
}
/*
循环结束时,如果low
!=
n-1,A[low]
>=
0,如果low>0,A[low-1]
<
0
*/
if(low
>
0
&&
abs(A[low-1])
<
abs(A[low]))
return
low-1;
else
return
low;
}
8,问题:给定一个有序(非降序)数组A和一个有序(非降序)数组B,可含有重复元素,求两个数组合并结果中的第k(k>=0)个数字。
这个题目出现了两个数组,有序的,不管怎样我们就应该首先考虑二分查找是否可行。若使用顺序查找,时间复杂度最低为O(k),就是类似归并排序中的归并过程。使用用二分查找时间复杂度为O(logM+logN)。二分查找的具体实现过程请参考实现代码与注释。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
findKthIn2SortedArrays(int
A[],
int
m,
int
B[],
int
n,
int
k)
{
if(m
<=
0)
//
数组A中没有元素,直接在B中找第k个元素
return
B[k];
if(n
<=
0)
//
数组B中没有元素,直接在A中找第k个元素
return
A[k];
int
i
=
(m-1)>>1;
//
数组A的中间位置
int
j
=
(n-1)>>1;
//
数组B的中间位置
if(A[i]
<=
B[j])
//
数组A的中间元素小于等于数组B的中间元素
{
/*
设x为数组A和数组B中小于B[j]的元素数目,则i+1+j+1小于等于x,
因为A[i+1]到A[m-1]中还可能存在小于等于B[j]的元素;
如果k小于i+1+j+1,那么要查找的第k个元素肯定小于等于B[j],
因为x大于等于i+1+j+1;既然第k个元素小于等于B[j],那么只
需要在A[0]~A[m-1]和B[0]~B[j]中查找第k个元素即可,递归调用下去。
*/
if(k
<
i+1+j+1)
{
if(j
>
0)
return
findKthIn2SortedArrays(A,
m,
B,
j+1,
k);
else
//
j
==
0时特殊处理,防止死循环
{
if(k
==
0)
return
min(A[0],
B[0]);
if(k
==
m)
return
max(A[m-1],
B[0]);
return
A[k]
<
B[0]
?
A[k]
:
max(A[k-1],
B[0]);
}
}
/*
设y为数组A和数组B中小于于等于A[i]的元素数目,则i+1+j+1大于等于y;
如果k大于等于i+1+j+1,那么要查找到第k个元素肯定大于A[i],因为
i+1+j+1大于等于y;既然第k个元素大于A[i],那么只需要在A[i+1]~A[m-1]
和B[0]~B[n-1]中查找第k-i-1个元素,递归调用下去。
*/
else
return
findKthIn2SortedArrays(A+i+1,
m-i-1,
B,
n,
k-i-1);
}
//
如果数组A的中间元素大于数组B的中间元素,那么交换数组A和B,重新调用即可
else
return
findKthIn2SortedArrays(B,
n,
A,
m,
k);
}
9问题:
一个有序(升序)数组,没有重复元素,在某一个位置发生了旋转后,求target在变化后的数组中出现的位置,不存在则返回-1如0124567可能变成4567012
我们先比较中间元素是否是目标值,如果是返回位置。如果不是,我们就应该想办法将搜索区间减少一半。因为存在旋转变化,所以我们要多做一些判断。我们知道因为只有一次旋转变化,所以中间元素两边的子数组肯定有一个是有序的,那么我们可以判断target是不是在这个有序的子数组中,从而决定是搜索这个子数组还是搜索另一个子数组。具体请参考实现代码与注释。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
searchInRotatedArray(int
A[],
int
n,
int
target)
{
int
low
=
0,
high
=
n-1;
while(low
<=
high)
{
int
mid
=
low+((high-low)>>1);
if(A[mid]
==
target)
return
mid;
if(A[mid]
>=
A[low])
{
//
low
~
mid
是升序的
if(target
>=
A[low]
&&
target
<
A[mid])
high
=
mid-1;
else
low
=
mid+1;
}
else
{
//
mid
~
high
是升序的
if(target
>
A[mid]
&&
target
<=
A[high])
low
=
mid+1;
else
high
=
mid-1;
}
}
return
-1;
}
如果这样的数组中存在重复元素,还能使用二分吗?答案是不能。请看几个例子
[1,2,2,2,2],[2,1,2,2,2],[2,2,1,2,2],[2,2,2,1,2],[2,2,2,2,1]这些都是有第一个数组旋转一次变化来的,我们不能通过二分确定是否存在元素1.10,问题:一个有序(升序)数组,没有重复元素,在某一个位置发生了旋转后,求最小值所在位置如果中间元素小于左端元素,则最小值在左半区间内(包含中间元素);如果中间元素大于右端元素,则最小值在右半区间内(包含中间元素)。请参考实现代码与注释。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
searchMinInRotatedArray(int
A[],
int
n)
{
if(n
==
1)
return
0;
int
low
=
0,
high
=
n-1;
while(low
<
high-1)
//
保证mid
!=
low且mid
!=
high
{
int
mid
=
low+((high-low)>>1);
if(A[mid]
<
A[low])
//
最小值在low~mid
high
=
mid;
else
//
A[mid]
>
A[low],
//
最小值在mid和high之间
low
=
mid;
}
return
A[low]
<
A[low+1]
?
low
:
low+1;
}
11,问题:
一个有序(升序)数组,没有重复元素,在某一个位置发生了旋转后,求第k(k>0)小元素的位置
我们可以利用上一题的解答,求出最小值所在位置后,便可以求出第k小元素。请参考实现代码与注释[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
searchKthInRotatedArray(int
A[],
int
n,
int
k)
{
int
posMin
=
searchMinInRotatedArray(A,
n);
return
(posMin+k-1)%n;
}
12,查找旋转数组的最小数字题目:即找分界点,比如34512,返回的是位置3。以题目中的旋转数组为例,{3,4,5,1,2},我们可以有序数组经过旋转以后被分割为两段有序的数组,比如此处被分为{3,4,5}{1,2}这样连个数组,并且前半段数组中的数字肯定大于等于后半段的数组。我们找中间元素a[mid],让其跟元素首元素a[low]和尾元素a[high]比较,如果大于首元素a[low],则中间元素属于前半段有序数组;如果小于尾元素a[high],那么中间元素就是后半段的元素。这里我们设定两个指针start和end分别指向数组的首尾元素,然后当start指向前半段最后一个元素,end指向后半段第一个元素,这是程序就找到了数组中的最小元素,就是end指向的那个数,程序的出口就是end-start==1。[cpp]\o"viewplain"viewplain\o"copy"copy\o"print"print\o"?"?int
bSearchMinValue(int
a[],
int
low,
int
high){
//终止递归条件是low和high差1,原因是后面mid都不是+1-1处理的
if(low+1==high)
return
high;
int
mid
=
(low
+
high)/2;
if(a[mid]>a[low])//左侧有序,在右边找分界点
return
bSearchMinValue(a,mid,high);
if(a[mid]<a[high])//右侧有序,在左边找分界点
return
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 七年级生物下册知识点(济南版)
- 中山大学排球教案
- 临时仓储设施转租协议
- 交通运输服务创新投标管理
- 互联网公司技术支持人员就业合同
- 人才招聘定金协议书
- 仓库租赁合同书范本制造业版
- 个人合伙协议书撰写指南
- 企业现金支票管理规范
- 5S管理办公室整洁的艺术
- 2024届高考英语复习语法填空课件
- 监控设备保养维护方案
- 公立医院绩效考核表
- 华电人才测评试题在线测试
- 《带压堵漏技术》课件
- 铜矿矿山规划与布局
- 备考2023高考语文二轮 高中语文 山水田园类诗歌阅读专项练习(解析)
- 人教版二年级上册口算练习1000题及答案
- 2024年浙江建银工程咨询有限责任公司招聘笔试参考题库含答案解析
- 水痘护理课件
- 设立招投标代理公司可行性研究报告
评论
0/150
提交评论