数据结构--11查找算法

本文详细介绍了算法的基本概念、评判标准,重点讲解了顺序查找、二分查找、分块查找以及不同类型的哈希函数,包括直接定址法、保留函数法、叠加法、随机函数法和平方取中法。此外,还涉及了开放地址法和链地址法处理哈希冲突的方法。
摘要由CSDN通过智能技术生成

一、算法

1.什么是算法

算法是一个有穷规则(或语句、指令)的有序集合。他确定了解决问题的一个运算序列。对于问题的初始输入,通过算法的有限步的运行,产生一个或多个的输出

2.如何评判算法的好坏

一个好的算法通常:
(1)算法对应的程序所耗时间少
(2)算法对应的程序所耗存储空间少
(3)算法的结构性好、易读、易移植和调试等

3.算法的特性

在这里插入图片描述

4.时间复杂度(T(n) )

在这里插入图片描述

二、查找算法

1.顺序查找

顺序遍历,随着n的变大,其效率会逐渐下降

2.二分查找

前提条件:首先要保证查找的数据     按大小排列    顺序存放      
思路:对给定值key,逐步去确定待查记录所在区间,每次将搜索空间减少一半,直到查找成功或失败为止。
设两个指针(或下标)low、high,分别指向当前待查找表的上界(表头)和下界(表尾)                              
mid=(low+high)/2
mid的值>key  :high=mid-1
mid的值<key  :low=mid+1
到最后low=high,就没有找到

在这里插入图片描述
在这里插入图片描述
代码实现:

int bin_search(int *arr,int len,int key)
{
    int low,mid,high;
    low=1;
    high=len-1;
    while(low<=high)
    {
        mid=(low+high)/2;
        if(key==arr[mid])    return mid;
        else if(key>arr[mid])
            low++;
        else
            high--;
    }   
    return -1; 

}

3.分块查找

索引表+源数据表
前提条件:块间有序,块内无序    (第二块的所有值一定得大于第一块得最大值)
思路:
(1)先在索引表中确定在那一块
(2)在遍历这一块进行查找

在这里插入图片描述
算法思路:
(1)又索引表确定待查找数据在那个块
(2)在块内顺序查找

在这里插入图片描述
在这里插入图片描述

程序实现:

具体实现思路:
1.将数据分块存入一个结构体数组中,结构体内容为块的起始位置和块最大值
2.将val在结构体数组里的最大值遍历一遍,找到其对应块
3.确定了相应块,同时就确定了初始位置,末尾位置,便于后期遍历可直接为后一个块的初始位置,即end=末尾位置+1
注意:要判断是否为最后一个块,是最后一个块的话,直接把最后的下标赋值给end
4.遍历start----end,找出val
typedef struct{        //索引表
    int post;    //块的起始位置
    int max;     //用来保存块的最大值
}indext_t;    //索引

                    //a原数据表 index_list 索引表 value 被查找的值
int findByBlock(int *a, index_t *index_list,int value)
{
                //start和end作为源数据表的下标取搜索
        int start;    //保存起始下标
        int end;        //终止下标的后一个位置
        
     
     //先确定value在哪一块中,遍历索引表中的max
        int i;
             
     for(i = 0; i < 4; i++)
        {
         if(value <= index_list[i].max)//说明value有可能在i块中
         {
                 
            start = index_list[i].post;    //确定start
   
             if(i == 3)        //是否在最后一块//假设value在最后一块中,i+1数组越界,所以end的赋值,需要进行条件判断
             
                end = 19;                    
             else 
             
                end = index_list[i+1].post;    //确定end
             
            break;    //注意此处一定要有break
        }
}


        for(i = start; i < end; i++) //start-end的遍历
        {
            if(a[i] == value)
            return i;
        }
        return -1;
    }
  
      
