查找算法-索引查找

  • 实际上,很多数据集可能增长非常快。例如空间动态信息等,
  • 对于这样的查找表,我们若是保证记录全部按照当中某个关键字有序,其维护的时间代价非常高,
    所以这种数据通常是按照先后顺序存储。
  • 对于这样的查找表,我们如何快速的查找到所需要的数据?---》通过索引
  • 数据结构的最终目的就是提高数据的处理速度,索引是为了加快查找速度而设计的一种数据结构。索引就是把一个关键字与他对应的记录相关联的过程
  • 一个索引由若干个索引项构成,每个索引项至少应包含关键字和其对应的记录在存储器中的位置等信息。
    索引技术是组织大型数据库以及磁盘文件的一种重要技术。
  • 索引按照结构可以分为线性索引,树形索引和多级索引。
  • 所谓线性索引就是将索引项集合组织为线性结构,也称为索引表。

一:稠密索引

  • 稠密索引文件的每个记录都有一个索引项,记录在数据区存放是任意的,但索引是按序的,这种索引称为稠密索引。
  • 稠密索引是指在线性索引中,将数据集中的每个记录对应一个索引项。


由上图可以知道:对于稠密索引这个索引表而言,索引项一定是按照关键码有序的排列。

稠密索引优点

  1. 索引项有序也就意味着,我们要查找关键字时,可以用折半,插值及斐波那契等有序查找算法。

  2. 比如要查找关键字18的记录,如果直接从右侧的数据表中查找,那只能顺序查找。

  3. 需要查找6次才可以看到结果

  4. 而如果是从侧的索引表中查找,只需两次折半查找就可以得到18对应的指针。对应找到结果。

缺点

1.如果数据集非常大,比如上亿,那也就意味着索引也得同样的数据集长度规模。

  1. 对于内存有限的计算机来说,可能就需要反复去访问磁盘,查找性能大大下降。

  2. 稠密索引文件的索引查找、更新都较方便,但由于索引项多,占用空间较大。

案例实现:年级最多1000个学生,现在进行稠密索引

#define MAXSIZE 1000

typedef struct _Student        //记录
{
    int No;
    char name[32];
    bool sex;
    struct _Student* next;
}Student;    

typedef struct _Index    //索引
{
    int No;
    Student* ind;
}Index;

全部代码

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAXSIZE 1000

typedef struct _Student        //记录
{
    int No;
    char name[32];
    bool sex;
    struct _Student* next;
}Student;    

typedef struct _Index
{
    int No;
    Student* ind;
}Index;

Student* S;
Index I[MAXSIZE];

void AddInfo(int No, char* name, int sex)
{
    //创建数据
    Student *ST;
    int loc,i;
    ST = (Student *)malloc(sizeof(Student));
    strcpy(ST->name, name);
    ST->No = No;
    ST->sex = sex;
    ST->next = S;
    S = ST;
    //插入索引

    if (Insert_Search(I, I[0].No, ST->No, &loc)==-1)
    {
        for (i = I[0].No + 1; i > loc; i--)
        {
            I[i].No = I[i - 1].No;
            I[i].ind = I[i - 1].ind;
        }

        I[i].No = ST->No;
        I[i].ind = ST;

        I[0].No++;    //存储数组大小
    }
}

int Search_Info(int no,Student** stu)
{
    int loc;
    loc = Insert_Search(I, I[0].No, no, &loc);
    if (loc != -1)
        *stu = I[loc].ind;
    else
        return -1;
    return 1;
}

int Insert_Search(Index *ind, int n, int key,int *loc)
{
    int low, high, mid;
    low = 1;
    high = n;
    if (n <= 1)
        mid = n+1;
    if (key<I[low].No)
    {
        *loc = low;
        return -1;
    }
    else if (key > I[high].No)
    {
        *loc = high + 1;
        return -1;
    }
    else
    {
        while (low < high)
        {
            mid = low + (key - ind[low].No) / (ind[high].No - ind[low].No)*(high - low);
            if (ind[mid].No < key)
                low = mid + 1;
            else if (ind[mid].No>key)
                high = mid - 1;
            else
                return mid;
        }
        mid++;
    }
    *loc = mid;

    return -1;
}

