67-线性表的查找

1. 线性表的查找

  线性表的查找主要有:顺序查找,二分查找,分块查找。

  通过前面的学习可知,线性表有顺序和链式两种存储结构,而这里我们主要是介绍的线性表查找算法是基于顺序存储结构的,其顺序表的存储类型定义:

#define MAXL 100
typedef int KeyType;
typedef char InfoType[10];
typedef struct
{
    KeyType key;        //KeyType为关键字的数据类型
    InfoType data;      //其他数据项
} NodeType;
typedef NodeType SeqList[MAXL];     //查找顺序表类型

2. 顺序查找

例如,查找表:3 9 1 5 8 10 6 7 2 4,要查找的值:k=10

这里写图片描述
图1-顺序查找

顺序查找思路如图1所示:
  从表的一端开始,顺序扫描线性表,依次将扫描到的关键字和给定值k相比较。若当前扫描到的关键字与k相等,则查找成功;若扫描结束后,仍未找到关键字等于k的记录,则查找失败。



顺序查找:

/*
顺序查找:
    参数R表示要查找的顺序表
    n表示元素个数
    k表示要查找的元素
*/
int SeqSearch(SeqList R , int n , KeyType key)
{
    int i;
    for(i = 0; i < n; i++)
    {
        if(R[i].key == key)
        {
            //找到了返回逻辑序号i+1
            return i+1;
        }
    }
    return -1;
}

3. 折半查找

折半查找也称为二分查找,要求线性表中的节点,按关键字值的递增或递减顺序排列。

这里写图片描述
图2-折半查找

折半查找的思路:
  1. 首先用要查找的关键字k与中间位置的节点的关键字比较,若比较结果相等则查找完成。若不相等,再根据k与该中间节点关键字的比较大小确定下一步查找哪个子表。

  2 . 递归进行下去,直到找到满足条件的节点或者该线性表中没有这样的节点。



折半查找算法实现:

/*
参数R表示要查找的顺序表
参数n表示元素个数
参数k表示要查找的元素
*/
int BinSearch(SeqList R,int n,KeyType k)
{
    int low=0,high=n-1,mid;
    //当前区间存在元素时循环
    while (low<=high)
    {
        mid=(low+high)/2;
        //查找成功返回其逻辑序号mid + 1
        if (R[mid].key==k)
        {
            return mid+1;
        }

        //否则继续在[high ... mid - 1]中查找
        if (R[mid].key>k)
        {
            high=mid-1;
        }
        //继续在[low ... mid - 1]中查找
        else
        {
            low=mid+1;
        }

    }
    //查找失败则返回0
    return 0;
}



二分查找算法递归版实现:

/*
二分查找的递归版算法

low表示开始下标
high表示结尾下标
*/
int BinSearch1(SeqList R,int low,int high,KeyType k)
{
    int mid;
    if (low<=high)
    {
        mid=(low+high)/2;
        if (R[mid].key==k)
        {
            return mid+1;
        }

        if (R[mid].key>k)
        {
            BinSearch1(R,low,mid-1,k);
        }
        else
        {
            BinSearch1(R,mid+1,high,k);
        }
    }
    return 0 ;
}

二分查找的特点:利用了顺序查找的有序性,使得查找过程得到提高。


4. 索引存储结构

这里写图片描述
图3-数据表

  上图中有一个城市的数据表,其中区号是作为数据表中的关键字,数据表中的区号是无序的,当我们要从这张数据表中去查找一个城市的数据,应该怎么去查找呢?

  根据我们前面学过的查找算法可知,有两种:顺序查找和折半查找。

  对于顺序查找来说,当数据表中的数据量非常大的时候,那么顺序查找需要一个一个的去查找数据,直到查找到数据为止,显然顺序查找的效率是比较低。对于折半查找来说,也存在同样的问题,因为折半查找的数据前提是有序的,而当数据表中的数据量非常大的时候,对数据进行排序非常耗时,因此折半查找的效率也是非常低的。

