二分查找问题全集_第1页
二分查找问题全集_第2页
二分查找问题全集_第3页
二分查找问题全集_第4页
二分查找问题全集_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

二分查找问题全集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. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论