int main()
{
    char name[32];
    int no;
    int sex;
    char cmd;
    Student* info;
    I[0].No = 0;

    while (1)
    {
        printf("input CMD:\n");
        scanf("%c", &cmd);
        switch (cmd)
        {
        case 'A':
            printf("input name:");
            scanf("%s", name);
            printf("input student number:");
            scanf("%d", &no);
            printf("input student sex:(1/0)");
            scanf("%d", &sex);
            AddInfo(no, name, sex);
            break;
        case 'S':
            printf("input number to find:");
            scanf("%d", &no);
            if (Search_Info(no, &info) == 1)
                printf("find student:%s %d %d", info->name, info->No, info->sex);
            else
                printf("no find");
            break;
        case 'Q':
            exit(0);
        default:
            break;
        }
        getchar();
    }

}

二:分块索引

  • 稠密索引是因为索引项和数据集的记录个数相同,所以空间代价很大。

如何减少索引项的个数呢?

  • 我们可以对数据集进行分块,使其分块有序,然后再对每一块建立一个索引项(类似于图书馆的分块)。

  • 分块有序是把数据集的记录分成了若干块,并且这些块需要满足两个条件:

(1)块内无序

每一块内的记录不要求有序

(2)块间有序

  • 比如要求第二块所以记录的关键字均要大于第一块中所有记录的关键字,第三块要大于第二块。

  • 只有块间有序才有可能在查找时带来效率。

  • 对于分块有序的数据集,将每块对应一个索引项,这种索引方法叫做分块索引。

结构

分块索引的索引项结构分为三个数据项:

a: 最大关键码--存储每一块中的最大关键字。

b: 存储每一块中记录的个数以便于循环时使用。

c: 用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历。

  • 在分块索引表中查找,可以分为两步:

a: 在分块索引表中查找要查的关键字所在块。

由于分块索引表是块间有序的,因此很容易利用折半插值等算法得到结果。

b:根据块首指针找到相应的块,并在块中顺序查找关键码。

因为块中可以是无序的,因此只能顺序查找。

  • 最大关键码、块长和块首指针很容易理解。

  • 平均查找长度ASL 为 在索引表中的平均查找长度 + 在记录(数据集)中的平均查找长度。

  • 假设 有m块,每块中共有t条记录,显然n = m * t。

  • 故而 ASL = (m+1)/2 + (t+1)/2 = (n/t + t)/2 + 1. 上式的最小值,即n/t = t,n = t^2,则最小的ASL = n^(1/2) + 1;

  • 课件,分块索引的效率比顺序查找o(n) 高了很多,总的来说,分块索引在兼顾了对细分块不需要有序的情况下,大大增加了整体查找的速度,所以普遍被用于数据库查找等技术的应用当中。

代码实现:结合二分查找和顺序查找

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 100

typedef struct _blockIndex
{
    int key;
    int len;
    int *first;
}blockInfo,BlockIndex[MAXSIZE];

int Binary_Search(BlockIndex B, int key, int n,int *loc)
{
    int low, high, mid;
    low = 0;
    high = n-1;
    while (low<=high)
    {
        mid = (high + low) / 2;
        if (B[mid].key == key)
            return mid;
        else if (B[mid].key < key)
            low = mid + 1;
        else
            high = mid - 1;
    }
    if (B[mid].key < key&&key < B[mid + 1].key)
        *loc = mid + 1;
    else
        *loc = mid;
    return -1;
}

int main()
{
    int a[9] = { 18, 5, 27, 13, 57, 36, 96, 62, 77 };
    BlockIndex BI;
    int n,loc,ret;
    //初始化分块索引
    BI[0].key = 27;
    BI[0].len = 4;
    BI[0].first = a;

    BI[1].key = 57;
    BI[1].len = 2;
    BI[1].first = a+4;

    BI[2].key = 96;
    BI[2].len = 3;
    BI[2].first = a + 6;

    printf("input number to search:\n");
    scanf("%d", &n);
    ret = Binary_Search(BI, n, 3, &loc);
    if (ret != -1)
        loc = ret;
    for (int i = BI[loc].first - a; i < BI[loc].first - a + BI[loc].len; i++)
        if (a[i] == n)
        {
            printf("find index:%d", i);
            break;
        }

    system("pause");
    return 0;
}

三:倒排索引

倒排索引
倒排索引

案例

忽略大小写和复数,得出单词表,找到他们分别出现在哪篇文章中