int main(int argc, const char *argv[])
{
    int i;
    
    int a[19] = {18, 10, 9, 8, 16, 20, 38, 42, 19, 50, 84, 72, 56, 55, 76, 100, 90, 88, 108};
    //         0              4  5                9  10                  15
    
    index_t index_list[4] = {{18,0},{50,5},{84,10},{108,15}};    //索引表,结构体数组
    for(i = 0; i < 19; i++)            //把源数据表中的每一个数据查询一遍,测试程序
    {
        printf("%d post is %d\n",a[i],findByBlock(a,index_list,a[i]));
    }    
    
    printf("%d post is %d\n",22,findByBlock(a,index_list,22));
    
return 0;
}

4.哈希表(hash函数)

散列存储
有一张表,保存了数据中关键字与对应存储位置的关系
在选择key的时候,要选择数据中不重复的关键字作为key
存时按照对应关系存,取时按照对应关系取
这里说的对应关系就是哈希函数

在这里插入图片描述

哈希函数的选取

1.直接定址法

在这里插入图片描述
要查找99岁老人的人数时,直接读出第99项即可。但该方法比较特殊,关键字很少是连续的,用该方法产生的哈希表会造成空间的大量浪费,适应性不强
程序实现:

int hashFun(int key)    //hasn函数,代表了数据中的关键字与存储位置之间的关系
{
    int post = key-1;    //key-1代表关系 post通过关系得到存储位置
    return post;
}

int saveAgeNum(int *hash_list,int key,int num)        //存数据(按照对应关系存)
{
    int post=hashFun(key);    //1.通过key得到数据存储的位置,调用哈希函数 
    hasn_list[post]=num;      //2.将数据存储到哈希表中
}

int getAgeNum(int *hash_list,int key)        //取数据(按照对应关系取)
{
    int post = hashFun(key);    //1.通过key得到数据存储的位置,调用哈希函数 
    return hash_list[post];     //2.将数据取出 
}

int main(int argc, const char *argv[])
{
    int i;
    int age,num;        //年龄和对应年龄的人口数
    int hash_list[200] = { 0 };//哈希表,用来保存年龄和对应年龄的人口数,之所以长度为200,暂定人的寿命为200岁
    for(i = 0; i < 4; i++)
        {
             printf("请您输入年龄和年龄对应的人口数:\n");//输入四组数据保存到哈希表中
             scanf("%d %d",&age,&num);        //将输入的数据保存到哈希表中        
             saveAgeNum(hash_list,age,num);
          }
        //进行查找对应年龄的人口数
     for(i = 0; i < 6; i++)
     {
         printf("请您输入要查询的年龄:\n");
         scanf("%d",&age);
         printf("%d: %d人\n",age,getAgeNum(hash_list,age));
      }        
    

    return 0;
}
2.保留函数法

在这里插入图片描述


//   数据个数为n 
 n=11
//    哈希表的长度     m = n/a 
//    n存储数据的个数 a的为装填因子,0.7-0.8之间最为合理
//    m = 11 / 0.75 == 15  0-15之间最大的指数为13
//prime 质数 为 不大于表长的质数
int hashFun(int key)
    {
     int post = key % prime;     //key % 13
     return post;
    }


在这里插入图片描述

3.叠加法

在这里插入图片描述
程序实现:

//图书馆书码

typedef struct{
    int num;        //图书条形码
    char name[30];  //图书的名字
}book_info_t;

int hashFun(int key)        //hash函数
{
        int post = (key / 1000000 + key % 1000000 / 1000 + key % 1000) % 1000;
        return post;
}

int saveBookInfo(book_info_t *hash_list , book_info_t book)        //存数据
{
    int post=hashFun(book.number);    //1.通过key得到数据存储的位置,调用哈希函数 
    hasn_list[post]=book;      //2.将数据存储到哈希表中
}

book_info_t getBookInfo(book_info_t *hash_list,int key)        //取数据
{
    int post = hashFun(key);    //1.通过key得到数据存储的位置,调用哈希函数 
    return hash_list[post];     //2.将数据取出 
}