这里写图片描述
图4-索引存储结构

  当数据量非常大的时候,对于这种情况,在实际的应用中更多的是采用索引存储结构来实现,而索引存储结构一般是由索引表和数据表组成的。

  索引表中的每一项都成为索引项,索引项中又分为两部分:关键字和地址,关键字用于唯一标识一个节点, 比如:索引表中的025关键字就表示了在数据表中的一个城市的相关数据(区号,城市名,说明)。

  地址作为指向该关键字对应节点的指针,也可以是相应的地址。比如在索引表中,我们可以根据025关键字所在的索引项中找到地位为210的,然后通过210地址直接在数据表中找到相关城市数据。

  我们可以知道索引存储结构,这种解决方案是通过索引表来查找数据的,也就是说,当我们在索引表中查找到数据的索引项时,直接通过索引项中存储的地址,就可以从数据表中访问到对应的城市数据。特别是针对数据量非常大的时候,避免了直接查找数据表,从而提高查找效率。


5. 分块查找

下面通过一个例子来说明分块查找

假设有一个线性表,其中包含23个记录:

  关键字序列:8 , 14 , 6 , 9 , 10 , 22 , 34 , 18 , 19 , 31 , 40 , 38 , 54 , 66 , 46 , 71 , 78 , 68 , 80 , 85 , 99 , 94 , 88

  我们要做的就是将23个记录分为5块,每块中有5个记录,在进行分块时,需要满足以下几个条件:
  1. 将数据表R中23个记录均分为b块,前b-1块中,每块的记录个数为5,但是最后一块即第b块的记录数小于等于5;

  2 . 每一块中的关键字不一定有序,但前一块中的最大关键字必须小于后一块中的最小关键字,即要求表是“分块有序”的。

  3 . 抽取各块中的最大关键字,构建索引表Idx, 在索引表Idx中的link存放着第i块在表R中的起始位置,而索引表Idx中的key存放着每一块中的最大关键字,由于表R是分块有序的,所以索引表是一个递增有序表。



最后构建成的分块索引表如下所示:

这里写图片描述
图5-索引存储结构

当我们在这样的分块索引表中查找就可以这样做:

  1 . 先在索引表中查找关键字所在的块,由于索引表中的块是有序的,因此可以利用折半查询,也可以利用顺序查询,比如查找关键字为80的,即 66< 80 < 85,在第4个分块中。

  2 . 于是再根据分块中的link找到分块中的关键字起始位置,由于分块内的关键字是无序的,因此只能通过顺序查找。比如找到第4块的关键字起始位置(71),通过顺序查找逐个比较,最终在第四次查找中找到了关键字为80的。



  由此可知,在索引表中顺序查找的话,需要查找4次,在对应的块中查找,需要查找4次,加起来总共需要8次比较。但如果我们直接在数据表R中采用顺序查找的话,总共需要19次才能找到。也就是说,分块查找的性能是介于顺序查找和折半查找之间的



存储结构:

#define MAXL    100 //数据表的最大长度
#define MAXI    20 //索引表的最大长度
typedef int KeyType;
typedef char    InfoType[10];

typedef struct
{
    KeyType key;        //关键字
    InfoType    data;   //其他数据
}   NodeType;

typedef NodeType    SeqList[MAXL]; //数据表类型
SeqList R;

typedef struct
{
    KeyType key;        //KeyType为关键字的类型
    int link;           //对应块的起始下标
}   IdxType;

typedef IdxType IDX[MAXI]; //索引表类型
IDX idx;



分块查找算法实现:

//m表示数组I(索引表)中的元素个数
//n表示数组R(数据表)中的元素个数
//k表示要查找的关键字
int IdxSearch(IDX   I , int m , SeqList R , int n , KeyType k)
{
    int low=0 , high = m-1 , mid , i;
    //b表示每个分块中的个数
    int b = n / m;  
    //在索引表中“折半”查找,主要是查找分块所在的位置
    while (low <= high) 
    {
        mid = (low+high)/2;
        if (I[mid].key >= k)
            high=mid-1;
        else
            low = mid+1;
    }

    //在数据表中顺序查找
    i = I[high+1].link;
    while (i <= I[high+1].link+b-1 && R[i].key != k)
        i++;
     //如果找到了则返回关键字的逻辑下标+1
    if (i <= I[high+1].link+b-1)
        return i+1;
    //否则没找到就返回0
    else
        return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_35733751/article/details/81365309