第八章 查找
第8章 查找
•
• 查找的分类
1. 按查找条件分为按记录的主关键字查找和多 关键字组合查找
2. 按查找数据在外存或者内存分为外部查找和 内部查找 3. 按目的分为静态查找和动态查找。
4. 静态查找就是在查找表中查找满足条件的记 录是否存在。 5. 动态查找是确定待查记录查找的位置,进而 进行插入或删除操作。
8.2 静态查找
8.1 查找的基本概念
• 查找表:在计算机中,被查找的数据对象是 由同一类型的数据元素(或记录)构成的集 合,可称之为查找表。
• 关键字:在实际应用问题中,每个记录一般 包含有多个数据域,查找是根据其中某一个 或几个指定的域进行的,这个作为查找依据 的域称为关键字。
• 主关键字与次关键字:在所有关键字中,我 们将能唯一标识一个数据元素的关键字称为 主关键字;而其它关键字称为辅助关键字或 次关键字。 • 查找:又称检索,它是根据给定的关键字值 ,在查找表中找出其关键字等于给定值的数 据元素。若有这样一个记录,则查找成功, 查找结果可输出该记录内容或给出该记录在 文件中的位置;若不存在这样的记录,返回 特定的值,查找不成功。
• 8.2.1 顺序表上的顺序查找 • 8.2.2折半查找 • 8.2.3 分块查找
8.2.1 顺序表上的顺序查找
顺序查找的基本思想
从顺序表的一端开始,依次将每个元素的关键 字同给定值K进行比较。若某个关键字值为K, 则查找成功,返回元素所在下标i;若所有元素 都比较完,仍找不到,则查找失败,返回0。
顺序查找算法-结点定义(适用于顺序查 找和折半查找)
#define MAXSIZE 100 typedef struct { int key; // 关键字项 } Sselement; typedef struct { SSELEMENT r[MAXSIZE]; int len; // 顺序表长度 } SSTable; // 顺序表类型
第八章 查找
8.2.3 分块查找
查找过程:将表分成几块,块内无序,块间有序; 先确定待查记录所在块,再在块内顺序查找. 适用条件:分块有序表. 算法实现 用数组存放待查记录,每个数据元素至少含有关 键字域. 建立索引表,每个索引表结点含有块最大关键 字域和指向块第一个结点的指针.
分块查找方法图示
索引表 22 1 48 7 86 13 查38
直接定址法
构造:取关键字或关键字的某个线性函 数作哈希地址,即H(key)=key 或 H(key)=akey+b 特点 直接定址法所得地址集合与关键字集合 大小相等,不会发生冲突. 实际中能用这种哈希函数的情况很少.
数字分析法
构造:对关键字进行分析,取关键字的若干位或其组 合作为哈希地址. 适于关键字位数比哈希地址位数大,且可能出现的关 键字事先知道的情况.
关键码 THE A TO OF IN THAT AND IS 二进制形式 101000100000101 0001 1010001111 0111100110 0100101110 1010001000000011010 0 000010111000100 0100110011 抽取结果(散列函数值) (101)2=5 (001)2=1 (111)2=7 (110)2=6 (010)2=2 (100)2=4 (000)2=0 (011)2=3
对含有 n 个记录的表, 其中: p i 为查找表中第 c i 为找到表中第 ASL =
∑
n
i =1
pici
i 个元素的概率,
数据结构第8章 查找 答案
第8章 查找 测试题 及答案一、填空题1. 在数据的存放无规律而言的线性表中进行检索的最佳方法是 顺序查找(线性查找) 。
2. 线性有序表(a 1,a 2,a 3,…,a 256)是从小到大排列的,对一个给定的值k ,用二分法检索表中与k 相等的元素,在查找不成功的情况下,最多需要检索 8 次。
设有100个结点,用二分法查找时,最大比较次数是 7 。
3. 假设在有序线性表a[20]上进行折半查找,则比较一次查找成功的结点数为1;比较两次查找成功的结点数为 2 ;比较四次查找成功的结点数为 8 ;平均查找长度为 3.7 。
解:显然,平均查找长度=O (log 2n )<5次(25)。
但具体是多少次,则不应当按照公式)1(log 12++=n nn ASL 来计算(即(21×log 221)/20=4.6次并不正确!)。
因为这是在假设n =2m -1的情况下推导出来的公式。
应当用穷举法罗列:全部元素的查找次数为=(1+2×2+4×3+8×4+5×5)=74; ASL =74/20=3.7 !!!4.【计研题2000】折半查找有序表(4,6,12,20,28,38,50,70,88,100),若查找表中元素20,它将依次与表中元素 28,6,12,20 比较大小。
5. 在各种查找方法中,平均查找长度与结点个数n 无关的查找方法是 散列查找 。
6. 散列法存储的基本思想是由 关键字的值 决定数据的存储地址。
7. 有一个表长为m 的散列表,初始状态为空,现将n (n<m )个不同的关键码插入到散列表中,解决冲突的方法是用线性探测法。
如果这n 个关键码的散列地址都相同,则探测的总次数是 n(n-1)/2=( 1+2+…+n-1) 。
(而任一元素查找次数 ≤n-1)二、单项选择题( B )1.在表长为n的链表中进行线性查找,它的平均查找长度为A. ASL=n; B. ASL=(n+1)/2;C. ASL=n +1; D. ASL≈log2(n+1)-1( A )2.折半查找有序表(4,6,10,12,20,30,50,70,88,100)。
第8章 查找
72
85
88
90
11
[ 35 ↑ ↑ low mid [ 35 ↑ low
64
72
85
88
90
98
5
11
23 ] ↑ high
64
72
85
88
90
98
(b) 查找K=30的过程(三次关键字比较后查找失败)
6 3 9
1
4
7
10
•不成功:从根到叶子或度1结点
2
5
8
11
二、算法实现
(略)
三、性能分析
•最坏:不超过树高, log2(n + 1) •平均:ASL≈(n+1)/n*log2(n+1)-1 ≈log2(n+1)-1=O(log2n) 。
第8章 查找
8.1 8.2 8.3 8.4 基本概念 静态查找表 树表的查找 散列表
查找就是在数据集合中寻找满足某种条件的数据对象。
知识点结构
查找的基本概念 查找结构 查找算法的时间性能
静态表的查找技术 顺序查找 二分查找
树表的查找技术 二叉排序树
散列表的查找技术 散列的基本思想
插值查找
斐波那契查找
6 3 9
1
4
7
10
2
5
8
11
例
10个结点的有序表,用二分查找,成功和不成功的ASL=?
首先,建立判定树:mid=(low+high)/2
5 2 1 3 4 6 7 8 9 10
查找成功时:ASL=(1×1+2×2+3×4+4×3)/10 不成功时:ASL=(3×5+4×6)/11
第8章 查找 习题参考答案
习题八 参考答案一、选择题1.对线性表进行二分查找时,要求线性表必须( B )A.以顺序方式存储B.以顺序方式存储,且结点按关键字值有序排列C.以链接方式存储D.以链接方式存储,且结点按关键字值有序排列2. 用二分查找法查找具有n 个结点的顺序表时,查找每个结点的平均比较次数是( D )A.O(n 2)B.O(nlog 2n)C.O(n)D.O(log 2n)3. 对长度为4的顺序表进行查找,若查找第一个记录的概率为1/24, 查找第二个记录的概率为1/6, 查找第三个记录的概率为2/3, 查找第四个记录的概率为1/8,则查找任意一个记录的平均查找长度为( A )A.23/8B.20/8C.17/8D.14/84. 若有一个长度为64的有序表,现用二分查找方法查找某一记录,则查找不成功,最多需要比较( B )次。
A.9B.7C.5D.35. 当采用分块查找时,数据的组织方式为( C )A.数据必须有序B.数据不必有序C.数据分成若干块,每块内数据不必有序,但块间必须有序D.数据分成若干块,每块内数据必须有序,但块间不必有序6. 一棵深度为k 的平衡二叉树,其每个非终端结点的平衡因子均为0,则该平衡二叉树共有( C )个结点。
A.2k-1-1B.2k-1+1C.2k -1D.2k +17. 具有5层结点的平衡二叉树至少有( B )个结点。
A.10B.12C.15D.178. 若结点的存储地址与其关键字之间存在某种映射关系,则称这种存储结构为( D )A.顺序存储结构B.链式存储结构C.索引存储结构D.散列存储结构9. 以下有关m 阶B-树的叙述中,错误的是( B )。
A.根结点至多有m 棵子树B.每个结点至少有⎥⎥⎤⎢⎢⎡2m 棵子树 C.所有叶子结点都在同一层上 D.每个结点至多有m-1个关键字10.哈希表的地址区间为0~17,哈希函数为h(key)=K%17。
采用线性探测法处理冲突,并将关键字序列{26,25,72,38,8,18,59}依次存储到哈希表中,则在哈希表中查找元素59需要搜索的次数为( C )。
西北大学:数据结构第8章 查 找
基于线性表的查找法
比较式查找法 查找的基本方法:
基于树的查找法
计算式查找法-HASH(哈希)查找法
返回主目录
8.2 基于线性表的查找法
具有顺序查找法、折半查找法和分块查找法三种
8.2.1 顺序查找法 顺序查找法的特点是:用所给关键字与线性表中 各元素的关键字逐个比较,直到成功或失败。 顺序结构 存储结构: 链式结构
返回主目录
LB= 1 ∑j= b j=1
b
b+1 2
Lw= 1 ∑j= s i=1
b+s +1 2
s
s+1 2
ASLbs=LB+LW = 将b=n/s代入,得 ASLbs=
1 ( n +s) +1 2 s
返回主目录
8.3 基于树的查找法
基于树的查找法(树表查找法),是将待查表组 织成特定树的形式并在树结构上实现查找的方法 。
return(i);
}
其中l.r[0]为监视哨,可以起到防止越界的作用。
返回主目录
不设置监视哨的顺序查找算法
int SeqSearch(RecordList l, KeyType k) /*不用监视哨法,在顺序表中查找关键字等于k的元素*/ {
l.r[0].key=k; i=l.length;
while (i>=1&&l.r[i].key!=k) i--; if (i>=1) return(i)else return (0); } 循环条件i>=1判断查找是否越界。
返回主目录
生成二叉排序树的算法:
void CreateBST(BSTree *bst)
/*从键盘输入元素的值,创建相应的二叉排序树*/
程序设计基础(C语言)第8章 查找和排序算法
8.2.3二分查找的实际应用
• 【例8.3】用二分法求下面的
一元三次方程 x3 x 1 0
在区间[1, 3]上误差不大于 10-6的根。先从键盘输入迭 代初值 x0和允许的误差 , 然后输出求得的方程根和所 需的迭代次数。
//函数功能:用二分法计算并返回方程的根 double Iteration(double x1, double x2, double eps) {
8.1.2线性查找算法的程序实现
#include <stdio.h>
#define N 40
int ReadRecord(int num[], int weight[]);
int LinSearch(int num[], int key, int n);
//主函数
int main(void)
{
int num[N], weight[N], n, pos, key;
double x0; do{
return BinSearch(num, key, mid+1, high); //在后一子表查找 } else if (key < num[mid]) {
return BinSearch(num, key, low, mid-1); //在前一子表查找 } return mid; //找到,返回找到的位置下标 }
序排列的。
int BinSearch(int num[], int key, int low, int high) {
int mid = (high + low) / 2; //取数据区间的中点 if (low > high) //递归结束条件 {
第8章 查找
12
8.2.3 分块查找
分块查找过程示例的如图8.4所示。 分块查找过程示例的如图 所示。 所示
13
8.3 动态查找表
动态查找表经常要对表中的记录进行插入、 动态查找表经常要对表中的记录进行插入、 删除的操作, 删除的操作,同时动态查找表的生成也是 在查找过程中动态生成的。 在查找过程中动态生成的。 动态查找表的这种特性要求采用灵活的存 储方式来组织查找表中的记录, 储方式来组织查找表中的记录,以便高效 率地实现动态查找表的查找、 率地实现动态查找表的查找、插入和删除 的操作。 的操作。 本节重点介绍二叉排序树。 本节重点介绍二叉排序树。
20
8.3.4 二叉排序树的删除
21
8.3.4 二叉排序树的删除
(3) 若结点 既有左子树 又有右子树 ,则首先找 若结点p既有左子树 又有右子树pR, 既有左子树pL又有右子树 到要删除结点的右子树中关键字值最小的结点(即右子 到要删除结点的右子树中关键字值最小的结点 即右子 树中最左端结点), 树中最左端结点 ,利用上面的方法将该结点从右子树 中删除,并用它取代要删除结点的位置, 中删除,并用它取代要删除结点的位置,这样处理的 结果一定能够保证删除结点后二叉排序树的性质不变, 结果一定能够保证删除结点后二叉排序树的性质不变, 如图8.9所示 所示。 如图 所示。
22
8.3.5 二叉排序树查找算法效率分析
23
8.4 哈希表查找
8.4.1 哈希表的概念 通过哈希函数H与给定值k的对应关系,计 算H(k),确定记录在表中的位置,从而达 到查找的目的,把这个函数H称作哈希函数 或哈希函数,这种查找方式称为哈希(Hash) 查找,通过H(k)计算出来的地址值称为哈 希地址。 根据哈希函数H计算各关键字的存储地址, 将关键字存储起来的查找表称为哈希表。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第八章查找
一、散列表的应用——通信录管理
1.题目要求:
采用散列表结构,实现通讯录管理。
要求采用散列表的拉链法解决冲突,设计实现通讯录管理中的插入、查找、删除、修改和遍历操作。
2.算法分析:
1.设计采用散列表的静态链地址法解决冲突。
线性数组链表的结构体代码如下:
typedef struct{
char key; /*键值*/
TXNode *next; /*链域*/
}TXhead;
它包含一个键值和一个链域的指针。
链域的结构体代码如下:
typedef struct Node{
char name[20];
int phone;
char addre[50];
char email[35]; /*记录信息*/
struct Node *adjv; /*邻域*/
}TXNode;
它包含联系人的姓名、电话、地址和E-mail的信息存储变量,以及下一个链域的指针。
2.设计功能流程如下:
1)启动运行程序显示菜单界面。
以菜单形式列出各个功能,及对应的序列关键字。
2)提示输入所要进行插入、查找、删除、修改或遍历操作的序号并进行确认。
3)进入所选功能模块根据提示做进一步插入、查找、删除、修改或遍历的操作,并进行确认。
4)确认后返回菜单界面,然后回到第二步或者选择退出,并进行确认。
5)退出程序。
二、查找综合练习
1.题目要求:
查找综合练习:
要求能实现顺序表的三种查找算法:顺序查找,二分查找,分块查找:
一.需要建立的函数:
1.为了实现顺序查找或者二分查找,输入元素建立线性表的函数create
2.顺序查找算法seqsearch
3.二分查找算法(必须对1建立的线性表先排序,再用二分查找)searchbin
4.为了实现分块查找,输入元素建立线性表的函数creatF
要求在输入元素过程中,同时要建立起索引表
5.分块查找算法blocksearch
二.运行时通过菜单来选择进行什么操作
============================
1:为顺序查找或二分查找建立线性表
2:顺序查找线性表
3:二分查找线性表
4:为分块查找建立线性表
5:分块查找线性表
6:退出系统
============================
2.算法分析:
1.定义线性表
typedef int KeyType;
typedef struct{
KeyType key;
}NodeType;
typedef NodeType SeqList[MAXSIZE+1];
2.分块查找时,定义的索引表
typedef struct
{
KeyType key;
int staddr; /* 表示开始地址 */
int len; /* 表示块长度 */
}indexlist;
typedef indexlist ID[MAXID+1];
3.建立分块查找时的线性表
假设以’#’作为线性表输入结束的标记,以’@’作为块结束的标记。
输入数据为ch while(输入不为#)
{ 假设每一块的初始最大值max为0
每一块的初始长度length为0
while(输入不为@)
{ 如果此次输入ch值比前一块的最大值小(即存储在in[k-1].key中的数值小),则提示用户重新输入;
否则的话, {判断此次输入是否比max大,如果比max大,则max=ch;
然后把此次输入值ch放在线性表中r[i].key=ch;
块的长度增加1;
线性表的长度也增加1;}
}
如果输入为@,表示一块已经结束。
此时把块中的最大值max赋值给id[k].key,length 赋值给块的长度id[k].len
再开始下一块的输入。
}
4.分块查找
可以先通过二分查找索引表id,确定元素所在的块。
再通过块的首地址id[k].staddr再去顺序扫描具体的某一块的所有元素。
三、二叉排序树
1.题目要求:
二叉排序树综合练习:
要求能实现二叉排序树的建立,显示,插入结点,删除结点,查找某个结点:
一.需要建立的函数必须有以下几个:
1.按照给定值在二叉排序树中查询searchnode
2.将一个元素插入到二叉排序树中insert
3.如果某个结点存在删除此结点delet
4.显示二叉树print
5.建立二叉树creat
二.运行时通过菜单来选择进行何种操作
============================
1.二叉排序树 --- 插入
2.二叉排序树 --- 查找
3.二叉排序树 --- 删除
4.二叉排序树 --- 显示
5.退出
============================
2.算法分析:
略。
四、散列文件
1.题目要求:
散列文件可以分为单元素散列文件和多元素散列文件(常称为按桶散列)文件两种,要求实现在单元素散列文件上的插入、删除和查找等操作。
实现在单元素散列文件上的算法:
1.初始化散列文件;
2.向散列文件中插入一个元素;
3.向散列文件中一次插入n个元素;
4.从散列文件中删除一个元素;
5.从散列文件中查找一个元素。
2.算法分析:
散列存储方法不仅适用于内存,也同样适用于外存,以散列方式存储的文件称为散列文件。
散列文件通常用链接法处理冲突,并且把保存每个单链表表头指针的表头向量用一个文件单独存储起来,称此为散列表文件,把所有单链表中的结点用一个文件单独存储起来,称此为散列主文件。
散列主文件中每个结点的类型定义为:
struct FLNode{
ElemType data;
int next;
};
其中data域用来存储待散列的元素,next域用来存储下一个同义词元素在散列主文件中的存储位置,即所在结点的位置序号。
散列主文件中第一个结点的位置序号为0,字节地址也为0,第二个结点的位置序号为1,字节地址为sizeof(FLNode),第三个结点的位置序号为2,字节地址为2*sizeof(FLNode),依此类推。
当一个结点为同义词单链表中的最后一个结点时,则next域为空,假定用-1表示。
散列表文件中每个结点的类型为整型,每个结点的值为指向对应单链表的第一个结点的位置序号,若相应的单链表不存在,则该结点的值为空,用数值-1表示。
散列表文件中每个结点的初始值均为-1。
初始化散列文件时,首先按输出方式打开并建立散列表文件和散列主文件,接着动态分配一个具有m+1个整数元素(m为散列表长度)的数组空间A,然后把A中的每一个元素均置为-1,表示空指针,最后把数组内容写入散列表文件即可。
向散列文件中插入一个元素x时,首先根据元素的关键字x.key、散列表长度m和所选择的任一散列函数计算出散列地址d,接着为x生成一个内存结点,并使该结点的next域为散列表文件中下标为d的元素值(即表头指针),然后把该内存结点的值写入到散列主文件的一个空闲结点中,若不存在经删除回收的空闲结点则写入到该文件的末尾,最后把该结点在散列主文件中的位置序号赋给散列表文件中下标为d的元素中,使得新插入的结点被链接到散列地址(即下标)为d的单链表的表头。
向散列文件中一次插入n个元素的操作,是上述插入一个元素过程的重复。
当然,文件的打开和关闭操作不需要重复。
从散列文件中删除一个关键字为x.key的元素时,首先计算出散列地址d,接着从散列表文件中下标为d的位置取出对应单链表的表头指针,然后遍历该单链表,从中查找出关键字为x.key的结点,并从单链表中删除该结点,其元素值由x带回,最后把被删除的结点链接到空闲结点表的表头。
假定空闲结点表的表头指针保存在散列表文件中下标为m的位置,若该位置为-1,则表明散列主文件中不存在空闲单元。
从散列文件中查找一个关键字为x.key的元素时,同样是首先计算出散列地址d ,接着从散列表文件中下标为d的位置取出对应单链表的表头指针,然后从单链表中顺序查找出对应的元素并由x带回。
例如,若采用散列文件散列存储线性表(18,75,60,43,54,90,46,31,58,73,15,34),假定采用的散列函数为h(k)=k%13,则得到的散列主文件和散列表文件的存储结构为:主文件
0 1 2 3 4 5 6 7 8 9 10 11
9。