int main(int argc, const char *argv[])
{
    int i;
    int num;                                //用来保存输入的条形码
    book_info_t hash_list[1000] = { 0 };    //结构体数组,因为叠加法后存储位置三位数
    book_info_t book;                       //存入的一个结构体变量
    
    for(i = 0; i < 4; i++)            //1.循环输入四组图书信息
    {
        printf("请您输入图书条形码和图书的名字:\n");
        scanf("%d %s",&book.number,book.name);
        saveBookInfo(hash_list,book);    //将数据存储到哈希表中
    }
    
    for(i = 0; i < 6; i++)    //2.取数据 ,查询图书信息
    {
        printf("请您输入要查询的条形码:\n");
        scanf("%d",&num);
        book = getBookInfo(hash_list,num);
        printf("%d---%s\n",book.number,book.name);
    }
    return 0;
}
4.随机函数法

在这里插入图片描述

5.平方取中法

在这里插入图片描述
程序实现:

//    key    key的平方   H(key)
//    0100   00 100 00    100  
//    0110   00 121 00    121
//    1010   10 201 00    201
//    1001   10 020 01    020
//    0111   00 123 21    123
//对key平方后,发现中间的三位重复次数最少

int hashFun(int key)    //哈希函数
{
    int post = key*key % 100000 / 100;        //公式可随实际值变化,目的是求中间的一串
    return post;
}

void saveNum(int *hash_list, int key)
{
    int post = hashFun(key);
    hash_list[post] = key;
}
int getNum(int *hash_list,int key)
{
    int post = hashFun(key);
    return hash_list[post];
}

int main(int argc, const char *argv[])
{
    int i;
    int a[] = {100,110,1010,1001,111}; //key
    int hash_list[1000] = { 0 };//哈希表
    for(i = 0; i < sizeof(a)/sizeof(a[0]); i++)
        saveNum(hash_list,a[i]);
    for(i = 0; i < sizeof(a)/sizeof(a[0]); i++)
        printf("post:%3d --- %d\n",hashFun(a[i]),getNum(hash_list,a[i]));
    return 0;
}

注意:不管怎么选取函数,都要尽可能的减少冲突
冲突没法避免要考虑如何解决冲突

解决冲突

 冲突是指:表中某地址j∈[0,m-1]中己存放有记录,而另一个记录的H(key)值也为j(两个记录相同冲突了)
 处理冲突的方法一般为:在地址j的前面或后面找一个空闲单元存放冲突的记录,或将相冲突的诸记录拉成链表等等。
1.开放地址法(必散列)

在这里插入图片描述
思想就是在H(key)的前后找一个空位置存放数据,找不到就一直找,直到找到为止
在这里插入图片描述
程序实现:

int hashFun(int key)    //哈希函数 
{
    int post = key % 13;    //取余13的原因是因为 选不大于哈希表长的最大质数
    return post;
}
int hashSearch(int *hash_list,int key)    //哈希查找(确定post的值,找空位)
{
    int d = 1;    //d 取值 1 2 3 4 5 当冲突发生的时候采用一次线性探查法
    int post;    //用来保存存储位置
    int remnum;    //用来保存余数
    post = remnum = hashFun(key);
    while(d < 15 && hash_list[post] != 0 && hash_list[post] != key)
    {        
          post = (remnum + d) % 15;    //采用一次线性探查法
          d++;
    }
    if(d >= 15)
        return -1;//代表表已经溢出
  return post;
    //hash_list[post] == 0//意味着当前post这个位置可以存放数据,初始化哈希表所有位置全为0,代表没有存放数据
    //hash_list[post] == key //意味着当前表中的key已经存在
}

void hashSave(int *hash_list, int key)    //先通过key获取存储位置,对key存储位置进行判断
{
    int post = hashSearch(hash_list,key);     //算出post的值
    if(post == -1 || hash_list[post] == key)
    {
        printf("表溢出或key已经存在!!!\n");
        return;
     }

    hash_list[post] = key;        //将数据存储到哈希表中 
}

