数据结构(C语言版)-第7章 查找

7.1 查找的基本概念

查找表:
    由同一类型的数据元素(或记录)构成的集合
静态查找表:
    查找的同时对查找表不做修改操作(如插入和删除)
动态查找表:
    查找的同时对查找表具有修改操作
关键字
    记录中某个数据项的值,可用来识别一个记录
主关键字:
    唯一标识数据元素
次关键字:
    可以标识若干个数据元素

查找算法的评价指标

关键字的平均比较次数,也称平均搜索长度ASL(Average Search Length)

n:记录的个数
pi:查找第i个记录的概率 ( 通常认为pi =1/n )
ci:找到第i个记录所需的比较次数

7.2 线性表的查找

一、顺序查找(线性查找)

应用范围:
  顺序表或线性链表表示的静态查找表
  表内元素之间无序

顺序表的表示

typedef struct {
          ElemType   *R; //表基址
          int          length;     //表长
}SSTable;

第2章在顺序表L中查找值为e的数据元素

int LocateELem(SqList L,ElemType e)
{  for (i=0;i< L.length;i++)
      if (L.elem[i]==e) return i+1;
  return 0;}

改进:把待查关键字key存入表头(“哨兵”),从后向前逐个比较,可免去查找过程中每一步都要检测是否查找完毕,加快速度。

int Search_Seq( SSTable  ST , KeyType  key ){
   //若成功返回其位置信息,否则返回0
  ST.R[0].key =key;
for( i=ST.length; ST.R[ i ].key!=key;  - - i  );
//不用for(i=n; i>0; - -i) 或 for(i=1; i<=n; i++)
   return i;
}

顺序查找的性能分析:

空间复杂度:一个辅助空间。
时间复杂度:
1) 查找成功时的平均查找长度
  设表中各记录查找概率相等
    ASLs(n)=(1+2+ ... +n)/n =(n+1)/2
2)查找不成功时的平均查找长度    ASLf =n+1

顺序查找算法有特点:

算法简单,对表结构无任何要求(顺序和链式)
n很大时查找效率较低

改进措施:非等概率查找时,可按照查找概率进行排序。

二、折半查找(二分或对分查找)

若k==R[mid].key,查找成功
若k<R[mid].key,则high=mid-1
若k>R[mid].key,则low=mid+1

设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,k为给定值
初始时,令low=1,high=n,mid=?(low+high)/2?
让k与mid指向的记录比较
若k==R[mid].key,查找成功
若k<R[mid].key,则high=mid-1
若k>R[mid].key,则low=mid+1
重复上述操作,直至low>high时,查找失败

int Search_Bin(SSTable ST,KeyType key){
//若找到,则函数值为该元素在表中的位置,否则为0
    low=1;high=ST.length;                         while(low<=high){
        mid=(low+high)/2;
        if(key==ST.R[mid].key) return mid;
        else if(key<ST.R[mid].key) high=mid-1;//前一子表查找
        else low=mid+1;                               //后一子表查找
    }                                        return 0;        //表中不存在待查元素
}

折半查找的性能分析:

查找过程:每次将待查记录所在区间缩小一半,比顺序查找效率高,时间复杂度O(log2 n)

适用条件:采用顺序存储结构的有序表,不宜用于链式结构

三、分块查找

分块有序,即分成若干子表,要求每个子表中的数值都比后一块中数值小(但子表内部未必有序)。
然后将各子表中的最大关键字构成一个索引表,表中还要包含每个子表的起始地址(即头指针)。

① 对索引表使用折半查找法(因为索引表是有序表);
② 确定了待查关键字所在的子表后,在子表内采用顺序查找法(因为各子表内部是无序表);

分块查找性能分析:

优点:插入和删除比较容易,无需进行大量移动。
缺点:要增加一个索引表的存储空间并对初始索引表进行排序运算。
适用情况:如果线性表既要快速查找又经常动态变化,则可采用分块查找。

7.3 树表的查找

二叉排序树
平衡二叉树
B-树
B+树
键树

二叉排序树:

二叉排序树或是空树,或是满足如下性质的二叉树:
(1)若其左子树非空,则左子树上所有结点的值均小于根结点的值;
(2)若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
(3)其左右子树本身又各是一棵二叉排序树

中序遍历二叉排序树后的结果有什么规律?

得到一个关键字的递增有序序列

二叉排序树的操作-查找

(1)若二叉排序树为空,则查找失败,返回空指针。
(2)若二叉排序树非空,将给定值key与根结点的关键字T->data.key进行比较:
① 若key等于T->data.key,则查找成功,返回根结点地址;
② 若key小于T->data.key,则进一步查找左子树;
③ 若key大于T->data.key,则进一步查找右子树。

BSTree SearchBST(BSTree T,KeyType key) {
   if((!T) || key==T->data.key) return T;
   else if (key<T->data.key)  return SearchBST(T->lchild,key);    //在左子树中继续查找
   else return SearchBST(T->rchild,key);                       //在右子树中继续查找
} // SearchBST

