DS博客作业05--查找
|这个作业属于哪个班级|数据结构--网络2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | DS博客作业05--查找 |
| 这个作业的目标 |学习查找的相关结构 |
| 姓名 | 陈佳桐 |
0.PTA得分截图
1.本周学习总结(0-5分)
1.1 查找的性能指标
ASL定义:
即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数称为平均查找长度。
ASL成功--找到T中任一记录平均需要的关键字比较次数
ASL不成功--未找到T中任一记录平均需要的关键字比较次数
时间复杂度 :根据查找算法而不同。
顺序查找 : 时间复杂度O(N)
分块查找 : 时间复杂度O(logN+N/m);
二分查找 : 时间复杂度O(logN)
哈希查找 : 时间复杂度O(1)
1.2 静态查找
1.2.1顺序查找:
思路:从表的一端开始,顺序扫描线性表,依次将扫描到的关键字和给定k值比较,若k值与关键字相等则查找成功,若扫描结束后,未找到相等记录,则查找失败。
int SeqSearch(RecTypr R[], int n, KeyType k)
{
int i = 0;
while (i < n && R[i].key != k)//从表头往后找
i++;
if (i >= n) //未找到返回0
return 0;
else
return i + 1;//找到后返回逻辑序号i+1
}
时间复杂度————O(n)
成功时的平均查找长度:
对于每个查找元素进行从头按顺序进行查找,ci为第i个所查找元素的比较次数
不成功时的平均查找长度:
查找不存在于顺序表中的元素时,每个元素都需遍历完整个顺序表来确定不在表中。
ASL=n
1.2.2二分查找:
二分查找,也称折半查找,此查找要求表中关键字的排序是有序的状态(递增或递减)
思路:
对于有序数组R。通过low和high两个坐标变量确认区间的左右端点。
在R[low]~R[high]中比对查找输入值k,和区间中间位置的元素R[mid] (mid=(low+high)/2)作比较,如果相等则直接return mid+1,结束算法;
如果k值大于则修改区间改为low=mid+1(降序数列时改为:high=mid-1);
如果k值小于也修改区间high=mid-1(数组为降序数列:low=mid+1);
直到low>high(区间中不存在元素)结束算法时间复杂度————O(log2n)
int BinSearch(SeqList R, int n, KeyType k)
{
int low = 0, high = n - 1, mid;
while (low <= high) //当前区间存在元素时循环
{
mid = (low + high) / 2;
if (k == R[mid].key) //查找成功
return mid + 1;
if (k < R[mid].key)
high = mid - 1;
else
low = mid + 1;
}
return 0;
}
ASL: 对二分查找的性能分析使用判定树
判定树(比较树):
把当前查找区间的中间位置上的记录作为根;
左子表和右子表中的记录分别作为根的左子树和右子树;
成功ASL
不成功ASL。
1.3 二叉搜索树
1.3.1 如何构建二叉搜索树(操作)
结合一组数据介绍构建过程,及二叉搜索树的ASL成功和不成功的计算方法。
如何在二叉搜索树做插入、删除。
1.3.2 如何构建二叉搜索树(代码)
1.构建、插入、删除代码。
构建——————时间复杂度为O(n)
思路 :采用边查找边插入的方式,
在查找过程中,当树中不存在关键字key等于给定值的结点k时再进行插入。
bool InsertBST(BSTNode*& bt, KeyType k)
{
if (bt == NULL)
{
bt = new BSTNode;
bt->key = k;
bt->lchid = bt->rchild = NULL;
return true;
}
else if (k == bt->key)
return false;
else if (k < bt->key)
return InsertBST(bt->lchild, k);
else
return InsertBST(bt->rchild, k);
}
删除——————最大时间复杂度为O(n),最小的时间复杂度为O(logn)
思路:二叉树删除结点的顺序也是要先查找,再删除
bool deletek(BSTNode*& bt, KeyType k)
{
if (bt != NULL)
{
if (k == bt->key)
{
delete(bt);
return true;
}
else if (k < bt->key)
delete(bt->lchild, k);
else
delete(bt->rchild, k);
}
}
查找——————时间复杂度为O(n)
BSTNode* SearchNode(BSTree bt, KeyType k)
{
if (bt == NULL || bt->key == k)
return bt;
else if (bt->key < k)
return SearchNode(bt->rchild, k);
else
return SearchNode(bt->child, k);
}
在树的遍历运算中,递归的实现简单,且时间复杂度更低。
1.4 AVL树平衡旋转
AVL也就是平衡二叉树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
1.LL型平衡旋转
在原结点的左孩子的左子树上插入结点,使得原结点的平衡因子由1变为2而引起的不平衡。需要进行一次顺时针旋转。
调整:将原结点的左孩子右上旋转作为A结点的根结点,原结点右下旋转作为根节点的右孩子,结点原右子树变为原结点的左子树。
2.RR型平衡旋转
在原结点的右孩子的右子树上插入结点,使得原结点的平衡因子由-1变为-2而引起的不平衡。需要进行一次逆时针旋转。
调整:将原结点的右孩子左上旋转作为A结点的根结点,原结点左下旋转作为根节点的左孩子,结点原左子树变为原结点的右子树。
3.LR型平衡旋转
这是因为在原结点的左孩子的右子树上插入结点,使得A结点的平衡因子由1增加至2而引起的不平衡。以插入的结点为旋转轴,先插入结点逆时针旋转,再原结点顺时针旋转。
调整:插入结点向上旋转到原结点的位置,原结点作为插入结点的右孩子,插入结点原左孩子作为原结点的右孩子,插入结点原右孩子作为原结点的左孩子。
4.RL型平衡旋转:
在原结点的右孩子的左子树上插入结点,使得原结点的平衡因子由-1增加至-2而引起的不平衡。以插入的结点为旋转轴,先顺时针旋转,再逆时针旋转。
调整:插入的节点向上旋转到原结点的位置,原结点作为插入结点的左孩子,插入结点原左孩子作为原结点的右孩子,插入结点原右孩子作为原结点右子树的左孩子。
高度为 h 的 AVL 树,节点数 N 最多2^h − 1; 最少N(h)=N(h− 1) +N(h− 2) + 1
map是STL的一个关联容器,它提供一对一(第一个称为关键字,第二个称为该关键字的值)的数据处理能力。map内部数据的组织,map内部自建一颗红黑树。
使用map:map对象是模板类,需要关键字和存储对象两个模板参数:
std:map<int,string> personnel
1.5 B-树和B+树
B-树和B+树一个节点可以放多个关键字,降低树的高度,适合大数据量查找。
B-树:
1.树中每个结点最多有m个子树(相对应的也就最多只能有m-1个关键字)
2.如果根结点不是叶子结点,则根节点最少有两棵子树。
3.对于所有的分支结点,最少要有【m/2】(取大于m/2的最小整数)。
4.所有的外部节点都在同一层上,且不带信息。
5.只支持随机查找。
插入操作:
往B-树中插入一个结点,插入位置必定在叶子结点层,但是因为B-树中对关键字个数有限制,所以,插入情况分为以下两种:
1.关键字个数n<m-1,不用调整
2.关键字个数n=m-1,进行分裂;如果分裂完之后,中间关键字进入的结点中关键字个数达到n=m-1,就继续进行分裂
删除操作:
非叶子结点删除:
1.从Pi子树节点借调最大或者最小关键字key代替删除结点;
2.pi子树中删除key;
3.若子树节点关键字个数 < m/2-1,重复步骤1;
4.若删除关键字为叶子结点层,按叶子结点删除操作。
叶子结点删除:
情况1:结点b关键字个数 > m/2-1, 直接删除;
情况2:结点b关键字个数 = m/2-1, 兄弟结点关键字个数大于 m/2-1,可以向兄弟结点借一个关键字,然后进行以下步骤;
A:兄弟结点最小关键字上移至双亲;
B:双亲节点大于删除关键字的关键字下移至删除结点。
情况3:结点b关键字个数 = m/2-1,兄弟结点关键字个数 = m/2-1,兄弟结点没有关键字时,进行借关键字操作
A:删除关键字
B:兄弟结点及删除结点,双亲节点中分割二者的关键字合并成一个新的叶子结点
C:若双亲节点的关键字个数 < m/2-1 ,则再进行b操作
b+树:
定义要求:
1.每个分支节点至多有m棵子树
2.根节点或者没有子树,或者至少有两棵子树
3.除根节点,其他每个分支节点至少有「m/2]棵子树
4.有n棵子树的节点有n个关键字。
5.所有叶子结点包含全部关键字及指向相应记录的指针
a.叶子结点按关键字大小顺序链接
b.叶子结点时直接指向数据文件的记录
6.所有分支结点包含子节点最大关键字及指向子节点的指针
区别:
1.B-树的叶子结点不含任何信息,而B+树的叶子结点含信息(关键字及其记录等)。
2.B-树只能进行分区间查找,而B+树上可以有两种查找:顺序查找和分区间查找。
3.B-树上所有的非叶结点都满足有n个关键字的话有n+1棵子树,而B+树上所有的非叶结点含n个关键字的话只含n棵子树。这个不同引起了如下几点的不同:
a.B-树的非叶结点有n+1个查找区间,而B+树的却少了一个区间,这个区间恰好是最右边的区间(如果存在,这个区间所指的子树上的所有关键字的值都大于结点的所有关键字的值)。
b.在B-树上,除根的非叶结点的子树个数不能少于m/2取上界(这个值用lowbou表示),等价地,关键字的个数不能少于lowbou-1,但在B+树上这个关系发生了变化,除根的其他结点的子树个数同样不能少于lowbou,但关键字的个数却不能少于lowbou,而不是lowbou减一,这个会给B+树的一些算法的具体实现带来不同。
c.由于根结点至少需要两棵子树,因而B-树上的根结点的关键字可以只有一个,但是B+树不能,它至少要有两个关键字,这样它才可以有两棵子树(至于为什么根结点都需要两棵子树,是因为它们都是平衡的)。
1.6 散列查找。
定义:
哈希表又叫散列表,是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数
哈希表构造方法:
1.直接定址法
直接定址法是以关键字k本身或关键字加上某个数值常量c作为哈希地址的方法。
直接定址法的哈希函数h(k)为:h(k)=k+c
2.除留余数法
除留余数法是用关键字k除以某个不大于哈希表长度m的数p所得的余数作为哈希地址的方法。
哈希函数h(k)为:
h(k)=k mod p (mod为求余运算,p≤m)
p最好是质数(素数)。
3.数字分析法
数字分析法是取数据元素关键字中某些取值较均匀的数字位作为哈希地址的方法。即当关键字的位数很多时,可以通过对关键字的各位进行分析,丢掉分布不均匀的位,作为哈希值。它只适合于所有关键字值已知的情况。通过分析分布情况把关键字取值区间转化为一个较小的关键字取值区间。
哈希冲突的处理
A.开放定址法————冲突时找一个新的空闲的哈希地址
1.线性探测法
线性探查法的数学递推描述公式为:
d0=h(k)
di=(di-1+1) mod m (1≤i≤m-1)
2.平方探测法
平方探查法的数学描述公式为:
d0=h(k)
di=(d0± i2) mod m (1≤i≤m-1)
B.拉链法————把所有的同义词用单链表链接
优点:
处理冲突简单,无堆积现象,非同义词不会发生冲突;
结点空间时动态申请的,空间利用效率高;
在链表中,删除结点操作方便。
假设散列表的长度是13,三列函数为H(K) = k % 13,给定的关键字序列为{32, 14, 23, 01, 42, 20, 45, 27, 55, 24, 10, 53}。分别画出用线性探测法和拉链法解决冲突时构造的哈希表,并求出在等概率情况下,这两种方法的查找成功和查找不成功的平均查找长度。
查找成功时的查找次数等于插入元素时的比较次数, 查找成功的平均查找长度为:
ASL = (1+2+1+4+3+1+1+3+9+1+1+3)/12 = 2.5
查找成功时的查找次数:第n个位置不成功时的比较次数为:第n个位置到第1个没有数据位置的距离:如第0个位置取值为1,第11个位置取值为3,第12个位置取值2
查找不成功的平均查找次数为:
ASL = (1+13+12+11+10+9+8+7+6+5+4+3 + 2)/ 13 = 91/13
2.PTA题目介绍
2.1 是否完全二叉搜索树
2.1.1 伪代码
建立结构体
{
int data 文本
定义左子树 BinTree left
定义右子树 BinTree right
}
void InserBST(BinTree*& BST,KeyType k)//往二叉树中插入结点k
{
if(BST==NULL)
建立新结点;
else if(k<BST->key)
递归进入右子树;
else 递归进入左子树
}
//层次遍历二叉树并判断是否为完全二叉树
bool Level_DispBST(BinTree* BST)
{
int end 表示是否已经找到第一个叶子结点
int flag 进行判断
若根结点BST不为空,入队,并输出;
while(队不为空)
出队结点p;
if(结点p的左,右孩子不为空)
入队;
if(剩余节点都应该是叶子结点的情况下p为非叶子节点)
flag=flase;
if(结点p只有右孩子,没有左孩子不是完全二叉树)
flag=flase;
if(结点p为叶子结点或者为第一个只有左孩子的结点)
修改end
end while
return flag;
}
2.1.2 提交列表
2.1.3 本题知识点
1.树的层次遍历
2.队列的相关库函数:
判断队列是否为空qu.empty()
返回队列中第一个元素,即最后插入到队列中的那个元素qu.front()
移除队首元素qu.pop()
3.生成二叉搜索树
2.2 航空公司VIP客户查询
2.2.1 伪代码
定义long long型变量
len存里程
ID存身份证
定义哈希表链的结点指针 变量p
定义哈希表 数组Hash
输入用户数量N和最小里程k
while (N--)
do
输入ID
if (ID小于18位)
then getchar()
end if
输入len
if (len小于k)
then len=k
end if
sum=ID%100000
p指向下标sum的链表的第一个结点
while (p不为空)
do
寻找相同身份证号,找到则对里程进行修改
end while
if 找不到相同身份证
then
则新建一个结点,将结点插在下标sum链表头部
end if
end while
输入查询人数N
while (N--)
do
输入ID
if ID小于18位 then getchar() end if
sum=ID%100000
p指向下标sum的链表的第一个结点
while (p不为空)
do
寻找对应身份证号,找到输出里程并退出循环
end while
if (p为空)
then 输出找不到
end if
end while
2.2.2 提交列表
2.2.3 本题知识点
1.哈希链的创建和数据插入,数据查找
2.很大数字的数据处理和哈希函数的设计,哈希地址的计算
3.字符串函数的使用
2.3 基于词频的文件相似度
2.3.1 伪代码
void GetFile(FileTable& list, int number)
{
定义str数组;
int i;
输入第一个字符(即用于跳过非字母'\n');
while (非字母)
if (字符为结束符'#')
break;
end if
end while
while (字母)
if (小写字母)
if (单词中字母个数小于10)
将该字母存入数组str中;
end if
else if (大写字母)
if (单词中字母个数小于10)
转换成小写字母存入数组中;//str[i++]-'a'+'A'
继续输入字符(直到输入字符为非字母时跳出循环);
if (单词中字母个数小于3)
单词不符合题意,
delete word;
i++;
else
单词读入成功,将数组清零;
成功返回;
end if
end while
}
int main()
{
定义一个文件列表list保存所有文件;
初始化文件列表;
for i=0 to n
提取每个文件的内容;
end for
for i=0 to m
比较两个文件的单词数量大小,单词数量小的作为模板,计算两文件的重复率;
输出结果;
end for
}
2.3.2 提交列表
2.3.3 本题知识点
1.拉链法构造哈希表
2.头插法插入链表
3.map库的使用
4.容器迭代器的使用