int main(int argc, const char *argv[])
{
    int i;
    int a[11] = {23,34,14,38,46,16,68,15,7,31,26};
    int hash_list[15] = { 0 };//哈希表长度为15 因为 数据长度n / 装填因子a  11 / 0.75,装填因子通常采用0.7-0.8之间最为合理
    for(i = 0; i < 11; i++)        //将数据全部保存到哈希表中
    {
        hashSave(hash_list,a[i]);
    }
    

for(i = 0; i < 15; i++)    //打印哈希表 
    {
        printf("%d ",hash_list[i]);
    }
    printf("\n");
    return 0;
}
2.链地址法(开散列)

在这里插入图片描述

typedef struct node_t  
{
    int key;
    struct node_t *next;
}link_node_t,*link_list_t;

int hashFun(int key)    //哈希函数
{
    int post = key % 13;
    return post;
}

 link_list_t hashSearch(link_list_t *hash_list,int key)
{
    link_list_t h = NULL;    //用来保存无头链表的头指针
        //1.先通过key调用哈希函数获取位置
    int post = hashFun(key);    //需要将判断key放入第条链表
    h = hash_list[post];    //h指向key对应存储位置的那条链表
    while(h != NULL && h->key != key)    //相当于遍历无头链表,同时检查key
    {
        h = h->next;
    }
    return h;
    //h == NULL 说明没找到key可以进行存储
    //h != NULL h->key == key 说明key已经存在
}


//存储数据
void hashSave(link_list_t *hash_list,int key)
{
    int post = hashFun(key);
    link_list_t new = NULL;    //用来保存新创建的节点
    link_list_t p = hashSearch(hash_list,key);
    if(p == NULL)//key不存在,可以进行插入数据
    {
        new = (link_list_t)malloc(sizeof(link_node_t));    //1.创建一个新的节点用来保存key 
        if(NULL == new)
        {
            perror("new malloc failed");
            return;
         }
                 //2.将key保存到新节点中
         new->key = key;    
         new->next = NULL;
                 //3.将新的节点插入到对应存储位置的链表中,将新节点每次插入无头链表头的位置
         new->next = hash_list[post];    
         hash_list[post] = new;
     }
     else
         
printf("key已经存在了!!!\n");
}


int main(int argc, const char *argv[])
{
    int i;
    link_list_t h = NULL;//临时保存每条链表的头
    int a[11] = {23,34,14,38,46,16,68,15,7,31,26}//hash_list是一个结构体指针数组,每一个元素都是结构体指针
    
link_list_t hash_list[13] = { 0 };//为什么长度是13,因为保留余数法每次%13 得到的位置在0-12之间

    for(i = 0; i < 11; i++)        //将所有的key保存起来
     {
         hashSave(hash_list,a[i]);
     }
    //遍历哈希表 有13条链表
     for(i = 0; i < 13; i++)//hash_list中保存的是13条无头链表的头指针
     {
         printf("%d:",i);
         h = hash_list[i];
         while(h != NULL)//相当于遍历无头链表
         {
             printf("%d ",h->key);
             h = h->next;
         }
         printf("\n");
     }
     return 0;
}

作业

#####作业#####

    char a[] = "asdfasdlifadshjklrgeopaewrfpawedfoplhafgd"
    
    统计找到字符串中出现次数最多的字符并统计其出现次数,打印输出


#include <stdio.h>

int hashFun(char key)
{
    int post = key-'a';//下标为0的位置存的字符'a'的个数  1位置 'b'的个数  
    return post;
}

int main(int argc, const char *argv[])
{
    int max = -1;
    int i = 0;
    int post;//保存存储位置
    char a[] = "aaaabbbbacdddddeeemmmlzoe";
    int hash_list[26] = { 0 };//长度为26,因为总共就26个字母
    while(a[i] != '\0')
    {
        post = hashFun(a[i]);
        hash_list[post]++;
        i++;
    }
    //经过上面的遍历后,已经统计出结果
    for(i = 0; i < 26; i++)//max保存出现字母最多的次数
        max = max < hash_list[i] ? hash_list[i] : max;
    for(i = 0; i < 26; i++)
    {
        if(hash_list[i] == max)//只要与最多次数相同,那么就是最多的字母
        {
            printf("%c出现的次数是%d\n",i+'a',hash_list[i]);
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值