• 第七章学习小结


    本章学习了三种不同结构的查找表:线性表、树表和散列表。查找的术语包括:查找表、关键字(主关键字、次关键字)、查找(查找成功、查找不成功)、动态查找表和静态查找表、平均查找长度(ASL)

    (一)线性表的查找

    1、顺序查找

     1 typedef struct   //数据元素类型定义 
     2 {
     3     KeyType key;    
     4     InfoType otherinfo;    
     5 }ElemType;
     6 typedef struct   //顺序表的定义
     7 {
     8     ElemType *R;    //基地址
     9     int length;    
    10 }SSTable;
    11 
    12 //查找 
    13 int Search_Seq(SSTable ST, KeyType key)
    14 {
    15     for(i=ST.length; i>=1; --i)
    16            if(ST.R[i].key==key)      
    17                return i;   //从后往前找
    18     return 0;
    19 }
    20 //or设置监视哨的顺序查找
    21 int Search_Seq(SSTable ST, KeyType key)
    22 {
    23     ST.R[0].key=key;   //“哨兵”
    24     for(i=ST.length; ST.R[i].key!=key; --i)
    25     return i;  
    26 }

    时间复杂度为O(n),空间复杂度为O(1)。

    优点:对表结构无任何要求,既适用于顺序结构,也适用于链式结构,无论记录是否按关键字有序均可应用。

    缺点:平均查找长度较大,查找效率较低,所以当n很大时,不宜采用顺序查找。

    习题:

    n个数存在一维数组A[1...n]中,在进行顺序查找时,这n个数的排列有序或无序其平均查找长度ASL不同。----正确

    析:查找概率相等时,ASL相同;查找概率不等时,如果从前向后查找,则按查找概率由大到小排列的有序表其ASL要比无序表ASL小。

    2、折半查找/二分查找

     1 //非递归算法 
     2 int Search_Bin(SSTable ST, KeyType key)
     3 {
     4     low=1;   high=ST.length;    
     5     while(low<=high)
     6     {
     7         mid=(low+high)/2;    
     8         if(key==ST.R[mid].key)    return mid;   
     9         else if(key<ST.R[mid],key)    high=mid-1;    //前一子表查找
    10         else   low=mid+1;     //后一子表查找
    11     }
    12     return 0;  
    13 }
    14 //递归算法
    15 int Search_Bin(SSTable ST, KeyType key, int low, int high)
    16 {
    17     if(low>high)     
    18         return 0;
    19     mid=(low+high)/2;
    20     if(key==ST.elem[mid].key)    
    21         return mid;
    22     else if(key<ST.elem[mid].key)
    23         return Search_Bin(ST, key, low, mid-1)   //递归
    24     else   
    25         return Search_Bin(ST, key, mid+1, high)    //递归
    26 }

    时间复杂度为O(log2n)。

    优点:比较次数少,查找效率高。

    缺点:对表结构要求高,只能用于顺序存储的有序表。查找前需要排序。不适用于数据元素经常变动的线性表。

    3、分块查找/索引顺序查找

    特点:块间有序,块内无序

    查找效率:ASL=Lb+Lw 。Lb :对索引表查找的ASL;Lw :对块内查找的ASL。

    查找块:顺序查找 Or 折半查找(更优);块内查找:顺序查找。

    优点:由于块内是无序的,故插入和删除比较容易,无需进行大量移动。如果线性表既要快速查找又经常动态变化,则可采用分块查找。

    缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。

    (二)树表的查找

    1、二叉排序树

    空树 Or 具有下列性质的二叉树:

    (1)若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

    (2)若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

    (3)它的左、右子树也分别为二叉排序树。

    二叉排序树是递归定义的。一个重要性质:中序遍历一棵二叉树时可以得到一个结点值递增的有序序列。

     1 typedef struct    //结点的数据域结构
     2 {
     3     KeyType key;    
     4     InfoType otherinfo; 
     5 }ElemType;   
     6 typedef struct BSTNode   //结点结构
     7 {
     8     ElemType data;   
     9     struct BSTNode *lchild, *rchild;   
    10 }BSTNode, *BSTree;
    11 
    12 //递归查找
    13 BSTree SearchBST(BSTree T, KeyType key)
    14 {
    15     if((!T)||key==T->data.key)   return T;     
    16     else if(key<T->data.key)   return SearchBST(T->lchild, key);  
    17     else  return SearchBST(T->rchild, key);    
    18 }
    19 
    20 //插入--插入的元素一定在叶结点上
    21 void InsertBST(BSTree &T, ElemType e)
    22 {
    23     if(!T)
    24     {//找到插入位置,递归结束
    25         S=new BSTNode;     //生成新结点*S
    26         S->data=e;      //新结点*S的数据域置为e
    27         S->lchild=S->rchild=NULL;    //新结点*S作为叶子结点
    28         T=S;     //把新结点*S链接到已找到的插入位置
    29     }
    30     else if(e.key<T->data.key)
    31           InsertBST(T->lchild, e);      //将*S插入左子树
    32     else if(e.key>T->data.key)
    33           InsertBST(T->rchild, e);      //将*S插入右子树
    34 }
    35 
    36 //创建
    37 void CreateBST(BSTree &T)
    38 {//依次读入一个关键字为key的结点,将此结点插入二叉排序树T中
    39     T=NULL;      //将二叉排序树T初始化为空树
    40     cin>>e;  
    41     while(e.key!=ENDFLAG)     //ENDFLAG为自定义常量,作为输入结束标志
    42     {
    43         InsertBST(T, e);     //将此结点插入二叉排序树T中
    44         cin>>e; 
    45     }  
    46 }
    47 
    48 //删除
    49 void DeleteBST(BSTree &T, KeyType key)
    50 {//从二叉排序树T中删除关键字等于key的结点
    51     p=T;  f=NULL;    //初始化
    52     /*------下面的while循环从根开始查找关键字等于key的结点*p------*/
    53     while(p)
    54     {
    55         if(p->data.key==key)   break;//找到关键字等于key的结点*p
    56         f=p;     //*f为*p的双亲结点
    57         if(p->data.key>key)   p=p->lchild;  //在*p的左子树中继续查找
    58         else p=p->rchild;     //在*p的右子树中继续查找
    59     }
    60     if(!p)      return;    //找不到被删结点则返回
    61     /*---考虑3种情况实现p所指子树内部的处理:*p左右子树均不空、无右子树、无左子树-----*/
    62     if((p->lchild)&&(p->rchild))     //被删结点*p左右子树均不空
    63     {
    64         q=p;  s=p->lchild;
    65         while(s->rchild)
    66         //在*p的左子树中继续查找其前驱家电,即最右下结点
    67         {
    68             q=s;  s=s->rchild;   //向右到尽头
    69         }
    70         p->data=s->data;     //s指向被删结点的“前驱”
    71         if(q!=p)    q->rchild=s->lchild;     //重接*q的右子树
    72         else  q->lchild=s->lchild;     //重接*q的左子树
    73         delete s;
    74         return;
    75     }
    76     else if(!p->rchild)    //被删结点*p无右子树,只需重接其左子树
    77     {
    78         q=p;   p=p->lchild;    delete q;
    79     }
    80     else if(!p->lchild)    //被删结点*p无左子树,只需重接其右子树
    81     {
    82         q=p;   p=p->rchild;   delete q;
    83     }
    84     /*---将p所指的子树挂接到其双亲结点*f相应的位置---------*/
    85     if(!f)    T=p;    //被删结点为根结点
    86     else if(q=f->lchild)    f->lchild=p;    //挂接到*f的左子树位置 
    87     else  f->rchild=p;    //挂接到*f的右子树位置
    88     delete q;    
    89 }
    二叉排序树

    查找的时间复杂度为 O(log2n),而插入和删除的基本操作也是查找,时间复杂度均为O(log2n)。创建的时间复杂度为 O(nlog2n)。

    查找性能分析:

    平均查找长度和二叉树的形态有关,即

    最好:log2n(形态匀称,与二分查找的判定树相似)

    最坏:(n+1)/2(单支树)

    2、平衡二叉树(AVL树)

    空树 Or 具有如下特征的二叉排序树:

    (1)左子树和右子树的深度之差的绝对值不超过1;

    (2)左子树和右子树也是平衡二叉树。 

    平衡因子只可能是-1、0和1。

    深度和log2n是同数量级的(其中n为结点个数)。由此,其查找的时间复杂度是O(log2n)。

    平衡二叉树的平衡调整方法:LL、RR、LR、RL。

    对于同样的数据量,二叉查找树的深度大,而多叉树的深度小,因此多叉树适用于要求查找深度小的检索。

    3、B树(多叉树):B-树和B+树

    特点:平衡、有序、多路

    (三)散列表的查找

    散列查找法又叫杂凑法或散列法。

    优点:查找速度极快O(1),查找效率与元素个数n无关。

    1、术语:

    散列函数(哈希函数)和散列地址:p=H(key),称这个对应关系H为散列函数,p为散列地址。H函数称为哈希函数。

    散列表:一个有限连续的地址空间,用以存储按散列函数计算得到相应散列地址的数据记录。通常散列表的存储空间是一个一维数组,散列地址是数组的下标。

    冲突:对不同的关键字可能得到同一散列地址。

    同义词:具有相同函数值的关键字。

    2、散列函数的构造方法

    (1)数字分析法:适合于能预先估计出全体关键字的每一位上各种数字出现的频度。

    (2)平法取中法:适合于关键字中每一位都有某些数字重复出现频度很高的现象。

    (3)折叠法:适合于关键字的数字位数特别多。

    (4)除留余数法:Hash(key)=key mod p(p是一个整数)

    一般情况下,可以选p为小于表长的最大质数。

    3、处理冲突的方法

    (1)开放地址法:Hi=(H(key)+di) MOD m       i=1,2,…,k(k≦m-1)

    其中,H(key)为散列函数,m为散列表表长,di为增量序列。根据di取值的不同,可以分为以下3种探测方法:

    A.线性探测法:di=l,2,3,…,m-1

    B.二次探测法:di=1²,-1²,2²,-2²,3²,…,+k²,-k²(k≦m/2)

    C.伪随机探测法:di=伪随机数序列

    优点:只要散列表未填满,总能找到一个不发生冲突的地址。

    缺点:会产生“二次聚集”现象。而二次探测法和伪随机探测法的优点是:可以避免“二次聚集”现象。缺点也很显然:不能保证一定找到不发生冲突的地址。

    (2)链地址法(拉链法)

    优点:非同义词不会冲突,无“聚集”现象;链表上结点空间动态申请,更适合于表厂不确定的情况。

    4、ASL的决定因素:散列函数、处理冲突的方法和散列表的装填因子α(α=表中填入的记录数/散列表的长度)。 

    散列表的ASL是处理冲突方法和装载因子的函数。

    哈希表的平均查找长度是α的函数,而不是n的函数。

    哈希表所特有的特点:用哈希表构造查找表时,可以选择一个适当的装填因子α,使得平均查找长度限定在某个范围内

    关于哈希查找的几点结论: 

    1.哈希表技术具有很好的平均性能,优于一些传统的技术

    2.链地址法优于开地址法

    3.除留余数法作哈希函数优于其它类型函数 

  • 相关阅读:
    keras:InternalError: Failed to create session
    centos 常用命令
    centos7 安装gdal2.3.1
    centos mysql初探 -- 配置、基本操作及问题
    machine learning 之 Recommender Systems
    machine learning 之 Anomaly detection
    centos R包 tidyverse安装
    centos 问题解决记录
    R python在无图形用户界面时保存图片
    隐私政策
  • 原文地址:https://www.cnblogs.com/zzssx/p/13193077.html
Copyright © 2020-2023  润新知