查找算法总结

只要打开电脑,就会涉及查找技术。如硬盘中文件搜索,浏览器中的搜索“关键字key”等,无论是数据库,还是普通的ERP系统,查找功能数据处理的一个基本,同时也是一个非常重要的功能。所有这些需要被查找数据的集合,统称为“查找表”。数据查找并不复杂,但是如何实现数据又快又好地查找呢?前人在实践中积累的一些方法,值得我们好好学些一下。我们假定查找的数据唯一存在,数组中没有重复的数据存在。
我们如何在查找表里面找到我们想要的那个数据。此时数据本身没有特征,所以我们需要的那个数据可能出现在数组的各个位置,可能在数据的开头位置,也可能在数据的结束位置。这种性质要求我们必须对数据进行遍历之后才能获取到对应的数据。

(1) 普通的顺序查找(无序查找)

顺序查找(Sequential Search)又叫线型查找,是基本的查找技术。其基本过程是:从查找表中的第一个(或者最后一个)记录开始,逐个进行给定值与关键字的比较,若相等则查找成功;如果直到最后一个(或者第一个)记录,关键字和给定值不等,则查找不成功.

[cpp] view plain copy
int find(int array[], int  length, int value)  
{  
    if(NULL == array || 0 == length)  
        return -1;  

    for(int index = 0; index < length; index++){  
        if(value == array[index])  
            return index;  
        }  
    return -1;  
} 

由于每次循环时都要对index是否越界,即是否小于等于length做判断,因此我们可以通过设置哨兵进行改进。

int find(int array[], int  length, int value)  
{  
    if(NULL == array || 0 == length)  
        return -1;  
  int i = length;
  array[0] = value;    //注意此时数组存储在array[1]到array[length]中
  while(array[i]!= key)//循环从尾部开始
     i--;
     return i;         //返回0则说明查找失败
} 

此时代码从尾部开始查找,由于array[0] = value,也就是说如果在array[i]有key则返回i值,查找成功,否则一定在最终的array[0]处等于value,此时返回的是0,即说明array[1]-array[length]中没有关键字value,查找失败。

分析:对于顺序查找而言,最好的情况是要查找的值在第一个位置就找到了,算法时间复杂度为O(1),最坏的情况是遍历所有元素,需要n次,时间复杂度为O(n),由于关键字出现在任何一个位置上的概率是相等的,因此平均查找次数为(n+1)/2,所以时间复杂度是O(n)。当n很大时,查找效率极为低下,由于这个算法简单,对静态查找表的记录没有任何要求,在一些小型数据的查找时,可以使用。

(2)有序表查找

如果数据排列地非常整齐,那结果会是什么样的呢?就像在生活中,如果平时 不注意收拾整齐,那么找东西的时候非常麻烦,效率很低;但是一旦东西放的位置固定下来,所有东西都归类放好,那么结果就不一样了,我们就会形成思维定势,这样查找东西的效率就会非常高。下面我们介绍几种方法:

二分法查找:
int binary_searcht(int array[], int length, int value)  
{  
    if(NULL == array || 0 == length)  
        return -1;  
    int start = 0;  
    int end = length -1;  
    while(start <= end){      
        int middle = start + ((end - start) >> 1);  
        if(value == array[middle])  
            return middle;  
        else if(value > array[middle]){  
            start = middle + 1;  
        }else{  
            end = middle -1;  
        }  
    }  
    return -1;  
}

分析::查找最好情况是1次,最坏情况是需要log(n+1)/log(2),即时间复杂度为O(logn),理论证明见《算法导论》.

  1. 插值查找
    其思想与折半查找较为相似,根据要查找的关键字value与查找表中最大最小记录的关键字比较后的查找方法,由于是在有序表中查找,例如是在升序的表中查找,对于一个较小的值我们自然想到在查找表前面部分进行查找,因此我们将上述middle语句更改为:
    middle = start+(end - start)*(value-array[start])/(array[end] - array[start]);即可,这里不再赘述。
  2. 斐波那契额查找
    相对于折半查找,一般将待比较的value值与第mid=(low+high)/2位置的元素比较,比较结果分三种情况
    1)相等,mid位置的元素即为所求
    2)> ,low=mid+1;
    3) < ,high=mid-1;
    斐波那契查找与折半查找很相似,他是根据斐波那契序列的特点对有序表进行分割的。他要求开始表中记录的个数为某个斐波那契数小1,即
    n=F(m)-1;开始将k值与第F(k-1)位置的记录进行比较(mid=low+F(k-1)-1),比较结果也分为三种:
    1)相等,mid位置的元素即为所求
    2)> ,low=mid+1,k-=2;
    (说明:low=mid+1说明待查找的元素在[mid+1,hign]范围内,k-=2 说明范围[mid+1,high]内的元素个数为n-(F(k-1)) = Fk-1-F(k-1) =Fk-F(k-1)-1=F(k-2)-1个,所以可以递归的应用斐波那契查找)
    3)< ,high=mid-1,k-=1;
    说明:low=mid+1说明待查找的元素在[low,mid-1]范围内,k-=1 说明范围[low,mid-1]内的元素个数为F(k-1)-1 个,所以可以递归 的应用斐波那契查找
    斐波那契查找的核心是:
    1)当key=a[mid]时,查找成功;
    2)当key
