算法设计之二分法搜索课程设计.docx

上传人:b****5 文档编号:5065265 上传时间:2022-12-13 格式:DOCX 页数:13 大小:104.31KB
下载 相关 举报
算法设计之二分法搜索课程设计.docx_第1页
第1页 / 共13页
算法设计之二分法搜索课程设计.docx_第2页
第2页 / 共13页
算法设计之二分法搜索课程设计.docx_第3页
第3页 / 共13页
算法设计之二分法搜索课程设计.docx_第4页
第4页 / 共13页
算法设计之二分法搜索课程设计.docx_第5页
第5页 / 共13页
点击查看更多>>
下载资源
资源描述

算法设计之二分法搜索课程设计.docx

《算法设计之二分法搜索课程设计.docx》由会员分享,可在线阅读,更多相关《算法设计之二分法搜索课程设计.docx(13页珍藏版)》请在冰豆网上搜索。

算法设计之二分法搜索课程设计.docx

算法设计之二分法搜索课程设计

《算法设计与分析》课程设计说明书

用二分搜索法查找数据

 

系、部:

计算机与信息科学系

学生姓名:

专业:

信息与计算科学

班级:

完成时间:

2012/5/27

摘要

 

折半查找法也称为二分查找法或二分搜索法,它充分利用了元素间的次序关系,采用分治策略而较快地查找数据。

现要求给出一个待查找的实例,并给出二分搜索算法,编写程序利用此算法实现查找。

它的基本思想是,将n个元素分成个数大致相同的两半,取a[n/2]与欲查找的x作比较,如果x=a[n/2]则找到x,算法终止。

如果x

如果x>a[n/2],则我们只要在数组a的右半部继续搜索x。

二分搜索法的应用极其广泛,而且它的思想易于理解,但是要写一个正确的二分搜索算法也不是一件简单的事。

第一个二分搜索算法早在1946年就出现了,但是第一个完全正确的二分搜索算法直到1962年才出现。

Bentley在他的著作《WritingCorrectPrograms》中写道,90%的计算机专家不能在2小时内写出完全正确的二分搜索算法。

问题的关键在于准确地制定各次查找范围的边界以及终止条件的确定,正确地归纳奇偶数的各种情况。

关键词:

二分法;折半;查找

 

 

1引言………………………………………………………………………………3

1.1问题的提出…………………………………………………………………3

1.2二分法搜索法运用领域……………………………………………………4

1.3二分法………………………………………………………………………5

1.4任务与分析…………………………………………………………………5

2二分法搜索详细设计……………………………………………………………6

2.1二分搜索算法………………………………………………………………6

2.2二分法算法的实现及编程…………………………………………………6

2.3二分法应用举例……………………………………………………………7

3程序测试与运行结果……………………………………………………………8

3.1程序测试过程………………………………………………………………8

3.2运行结果……………………………………………………………………9

3.3算法复杂度分析……………………………………………………………11

4心得总结…………………………………………………………………………11

参考文献………………………………………………………………………………….

附录………………………………………………………………………………………

 

1

引言

 

1.1问题的提出

1.1.1什么是二分搜索法?

举个简单的例子,如果我们看这样一本书,这本书每一页次都是分开的,没有装辑成册,同时页次顺序又是打乱了的。

这时候我们要看这本书的某一页,比如第55页,那我们就必须从第一张翻看直到找到我们需要的55页出现。

这种方法称为顺序搜索。

现在还是这本书,不过把它装辑成册,这时候页次是按从小到大的顺序排列的,但由于上一次好多同学顺序查找的原因,这次有些页面丢失了。

现在我们通常要怎样查找我们需要的页次(页次可能不存在)呢?

假设这本书共有100页,如果要查找第30页,我们通常的做法是:

首先翻到这本书的中间部分(由于我们都是有头脑的人,在这种已知条件下,因为30大约是100的三分之一,所以我们会翻到书本的三分之一处,但计算机不同于人脑,计算机是最笨的家伙,它可不会动这样的脑子,所以说计算机永远也代替不了人脑,机器人不会控制人类。

),如果这时候正是我们要查找的页次,则可以直接查看,如果此页次大于要查找的页次,这时候我们肯定不会再向后翻了吧(我们都不傻,是吧!

),我们就把后半部分抛弃,而是从前半部分进行查找;如果此页次小于要查找的页次,这时候我们肯定不会再向前翻,就把前半部分抛弃,而是从后半部分进行查找。

对于前半部分,或后半部分,我们还是会首先翻看其中间部分,重复这些操作直到找到要查找的页次。

正因为这样的查找总是从中间查起,每次都将要查找的数据一分为二,所以叫二分查找(搜索)法。

