折半查找

来源:互联网 发布:红蜘蛛软件年费 编辑:程序博客网 时间:2024/06/16 04:26

折半查找

1.查找的定义

  根据某个给定的关键字K,从集合的R中找出关键字与K相同的记录`

2.查找方法的性能指标

  • -查找运算时间主要花费在关键字比较上。通常把查找过程中执行的关键字平均比较次数(即平均查找长度),作为衡量一个查找算法效率的标准。(当然我们不仅关心查找算法在平均情况下的查找效率,也关心在最坏情况下的效率。)
    -平均查找长度ASL 定义为:
    这里写图片描述
 n :查找表中记录的个数。 Pi:查找第i个记录的概率.(一般认为,每条记录被查找的概率相等,即 Pi=1/n)                      ci:找到第i个记录需要的比较次数。

3.算法基本思路

  • 思考:在无序线性表的查找算法中,我们不得不遍历线性表。然而当我们已知记录为有序情况下,我们还需运用遍历算法吗?

  • 算法基本思路为:在查找时,我们将被查找的键与数组的中间键进行比较,若中间键即为被查找的键,则结束算法,否则,得益于有序这一特点,我们可以将问题向中间键的两侧中的一侧递归深入
    这里写图片描述

4.基本实现

<1>迭代版

int Binary( int A[] , int K ,int N){    int left, mid , right;    int NotFind = -1;    left  = 0;    right = N;    while( left <= right )    {      mid  = ( left + right ) / 2;      if( A[ mid ] > K )          right = mid - 1;      else if( A[ mid ] < K)          left =  mid + 1;      else           return mid;    }    return NotFind ;}

<2>递归版

int Binary( int A[] , int K  , int left , int right ){    int  mid ;    int NotFind = -1;    if( right < left )  return NotFind ;    mid  = ( left + right ) / 2;    if( A[ mid ] > K )      return  Binary( A, K , left, mid - 1  );    else if( A[ mid ] < K)      return Binary( A, K , mid + 1, right   );    else       return mid;}

5.算法分析

   递推式 :T(n)= T(n /2 ) + O(1) —— O(log n)

6.发现问题

  • 考察对下例实例
    1   2   3 
  • 下面对所有关键字,进行查找:
    1.查找 关键字 2 ,算法经过两次比较,成功返回。
    2.查找关键字 3 ,算法与中间关键字进行一次比较,转向右侧子数组。
    3.查找关键字 1 ,算法与中间关键字进行两次比较,转向右侧子数组。

  • 我们不难发现一个问题,算法的实现过程与我们所想存在一定的差距,难道不是吗?在上述查找过程中,查找关键字 1 和 3 ,算法经过与中间关键字次数不同的比较,才分别转向了左右字数组。幸运的是转向左右字数组均是经过常数比较次数,而且算法的迭代次数或递归深度相同。上面算法分析中递推式运用的是 O(1),因此算法的渐进复杂度并没有受到影响,而是算法的常系数受到影响。

7.改进

  • 1.Fibonacci查找:大体思路是不再以划分出长度相同的两个子数组为标准,而是改变“中间关键字”的选取位置,构建出表面上的不平衡(迭代次数或递归深度的不均衡),却因为转向成本的不均衡进行补偿,反而会使得平均查找长度的缩短

  • 2.更改原算法实现方式:-转向左右字数组的代价不平衡的问题,也可直接解决。算法同样每次与中点的关键字进行比较,每比较一次,问题规模也就缩短一半。每次迭代或递归实例仅做一次关键码的比较,便转向左右区间,而并不是前面像一样还需与中点关键字做一次比较,如此所有分支只有2个方向,而不是以前的3个分支。当然这种改进使得我们不得不做出一些牺牲,我们不能及时判断当前是否在中点出命中,而是只有当元素right - left = 1 时,才判断该元素是否命中。
    这里写图片描述

<1>.实现:

int Binary( int A[] , int K ,int N)
{
int left, mid , right;
int NotFind = -1;

left  = 0;right = N;while( 1  <  right - left )   {  mid  = ( left + right ) / 2;  if( A[ mid ] > K )      right = mid ;  else       left =  mid ;}return (A[ left ] == K )? left :NotFind ;

}

<2>.分析:相对于先前版本,最好情况下我们要切分到最后,因此情况更坏,最坏情况下各种情况下,成本更加接近,转向成本变得更加均衡,因此情况更好,所以整体性能更稳定。