二叉排序树的操作-插入

若二叉排序树为空,则插入结点应为根结点
否则,继续在其左、右子树上查找
树中已有,不再插入
树中没有,查找直至某个叶子结点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或右孩子

插入的元素一定在叶结点上

二叉排序树的操作-生成

从空树出发,经过一系列的查找、插入操作之后,可生成一棵二叉排序树

不同插入次序的序列生成不同形态的二叉排序树

二叉排序树的操作-删除

将因删除结点而断开的二叉链表重新链接起来
防止重新链接后树的高度增加

查找的性能分析:

平均查找长度和二叉树的形态有关,即,
最好:log2n(形态匀称,与二分查找的判定树相似)
最坏:  (n+1)/2(单支树)

平衡二叉树

左、右子树是平衡二叉树;
所有结点的左、右子树深度之差的绝对值≤ 1

平衡因子:该结点左子树与右子树的高度差

任一结点的平衡因子只能取:-1、0 或 1;如果树中任意一个结点的平衡因子的绝对值大于1,则这棵二叉树就失去平衡,不再是AVL树;

对于一棵有n个结点的AVL树,其高度保持在O(log2n)数量级,ASL也保持在O(log2n)量级。

如果在一棵AVL树中插入一个新结点,就有可能造成失衡,此时必须重新调整树的结构,使之恢复平衡。我们称调整平衡过程为平衡旋转。

7.4 哈希表的查找

基本思想:记录的存储位置与关键字之间存在对应关系,Loc(i)=H(keyi)

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

哈希方法(杂凑法)
选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放;
查找时,由同一个函数对给定值k计算地址,将k与地址单元中元素关键码进行比,确定查找是否成功。

哈希函数(杂凑函数):哈希方法中使用的转换函数

冲 突:不同的关键码映射到同一个哈希地址    key1?key2,但H(key1)=H(key2)

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

如何减少冲突

哈希函数的构造方法

1. 直接定址法

Hash(key) = a·key + b    (a、b为常数)

优点:以关键码key的某个线性函数值为哈希地址,不会产生冲突。

缺点:要占用连续地址空间,空间效率低。

2. 数字分析法

3.  平方取中法

4. 折叠法

5. 除留余数法

Hash(key)=key  mod  p    (p是一个整数)

关键:如何选取合适的p?
技巧:设表长为m,取p≤m且为质数

6. 随机数法

处理冲突的方法

1.开放定址法(开地址法)   2.链地址法

1.开放定址法(开地址法)

基本思想:有冲突时就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总能找到,并将数据元素存入。

线性探测法

Hi=(Hash(key)+di) mod m       ( 1≤i < m )
  其中:m为哈希表长度
     di 为增量序列 1,2,…m-1,且di=i

一旦冲突,就找下一个空地址存入

优点:只要哈希表未被填满,保证能找到一个空地址单元存放有冲突的元素。
缺点:可能使第i个哈希地址的同义词存入第i+1个地址,这样本应存入第i+1个哈希地址的元素变成了第i+2个哈希地址的同义词,……,产生“聚集”现象,降低查找效率。

解决方案:二次探测法

伪随机探测法

Hi=(Hash(key)+di) mod m       ( 1≤i < m )
  其中:m为哈希表长度
     di 为随机数

2.链地址法(拉链法)

基本思想:相同哈希地址的记录链成一单链表,m个哈希地址就设m个单链表,然后用用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构

step1 取数据元素的关键字key,计算其哈希函数值(地址)。若该地址对应的链表为空,则将该元素插入此链表;否则执行step2解决冲突。
step2 根据选择的冲突处理方法,计算关键字key的下一个存储地址。若该地址对应的链表为不为空,则利用链表的前插法或后插法将该元素插入此链表。

链地址法的优点:

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

原文地址:https://www.cnblogs.com/mohuishou-love/p/10400511.html

时间: 2024-08-03 03:37:11

数据结构(C语言版)-第7章 查找的相关文章

《数据结构-C语言版》(严蔚敏,吴伟民版)课本源码+习题集解析使用说明

先附上文档归类目录: 课本源码合辑  链接??? <数据结构>课本源码合辑 习题集全解析  链接??? <数据结构题集>习题解析合辑 博主有话说: 01.自学编程,难免思路阻塞,所以从今天起,我(StrayedKing)决定在本博客陆续更新严蔚敏,吴伟民版<数据结构-C语言版>各章节的课本源码和配套习题集答案解析,目的是为了整理数据结构中的知识点,并与网友交流意见,集思广益,共同进步.        ★注★ 左侧随笔分类下用两个栏目:<课本源码>.<习

数据结构c语言版串的操作

#include<stdio.h> #include<malloc.h> #include<string.h> //定义字符串的结构体 typedef struct { char *str;//字符串 int maxLength;//最大可以存放字符的长度 int length;//目前的字符长度 }DString; //1.初始化操作 //初始化操作用来建立和存储串的动态数组空间以及给相关的数据域赋值 void Initiate(DString *s,int max,

深入浅出数据结构C语言版(12)——从二分查找到二叉树