二分搜索法的应用极其广泛,而且它的思想易于理解,问题的关键在于准确地制定各次查找范围的边界以及终止条件的确定,正确地归纳奇偶数的各种情况,其实整理后可以发现它的具体算法是很直观的,我们可用C++描述如下:

template  

intBinarySearch(Typea[],constType&x,intn) 

{intleft=0;intright=n-1;  

while(left<=right)

{ intmiddle=(left+right)/2;  

if(x==a[middle])

returnmiddle;  

if(x>a[middle])left=middle+1;  

elseright=middle-1;  }  

return-1;  }  

模板函数BinarySearch在a[0]<=a[1]<=...<=a[n-1]共n个升序排列的元素中搜索x,找到x时返回其在数组中的位置,否则返回-1。

容易看出,每执行一次while循环,待搜索数组的大小减少一半,因此整个算法在最坏情况下的时间复杂度为O(logn)。

在数据量很大的时候,它的线性查找在时间复杂度上的优劣一目了然。

  

1.1.2二分搜索的局限性

必须是在有序的元素中进行,不能在无序的元素中使用。

1.2二分搜索法运用领域

二分法现在已经运用到各个领域。

在数学方面,一般地,对于函数f(x),如果存在实数c,当x=c时,若f(c)=0,那么把x=c叫做函数f(x)的零点。

解方程即要求f(x)的所有零点。

通过每次把f(x)的零点所在小区间收缩一半的方法,使区间的两个端点逐步迫近函数的零点,以求得零点的近似值,这种方法叫做二分法。

在经济学方面,传统的经济学家把经济分为实物经济和货币经济两部分,其中,经济理论分析实际变量的决定,而货币理论分析价格的决定,两者之间并没有多大的关系,这就是所谓的二分法。

在哲学方面,又称二分说,爱利亚学派芝诺四大著名悖论之一,证明运动是不可能的。

其主要思路是:

假设一个存在物经过空间而运动,为了穿越某个空间,就必须穿越这个空间的一半。

为了穿越这一半,就必须穿越这一半的一半;以此类推,直至无穷。

所以,运动是不可能的。

在日常生活中,即将所有的事物根据其属性分成两种。

错误的分类可能导致逻辑谬论,如:

非黑即白,不是忠的就是奸的。

这很明显忽略了中间状态的存在。

正确的分类法应如:

白-非白。

但是二分法在计算机领域是用得最多的,他可以坚决许多复杂的问题,使得复杂的问题变得简单易解。

1.3二分法

在对线性表的操作中,经常需要查找某一个元素在线性表中的位置。

此问题的输入是待查元素x和线性表L,输出为x在L中的位置或者x不在L中的信息。

比较自然的想法是一个一个地扫描L的所有元素,直到找到x为止。

这种方法对于有n个元素的线性表在最坏情况下需要n次比较。

一般来说,如果没有其他的附加信息,在有n个元素的线性表中查找一个元素在最坏情况下都需要n次比较。

下面我们考虑一种简单的情况。

假设该线性表已经排好序了,不妨设它按照主键的递增顺序排列(即由小到大排列)。

在这种情况下,我们是否有改进查找效率的可能呢?

如果线性表里只有一个元素,则只要比较这个元素和x就可以确定x是否在线性表中。

因此这个问题满足分治法的第一个适用条件;同时我们注意到对于排好序的线性表L有以下性质:

比较x和L中任意一个元素L[i],若x=L[i],则x在L中的位置就是i;如果xL[i],同理我们只要在L[i]的后面查找x即可。

无论是在L[i]的前面还是后面查找x,其方法都和在L中查找x一样,只不过是线性表的规模缩小了。

这就说明了此问题满足分治法的第二个和第三个适用条件。

很显然此问题分解出的子问题相互独立,即在L[i]的前面或后面查找x是独立的子问题,因此满足分治法的第四个适用条件。

于是我们得到利用分治法在有序表中查找元素的算法

1.4任务与分析

1.4.1设计内容:

折半查找法也称为二分查找法或二分搜索法,它充分利用了元素间的次序关系,采用分治策略而较快地查找数据。

现要求给出一个待查找的实例,并给出二分搜索算法,编写程序利用此算法实现查找。

1.4.2设计要求:

(1)给出分治思想;

(2)给出一个实际应用;

(3)给出二分搜索算法;

(4)编程实现算法;

(5)给出算法的时间复杂度分析。

 

2二分法详细设计

 

2.1二分搜索算法

下面我们考虑一种简单的情况。

假设该线性表已经排好序了,不妨设它按照主键的递增顺序排列(即由小到大排列)。

在这种情况下,我们是否有改进查找效率的可能呢?