我们输入某个单词,就能够索引处对应的文章,而且由于单词有序,索引很快,根据单词直接找到文章,所以整体速度很快。但是当文章数量一大,就难以实现

其中记录号表存储具有相同次关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字)。这种索引方法就是倒排索引
查找快,维护困难

  • 引子:搜索引擎如何进行搜索,能够在非常快的速度之下显示搜索的最优匹配内容。

  • 索引项的结构是次关键字码和记录号表,其中记录号表存储具有相同关键字的所有记录的记录号(可以是指向记录的指针或者是该记录的主关键字),这样的索引方法就是倒排索引。

  • 倒排索引源于实际应用中需要根据属性(或字段、次关键码)的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性来确定记录的位置,因而称为倒排索引。

  • 倒排索引的优点显然是查找记录非常快,基本等于生成索引表后,查找时都不用去读取记录,即可以得到结果。但是它的缺点是这个记录号不定长,可多可少。

typedef struct _wordsRec
{
    char word[32];
    int record[10];
}wordsRec, WordsRec[MAXSIZE];

void createTable(WordsRec WR)
{
    strcpy(WR[0].word, "a");
    WR[0].record[0] = 2;

    strcpy(WR[1].word, "and");
    WR[1].record[0] = 1;

    strcpy(WR[2].word, "be");
    WR[2].record[0] = 1;

    strcpy(WR[3].word, "book");
    WR[3].record[0] = 1;
    WR[3].record[1] = 2;

    strcpy(WR[4].word, "but");
    WR[4].record[0] = 1;

    strcpy(WR[5].word, "few");
    WR[5].record[0] = 1;

    strcpy(WR[6].word, "friend");
    WR[6].record[0] = 1;
    WR[6].record[1] = 2;

    strcpy(WR[7].word, "good");
    WR[7].record[0] = 1;
    WR[7].record[1] = 2;

    strcpy(WR[8].word, "is");
    WR[8].record[0] = 2;

    strcpy(WR[9].word, "should");
    WR[9].record[0] = 1;
}

int Binary_Search(WordsRec WR, int n, char *str)
{
    int low, high,mid;
    low = 0; 
    high = n-1;
    while (low<=high)
    {
        mid = (low + high) / 2;
        if (strcmp(WR[mid].word, str) > 0)
            high = mid - 1;
        else if (strcmp(WR[mid].word, str) < 0)
            low = low + 1;
        else
            return mid;
    }
    return -1;
}

int main()
{
    int i, index;
    char cmd;
    char arti[2][50] = {
        "Books and friends should be few bu good",
        "A good book is a good friend"
    };
    char words[32];

    WordsRec WR;
    for (i = 0; i < MAXSIZE;i++)
        memset(WR[i].record, 0, 10 * sizeof(int));

    createTable(WR);

    while (1)
    {
        printf("input cmd to operate:\n");
        scanf("%c",&cmd);
        if (cmd=='Q')
            break;
        printf("input words to index:\n");
        scanf("%s", words);
        index = Binary_Search(WR, 10, words);
        if (index == -1)
            printf("no found\n");
        else
        {
            for (i = 0; WR[index].record[i]!=0;i++)
                printf("find %d:%s\n", i + 1, arti[WR[index].record[i]-1]);
        }
        getchar();
    }

    system("pause");
    return 0;
}

原文:山上有风景

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容

  • 一、相关定义 查找——查找就是根据给定的某个值,在查找表中确定一个其关键字等于给定值的数据元素(或记录)。所有这些...
    开心糖果的夏天阅读 1,041评论 0 8
  • 目录一.查找概论1.概念:2.查找表按照操作方式来分有两大种:静态查找表和动态查找表3.面向查找操作的数据结构称为...
    Movle阅读 354评论 0 1
  • 查找 查找表是由同一类型的数据元素(或记录)构成的集合。关键字是数据元素中某个数据项的值,也称为键值,用它可以标识...
    keeeeeenon阅读 1,903评论 0 3
  • 文章共分为三篇 第一篇:数据结构 -《大话数据结构》读书笔记(1) 一、数据结构绪论二、算法三、线性表 第二篇:数...
    Q以梦为马阅读 1,820评论 3 13
  • 凌晨四点过,静谧得听得见心跳和血液流过的声音。 一觉醒来就失了眠。 这样的夜,总是爱东想西想。寒凉,安静,那些不愉...
    若莲若漪阅读 157评论 1 4