// 斐波那契查找.cpp   

#include "stdafx.h"  
#include <memory>  
#include  <iostream>  
using namespace std;  

const int max_size=20;//斐波那契数组的长度  
/*构造一个斐波那契数组*/   
void Fibonacci(int * F)  
{  
    F[0]=0;  
    F[1]=1;  
    for(int i=2;i<max_size;++i)  
        F[i]=F[i-1]+F[i-2];  
}  

/*定义斐波那契查找法*/    
int Fibonacci_Search(int *a, int n, int key)  //a为要查找的数组,n为要查找的数组长度,key为要查找的关键字  
{  
  int low=0;  
  int high=n-1;  

  int F[max_size];  
  Fibonacci(F);//构造一个斐波那契数组F   

  int k=0;  
  while(n>F[k]-1)//计算n位于斐波那契数列的位置  
      ++k;  

  int  * temp;//将数组a扩展到F[k]-1的长度  
  temp=new int [F[k]-1];  
  memcpy(temp,a,n*sizeof(int));  

  for(int i=n;i<F[k]-1;++i)  
     temp[i]=a[n-1];  

  while(low<=high)  
  {  
    int mid=low+F[k-1]-1;  
    if(key<temp[mid])  
    {  
      high=mid-1;  
      k-=1;  
    }  
    else if(key>temp[mid])  
    {  
     low=mid+1;  
     k-=2;  
    }  
    else  
    {  
       if(mid<n)  
           return mid; //若相等则说明mid即为查找到的位置  
       else  
           return n-1; //若mid>=n则说明是扩展的数值,返回n-1  
    }  
  }    
  delete [] temp;  
  return -1;  
}  

int _tmain(int argc, _TCHAR* argv[])  
{  
    int a[] = {0,16,24,35,47,59,62,73,88,99};  
    int key=100;  
    int index=Fibonacci_Search(a,sizeof(a)/sizeof(int),key);  
    cout<<key<<" is located at:"<<index;  
    system("PAUSE");  
    return 0;  
} 

分析:关于斐波那契查找, 如果要查找的记录在右侧,则左侧的数据都不用再判断了,不断反复进行下去,对处于当众的大部分数据,其工作效率要高一些。所以尽管斐波那契查找的时间复杂度也为O(logn),但就平均性能来说,斐波那契查找要优于折半查找。可惜如果是最坏的情况,比如这里key=1,那么始终都处于左侧在查找,则查找效率低于折半查找。
还有关键一点,折半查找是进行加法与除法运算的(mid=(low+high)/2),插值查找则进行更复杂的四则运算(mid = low + (high - low) * ((key - a[low]) / (a[high] - a[low]))),而斐波那契查找只进行最简单的加减法运算(mid = low + F[k-1] - 1),在海量数据的查找过程中,这种细微的差别可能会影响最终的效率。

(3)二叉搜索树

二叉搜索树或者是一棵空树或者有下列性质:
1.若左子树不为空,则左子树上的所有结点的值均小于根节点的值
2.若右子树不为空,则右子树上的所有结点的值均大于根节点的值
3.它的左右子树也分别为二叉搜索树

二叉树定义:
typedef struct _NODE  
{  
    int data;  
    struct _NODE* left;  
    struct _NODE* right;  
}NODE;  

查找过程如下:
const NODE* find_data(const NODE* pNode, int data){  
    if(NULL == pNode)  
        return NULL;  

    if(data == pNode->data)  
        return pNode;  
    else if(data < pNode->data)  
        return find_data(pNode->left, data);  
    else  
        return find_data(pNode->right, data);          
} 

分析:若果二叉树是比较平衡的,即其深度与完全二叉树相同,那么查找的时间复杂度为O(logn),近似于折半查找,最坏的情况下是斜树,查找时间复杂度是O(n),对于二叉搜索树的其它诸如插入(insert),删除(delete)操作,在其它博客中有,可自行查找。
另外还有AVL树,多路查找树(B树),以及散列表查找(哈希表)另有单独博客讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值