如果线性表里只有一个元素,则只要比较这个元素和x就可以确定x是否在线性表中。

因此这个问题满足分治法的第一个适用条件;同时我们注意到对于排好序的线性表L有以下性质:

比较x和L中任意一个元素L[i],若x=L[i],则x在L中的位置就是i;如果xL[i],同理我们只要在L[i]的后面查找x即可。

无论是在L[i]的前面还是后面查找x,其方法都和在L中查找x一样,只不过是线性表的规模缩小了。

这就说明了此问题满足分治法的第二个和第三个适用条件。

很显然此问题分解出的子问题相互独立,即在L[i]的前面或后面查找x是独立的子问题,因此满足分治法的第四个适用条件。

于是我们得到利用分治法在有序表中查找元素的算法。

functionBinary_Search(L,a,b,x);

begin

ifa>bthenreturn(-1)

elsebegin

m:

=(a+b)div2;

ifx=L[m]thenreturn(m)

elseifx>L[m]

then

return(Binary_Search(L,m+1,b,x));

else

return(Binary_Search(L,a,m-1,x));

end;

end;

2.2二分法算法实现与编程

在常规的二分查找法查找到一个符合条件数便会停止,如果数组中存在多个符合条件的数,并不会全部输出,二分查找法(折半查找法)的算法(见附录)

2.3二分法应用举例

2.3.1用二分法求方程的近似解

问题:

一元二次方程可用判别式判定根的存在性,可用求根公式求方程的根.但对于一般的方程,虽然可用零点存在性定理判定根的存在性,而没有公式.求根:

如何求得方程的根呢?

例用二分法求函数f(x)=x3–3的一个正实数零点(精确到0.1).

由于f

(1)=–2<0,f

(2)=5>0,因此可以确定区间[1,2]作为计算的初始区间,用二分法逐步计算,列表如下:

端点或中点的横坐标

计算端点或中点的函数值

定区间

a0=1,b0=2

f

(1)=–2,f

(2)=5

[1,2]

f(x0)=0.375>0

[1,1.5]

f(x1)=–1.0469<0

[1.25,1.5]

f(x2)=–0.4004<0

[1.375,1.5]

f(x3)=–0.0295<0

[1.4375,1.5]

f(x4)=0.1684>0

[1.4375,1.46875]

f(x5)>0

[1.4375,1.453125]

x6=1.4453125

f(x6)>0

[1.4375,1.4453125]

由上表的计算可知区间[1.4375,1.4453125]的左、右端点精确到0.1所取的近似值都是1.4,所以1.4可作为所求函数的一个正实数零点的近似值.

 

3程序测试与运行结果

 

3.1程序测试过程

1.我们的运行幻境:

操作系统:

windowsXP,语言环境:

VC++6.0。

2.核心代码及调试过程

intmain()