在很多有关数据结构和算法的书籍或文章中,作者往往是介绍完了什么是树后就直入主题的谈什么是二叉树balabala的.但我今天决定不按这个套路来.我个人觉得,一个东西或者说一种技术存在总该有一定的道理,不是能解决某个问题,就是能改善解决某个问题的效率.如果能够先了解到存在的问题以及已存在的解决办法的不足,那么学习新的知识就更容易接受,也更容易理解. 万幸的是,二叉树的讲解是可以按照上述顺序来进行的.那么,今天在我们讨论二叉树之前,我们先来讨论一种情形.一种操作:假设现在有一个数组,数组中的数据按照某

严蔚敏《数据结构(C语言版)》——第1章 绪论

数据是对客观事物的符号表示.在计算机科学中是指所有能输入到计算机中并被计算机程序处理的符号的总称. 数据元素是数据的基本单位,在计算机程序中通常作为一个整体进行考虑和处理. 数据对象是性质相同的数据元素的集合,是数据的一个子集. 数据结构是相互之间存在一种或多种特定关系的数据元素的集合. 存储结构是数据结构在计算机中的表示. 数据类型是一个值的集合和定义在这个值集上的一组操作的总称. 抽象数据类型是指一个数学模型以及定义在该模型上的一组操作,是对一般数据类型的扩展. 试描述数据结构和抽象数据类型

数据结构与算法分析(C语言版)第二章习题2.7代码实现

#include<stdio.h> #include<stdlib.h> #include<time.h> void swap(int *a,int *b) {     if(*a!=*b)     {         /*          算法1          *a+=*b;          *b=*a-*b;          *a-=*b;                    算法2          *a*=*b;          *b=*a/*b;

深入浅出数据结构C语言版(5)——链表的操作

上一次我们从什么是表一直讲到了链表该怎么实现的想法上:http://www.cnblogs.com/mm93/p/6574912.html 而这一次我们就要实现所说的承诺,即实现链表应有的操作(至于游标数组--我决定还是给它单独写个博文比较好~). 那么,我们的过程应该是怎么样的呢?首先当然是分析需要什么操作,然后再逐一思考该如何实现,最后再以代码的形式写出来. 不难发现,我们希望链表能支持的(基础,可以由此延伸)操作就是: 1.给出第n个元素 2.在第n个元素的后面插入一个元素(包含在最后一个

深入浅出数据结构C语言版(14)——散列表

我们知道,由于二叉树的特性(完美情况下每次比较可以排除一半数据),对其进行查找算是比较快的了,时间复杂度为O(logN).但是,是否存在支持时间复杂度为常数级别的查找的数据结构呢?答案是存在,那就是散列表(hash table,又叫哈希表).散列表可以支持O(1)的插入,理想情况下可以支持O(1)的查找与删除. 散列表的基本思想很简单: 1.设计一个散列函数,其输入为数据的关键字,输出为散列值n(正整数),不同数据关键字必得出不同散列值n(即要求散列函数符合单射条件) 2.创建一个数组HashT

深入浅出数据结构C语言版(8)——后缀表达式、栈与四则运算计算器

在深入浅出数据结构(7)的末尾,我们提到了栈可以用于实现计算器,并且我们给出了存储表达式的数据结构(结构体及该结构体组成的数组),如下: //SIZE用于多个场合,如栈的大小.表达式数组的大小 #define SIZE 1000 //表达式的单个元素所使用的结构体 typedef struct elem { int num = 0; //若元素存储操作数则num为该操作数 char oper = '='; //若元素存储操作符则oper为该操作符 bool IsNum = false; //用于

《数据结构(C语言版)》学习——day1,初识数据结构

1. 什么是数据结构 一般而言,使用计算机解决一个具体的问题时,大致需要经过以下几个步骤: ① 从具体的问题中抽象出一个适当的数学模型: ② 设计一个求解该数学模型的算法: ③ 编写程序,进行测试.调整,直至得到最终的问题解答. 对实际问题建立数学模型的实质是:分析问题,并从中提取操作的对象,并找出这些对象间含有的关系,然后使用数学的语言加以描述. 数据结构(data structure)可以定义为:相互之间存在一种或多种特定关系的数据元素的集合. 在任何问题中,数据元素都不是孤立存在的,而是存

数据结构( Pyhon 语言描述 ) &mdash; &mdash;第9章:列表

概念 列表是一个线性的集合,允许用户在任意位置插入.删除.访问和替换元素 使用列表 基于索引的操作 基本操作 数组与列表的区别 数组是一种具体的数据结构,拥有基于单个的物理内存块的一种特定的,不变的实现. 列表是一种抽象的数据类型,可以由各种方式表示,数组只是其中一种方式 基于内容的操作 基本操作 基于位置的操作 相对于游标位置执行,这个操作允许程序员在通过移动游标在列表中导航.其可通过列表迭代器来实现 列表迭代器是附加到列表的后备储存 列表迭代器游标的位置 第一项之前 相领两项之间 最后一项之