DS博客作业——查找
DS博客作业——查找------------恢复内容开始------------
0.PTA得分截图
查找题目集总得分,请截图,截图中必须有自己名字。题目至少完成总题数的2/3,否则本次作业最高分5分。没有全部做完扣1分。
1.本周学习总结(0-5分)
1.1 查找的性能指标
ASL成功、不成功,比较次数,移动次数、时间复杂度
ASL(Average Search Length)
即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数称为平均查找长度。一个算法的ASL越大,说明时间性能差,反之,时间性能好.
1.2 静态查找
分析静态查找几种算法包括:顺序查找、二分查找的成功ASL和不成功ASL。
顺序查找
按照序列原有顺序对数组进行遍历比较查询的基本查找算法,从表中的最后一个数据元素开始,逐个同记录的关键字做比较,如果匹配成功,则查找成功;反之,如果直到表中第一个关键字查找完也没有成功匹配,则查找失败。
ASL成功
、
ASL不成功
二分查找
二分查找,也称折半查找,在某些情况下相比于顺序查找,使用折半查找算法的效率更高。但是该算法的使用的前提是静态查找表中的数据必须是有序的。非有序表的查找表使用折半查找算法查找数据之前,需要首先对该表中的数据按照所查的关键字进行排序。
ASL成功
ASL成功=每一层节点数层次数的总和/总结点数
ASL不成功
ASL不成功=其他不存在于树中的每一层节点数(层次数-1)/总结点数
1.3 二叉搜索树
二叉排序树要么是空二叉树,要么具有如下特点:
二叉排序树中,如果其根结点有左子树,那么左子树上所有结点的值都小于根结点的值
二叉排序树中,如果其根结点有右子树,那么右子树上所有结点的值都大小根结点的值
二叉排序树的左右子树也要求都是二叉排序树
1.3.1 如何构建二叉搜索树(操作)
结合一组数据介绍构建过程,及二叉搜索树的ASL成功和不成功的计算方法。
以关键字集合为(5,2,1,6,7,4,8,3,9)的序列创建二叉树:
ASL成功
将每层的节点数和层数相乘并求其他的和再除以树的节点数。
ASL成功为:(1·1+2·2+3·3+2·4+1·5)/9=3
ASL不成功为:(1·2+4·3+1·4)/6=3
如何在二叉搜索树做插入、删除。
插入结点
若二叉树为空,则第一个插入的k新建一个结点,作为根结点;否则将k与根结点的Key域比较,若两者相等,则说明树中已有此结点,无需插入;若kkey,则将k插入根结点的左子树;否则将k插入根结点的右子树。
删除结点
没有左右子节点,可以直接删除
存在左节点或者右节点,删除后需要对子节点移动
同时存在左右子节点,不能简单的删除,但是可以通过和后继节点交换后转换为前两种情况
1.3.2 如何构建二叉搜索树(代码)
1.如何构建、插入、删除及代码。
构建
STNode* CreateBST(KeyType A[], int n)//创建二叉排序树
//返回BST树根结点指针
{
BSTNode* bt = NULL;//初始时bt为空树
int i = 0;
while (i < n)
{
InsertBST(bt, a[i]);//插入,构建二叉树的实质是连续向二叉树插入若干个元素的过程
i++;
}
return bt;//返回建立的二叉排序树的根指针
}
插入
bool InsertBST(BSTNode*& bt, KeyType k)
//在二叉排序树bt中插入一个关键字为k的结点,若插入成功返回真,否则返回假
{
if (bt == NULL)//原树为空,新插人的结点为根结点
{
bt = (BSTNode*)malloc(sizeof(BSTNode));
bt->key = k; bt->lchild - 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 DeleteBST(BSTNode& bt, KeyType k)//在bt中删除关键字为k的结点
{
if (bt == NULL)
return false//空树删除失败,返回假
else
{
if (k < bt->key)
return DeleteBST(bt->lchild, k);//递归在左子树中删除为k的结点
else if (k > bt->key)
return DeleteBST(bt->rchild, k);//递归在右子树中删除为k的结点
else//找到了要删除的结点bt
{
Delete(bt);//调用Delete(bt)函数删除结点bt
return true;//删除成功,返回真
}
}
}
void Delete(BSTNode*& p)//从二叉排序树中删除结点p
{
BSTNode* q;
if (p->rchild == NULL) //结点p没有右子树(含为叶子结点)的情况
{
q = p;
p = p->lchild;//用结点p的左孩子替代它
free(q);
}
else if (p->lchild = NULL) //结点口没有左子树的情况
{
q = р;
p = p->rchild;//用结点p的右孩子替代它
free(q);
}
else Delete(p, p->lchild);//结点p既有左子树又有右子树的情况
}
void Deletel(BSTNode* p, BSTNode*& r) //被删结点p有左、右子树,r指向其左孩子
{
BSTNode* q;
if (r->rchild != NULL)
Deletel(p, r->rchild);//递归找结点r的最右下结点
else//找到了最右下结点r(它没有右子树)
{
p->key = r->key;//将结点r的值存放到结点p中(结点值替代)
P->data = r->data;
q = r;//删除结点r
r = r->lchild;//即用结点r的左孩子替代它
free(q);//释放结点a的空间
}
}
二叉树删除结点的顺序也是要先查找,再删除,与插入结点类似,二叉排序树插入时间复杂度最大为O(n)。若是二叉排序树比较平衡,其时间复杂度下降,最小的时间复杂度为O(logn)。
优势
删除和插入容易找到父亲和孩子
代码简洁,清楚,代码量小
1.4 AVL树
AVL树解决什么问题,其特点是什么?
由于二叉搜索树存在数据分配不平衡的现象,会导致查找时的时间复杂度提高,所以诞生了AVL解决此类问题。
特点
AVL树中任何结点的两个子树的高度最大差别为1 ,这样就克服了结点单边存储而导致查找效率低下的问题。即平衡因子=某一结点左右子树的高度差,所以平衡因子取值范围:-1 0 1
一般情况下,一颗平衡二叉树总是二叉排序树,因为脱离二叉排序树来讨论平衡二叉树是没有意义的,所以每个节点是平衡的。
AVL树的4种调整做法
AVL树的高度和树的总节点数n的关系
AVL树,它的特点是在二叉搜索树的基础上,要求每个节点的左子树和右子树的高度差至多为1。这个要求使AVL的高度h = log2 n.(n为树的结点个数)
基于AVL树结构实现的STL容器map的特点、用法
map可以将任何基本类型(包括STL容器)映射到任何基本类型(包括STL容器)。
特点:map提供关键字到值的映射 ,其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个称为该关键字的值。
用法:
begin() 返回指向map头部的迭代器
end() 返回指向map末尾的迭代器
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
lower_bound() 返回键值>=给定元素的第一个位置
upper_bound() 返回键值>给定元素的第一个位置
empty() 如果map为空则返回true
max_size() 返回可以容纳的最大元素个数
size() 返回map中元素的个数
clear() 删除所有元素
count() 返回指定元素出现的次数
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
swap() 交换两个map
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
value_comp() 返回比较元素value的函数
1.5 B-树和B+树
B-树和AVL树区别
B-树每个节点可以有多个关键字,而AVL树每个节点只能有一个关键字。同时,B-树可以有多个子树,而AVL树只能最多有两个子树。
B-树是一棵多叉平衡搜索树,旨在比AVL树能够拥有更低的树高,提高查找的效率,但是同AVL树一样,面对插入和删除数据的操作后需要维持平衡,这可能带来一些得不偿失的情况。其次B-树可以被采用在外存的数据查询上,因为树高比较低,这样就可以减少磁盘的I/O次数。
B-树定义。结合数据介绍B-树的插入、删除的操作,尤其是节点的合并、分裂的情况
定义
一棵m阶B-树或者是一棵空树,或者满足一下要求的树就是B-树
每个结点之多m个孩子节点(至多有m-1个关键字);
除根节点外,其他结点至少有⌈m/2⌉个孩子节点(至少有⌈m/2⌉-1个关键字);
若根节点不是叶子结点,根节点至少两个孩子
B+树定义
B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
1.6 散列查找。
散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,使得每个关键字key对应一个存储位置f(key)。建立了关键字与存储位置的映射关系
哈希表的设计主要涉及哪几个内容?
如果有一种查找方法,我们能一下子就知道其存储位置,并能进行快速地访问,那么时间复杂度就是 O(1),这样查找的效率就太高了。能够实现这种效果的查找成为散列,所谓散列技术就是数据保存的存储位置和关键字之间存在一个映射关系,我们称之为散列函数,这使得关键字可以和存储位置直接对应起来。通过散列技术将数据存储起来的所建成的存储结构称之为哈希表(Hash table)。
例:设一组初始记录关键字集合为(25,10,8,27,32,68),散列表的长度为8,散列函数H(k)=kmod7,要求分别用线性探测和链地址法作为解决冲突的方法设计哈希表。
2.PTA题目介绍(0--5分)
介绍3题PTA题目
2.1 是否完全二叉搜索树(2分)
2.1.1 伪代码(贴代码,本题0分)
伪代码为思路总结,不是简单翻译代码。
bool disp(BinTree T)
{
int flag = 1
queue<BinTree>qu
BinTree p
if (T != NULL)
qu.push(T)
end if
p = qu.front()
if (p != NULL)
cout << p->Data
end if
qu.pop()
if (p->Left)
qu.push(p->Left)
end if
if (p->Right)
qu.push(p->Right)
end if
while (!qu.empty())
p = qu.front()
if (p != NULL)
cout<<" " << p->Data
end if
qu.pop()
if (p->Left)
qu.push(p->Left)
end if
if (p->Right)
qu.push(p->Right)
end if
if (p->Left&&!p->Right)
flag=0
end if
if (p->Right && !p->Left)
flag = 0
end if
end while
if (flag == 1)
return true
else
return false
end if
}
2.1.2 提交列表
2.1.3 本题知识点
建树,树的插入,树的层次遍历,队列的使用
2.2 航空公司VIP客户查询(2分)
2.2.1 伪代码(贴代码,本题0分)
伪代码为思路总结,不是简单翻译代码。
定义Hash类型变量H构建哈希链;
定义List 类型变量ptr存储查找到的结点;
调用createHash函数构造哈希链H;
for (inti= 0;i <数据个数; i+ +) do
输入身份证信息和里程数;
if (里程数<最小里程数)
里程数=最小里程数;
end if
调用Insert函数向H插入结点;
end for
for (inti= 0;i <查找次数; i++) do
调用findKey函数在H中查找数据,将查找结果赋值给ptr;
if (ptr == NULL) do
查找失败;
else
查找成功;
end if
end for
2.2.2 提交列表
2.2.3 本题知识点
哈希链的创建,初始化,查找和插入
2.3 基于词频的文件相似度(1分)
本题设计一个倒排索引表结构实现(参考课件)。单词作为关键字。本题可结合多个stl容器编程实现,如map容器做关键字保存。每个单词对应的文档列表可以结合vector容器、list容器实现。
2.3.1 伪代码(贴代码,本题0分)
伪代码为思路总结,不是简单翻译代码。
定义变量类型为<string,int[]>的map容器构建单词索引表
根据单词在单词索引表中构建映射
for i=0 to 文件组数
输入文件编号
for iterator=map.begin() to !map.end()遍历单词
if 单词在两个文件都出现过
修正重复单词数,合计单词数
else if 单词在其中一个文件中出现过
修正合计单词数
end if
end for
end for
计算输出
2.3.2 提交列表
2.3.3 本题知识点
字符串的遍历,string的使用