{intL[100],search,pos,len;

cout<<"请输入数组的长度:

";

cin>>len;

cout<<"请输入长度为"<

";

for(inti=0;i>L[i];

cout<<"请输入要查找的数:

";

cin>>search;

pos=binarySearh(L,len,search);

if(pos==-1)

cout<<"非递归算法没有找到该数"<

elsecout<<"非递归算法找到该数的位置为:

"<

pos=binarySearh2(L,0,(len-1),search);

if(pos==-1)cout<<"递归算法没有找到该数"<

elsecout<<"递归算法找到该数的位置为:

"<

system("pause");

return0;}

intbinarySearh(intArr[],intlen,intsearch){

//二分查找非递归算法函数,返回找到的位置

intlow,mid,high;

low=0;high=len-1;

while(low<=high){

//当low指针不在high指针右边的时候指向循环

mid=(low+high)/2;//设置查找中点

if(Arr[mid]==search)returnmid;//如果找到,返回位置

elseif(Arr[mid]>search)high=mid-1;

//如果mid指针指向的元素值大于查找元素,令high指针指向mid指针前一个元素

elselow=mid+1;

//如果mid指针指向的元素值小于查找元素,令low指针指向mid的后一个元素

}

return-1;//没有找到则返回-1}

intbinarySearh2(intArr[],intlow,inthigh,intsearch){

//二分查找递归算法函数,返回找到的位置

if(low<=high){

intmid=(low+high)/2;//设置查找中点

if(Arr[mid]==search)returnmid;//如果找到,返回位置

else

{if(Arr[mid]>search)high=mid-1;

//如果mid指针指向的元素值大于查找元素,令high指针指向mid指针前一个元素

elselow=mid+1;

//如果mid指针指向的元素值小于查找元素,令low指针指向mid的后一个元素

returnbinarySearh2(Arr,low,high,search);

//如果没找到,递归寻找

}

}return-1;//没有找到则返回-1}

3.2运行结果

输入长度为三的数组

当K=70时,运行结果为:

当K=45时,我们会发现,数据中没有该数,起运行结果为:

 

3.3算法复杂度分析

在以上算法中,L为排好序的线性表,x为需要查找的元素,b,a分别为x的位置的上下界,即如果x在L中,则x在L[a..b]中。

每次我们用L中间的元素L[m]与x比较,从而确定x的位置范围。

然后递归地缩小x的范围,直到找到x。

下面分析该算法的复杂性。

设在n个元素的数组中查找x需要的比较次数为T(n),如果每次比较x和L[m]时,总有x<>L[m],即x根本不在L中,则:

T(n)=2+T(n/2),T

(1)=1

该方程的解为T(n)=O(logn)。

所以在最坏情况下二分查找法的复杂度为O(logn)。

4心得体会与总结

通过这次课程设计,我认识到了要做好一个课程设计,只依靠在课堂上学习的知识是远远不够的,必须要查阅大量的书籍,通过查找资料,我知道了二分法在实际中的运用非常广泛,二分法对我们解决问题有很大的帮助。

二分法是分治法的特殊算法,是分治法的一个非常不典型的特例。

二分查找已经是一种最优的查找算法,但还有一些查找算法具有更优的平均效率,其中一个算法(散列查找)甚至不需要它的输入时有序的。

最后折半查找所包含的思想不仅仅能运用于查找,它还可以对一元非线性方程求解。

这次课程设计做不同以往的地方就是在课设中使用了系统的,工程化的方法和技术。

借鉴了很过原来的成功项目的实践经验。

今后的实践和工作都应始终遵循工程化原则,以保证任务的准确和高效完成。

 

参考文献

[1]《算法设计与分析》周培德电子工业出版社,2000。

[2]《算法设计与分析》(第三版)王晓东电子工业出版社,2007

[3]《算法设计与分析基础》AnanyLevitin(潘彦译)清华大学出版社2004

 

附录

 

#include

#include

#defineARR_MAX100//定义数组的最大长度

//为方便测试,此函数自动用随机数填充数组

voidfillArray(int*Array,intLength,intRange);

//二分查找法要求查找目标是有序数组,此函数来对数组排序

voidsortArray(int*Array,intLength);

//用二分法查找,改进后的搜索函数可查找出全部符合条件的数

//并输出找到数的数量和这些数在数中的位置

voidsearchNumber(int*Array,intLength,intNum);

voidmain()

{intnArray[ARR_MAX];

intnLength=ARR_MAX;//指定在数组前nLength项进行操作

intnFillRange=50;//规定用来填充数组的随机数的取值范围

intnNum=0,nFind=0;

charcContinue;

fillArray(nArray,nLength,nFillRange);//填充数组

sortArray(nArray,nLength);//数组排序

loopSearchAgain:

cout<

";

cin>>nNum;//取得待对比的整数

searchNumber(nArray,nLength,nNum);//查找

//询问是否继续在数组中查找新的数值

cout<

";

cin>>cContinue;

if((cContinue='C')||(cContinue='c'))

{gotoloopSearchAgain;}}//填充

voidfillArray(int*Array,intLength,intRange)

{for(inti=0;i

{Array=rand()%Range;

//cout<<"Array["<

}}//排序

voidsortArray(int*Array,intLength)

{inti,j,t;

for(i=0;i

{for(j=i;j

{if(Array>Array[j])

{t=Array;

Array=Array[j];

Array[j]=t;}}}

//for(i=0;i

}//查找

voidsearchNumber(int*Array,intLength,intNum)

{intnStart=0,nEnd=Length-1,nMiddle,nFound=0;

while(nStart<=nEnd)

{nMiddle=(nStart+nEnd)/2;

//cout<<"Start="<

if(Array[nMiddle]==Num)

{//如果找到,分别向两侧继续寻找条例条件的数

//由于数组已经过排序,所以如果不止一个符合条件的数存在

//那么这些数应一定是相邻的。

nFound++;

intnMin,nMax;

nMin=nMiddle-1;

nMax=nMiddle+1;//向上查找

while(Array[nMin]==Num)

{nFound++;

nMin--;}//向下查找

while(Array[nMax]==Num)

{nFound++;

nMax++;}//调整指示变量

nMin++;nMax--;//输出查询结果

if(nFound>1)

{

cout<<"找到"<

"<

break;}

else

{cout<<"在Array["<

"<

break;}}

elseif(Array[nMiddle]>Num)//判断方向,折半继续查找

{nEnd=nMiddle-1;}

else

{nStart=nMiddle+1;}}

if(nFound==0)cout<<"数组中没找到符合条件的数。

"<

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 军事

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1