DS博客作业05--查找

| 这个作业属于哪个班级 | 数据结构2011/2012 |
| ---- | ---- | ---- |
| 这个作业的地址 | DS博客作业05--查找 |
| 这个作业的目标 | 学习查找的相关结构。 |
| 姓名 |骆念念|

0.PTA得分截图

查找题目集总得分,请截图,截图中必须有自己名字。题目至少完成总题数的2/3,否则本次作业最高分5分。没有全部做完扣1分。

1.本周学习总结(0-5分)

1.1 查找的性能指标

ASL成功、不成功,比较次数,移动次数、时间复杂度
什么是ASL
关键字的平均比较次数,也称平均搜索长度
ASL成功
找到T中任一记录平均需要的关键字比较次数
ASL不成功
在T中任一记录找不到的关键字比较次数

1.2 静态查找

分析静态查找几种算法包括:顺序查找、二分查找的成功ASL和不成功ASL。
顺序查找概念
从顺序表的一端开始,顺序扫描,依次将扫描到的关键字与给定值k相比较,若相等,则成功,若扫描结束,仍没有找到与给定值k相等,则查找失败。
顺序查找时间复杂度
O(n)
顺序查找的ASL
成功时:(n+1)/2 不成功时:n
二分查找概念
也叫折半查找,要求线性表必须是递增或递减。去长度的一半为中点,将给定值k与中点值比较,若相等,即找到,若小于,则在中点的左区域找,若大于,在右区域找
二分查找的时间复杂度
O(log2n)
二分查找的ASL
成功:在二分查找判定树中,Σ(圆形节点(i=1,2...)高度)/圆形节点个数*
不成功:在二分查找判定树中,Σ(矩形节点(i=1,2...)高度)/矩形节点个数*

1.3 二叉搜索树

1.3.1 如何构建二叉搜索树(操作)

结合一组数据介绍构建过程,及二叉搜索树的ASL成功和不成功的计算方法。
如何在二叉搜索树做插入、删除。


建造原则
依次插入数据时,要满足左子树的节点值小于根,右子树的节点值大于根。
插入
*插入的三种情况
1.树为空,则创建一个key域为k的节点
2.树不为空,则将k与关键字比较,两者相等,不插入
3.k大于关键字,则插入右子树
4.k小于关键字,则插入左子树
*
注意
插入必须是插入在叶子节点的,并且插入的过程是边插入边查找
删除
删除的三种情况
1.被删除的节点是叶子节点,直接删除该节点
2.被删除的节点只有左子树或者只有右子树,用其左子树或右子树代替它
3.被删除的节点有左子树和右子树,用左子树中最大的节点替代它,并删除左子树最大的节点,或者用右子树中最小的节点替代它,并且删除右子树最小的节点

1.3.2 如何构建二叉搜索树(代码)

1.如何构建、插入、删除及代码。
2.分析代码的时间复杂度
3.为什么要用递归实现插入、删除?递归优势体现在代码哪里?

1.

//结构体
typedef struct node
{
   KeyType key;
   InfoType data;
   struct node*lchild,*rchild;
}BSTNode,*BSTree;
//插入
int InsertBST(BSTree &p,KeyType k)
{
   if(p=NULL)
    {p=new BSTNode;
     p->key=k;
     p->lchild=p->rchild=NULL;
     return 1;
    }
   else if(k==p->key0
   return 0;
   else if(k<p->key)
   return InsertBST(p->lchild,k);
   else
   return InsertBST(p->rchild,k);
}
//构建
BSTNode *Creat(KeyType A[],int n)
{
   BSTNode *bt=NULL;
   int i=0;
   while(i<n)
    {
     InsertBST(bt,A[i]);
     i++;
     }
    return bt;
}
//删除
int DeleteBST(BSTree &bt,KeyType k)
{
   if(bt==NULL)
   return 0;
   else
   {
     if(k<p->key)
     return DeleteBST(bt->lchild,k);
     else if(k>bt->key0
     return DeleteBST(bt->rchild,k);
     else
     {
      Delete(bt);
      return 1;
     }
    }
}

O(n)
3.递归才能把节点父子关系保留

1.4 AVL树

AVL树解决什么问题,其特点是什么?
AVL树能提高查找xiaolv
特点
1.本身首先是一棵二叉搜索树。
2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。
也就是说,AVL树,本质上是带了平衡功能的二叉查找树(二叉排序树,二叉搜索树)。
结合一组数组,介绍AVL树的4种调整做法。
LL调整

RR调整

LR调整

RL调整

AVL树的高度和树的总节点数n的关系?
N(1)=1,N(2)=2,N(h)=N(h-1)+N(h-2)+1
介绍基于AVL树结构实现的STL容器map的特点、用法。

1.5 B-树和B+树

B-树和AVL树区别,其要解决什么问题?
B-树定义。结合数据介绍B-树的插入、删除的操作,尤其是节点的合并、分裂的情况
B树的定义
1、根结点至少有两个子女;
2、每个非根节点所包含的关键字个数 j 满足:┌m/2┐ - 1 <= j <= m - 1;
3、除根结点以外的所有结点(不包括叶子结点)的度数正好是关键字总数加1,故内部子树个数 k 满足:┌m/2┐ <= k <= m ;
4、所有的叶子结点都位于同一层。
在B-树中,每个结点中关键字从小到大排列,并且当该结点的孩子是非叶子结点时,该k-1个关键字正好是k个孩子包含的关键字的值域的分划。
B-树的插入
插入操作是指插入一条记录,即(key, value)的键值对。如果B树中已存在需要插入的键值对,则用需要插入的value替换旧的value。若B树不存在这个key,则一定是在叶子结点中进行插入操作。
1)根据要插入的key的值,找到叶子结点并插入。
2)判断当前结点key的个数是否小于等于m-1,若满足则结束,否则进行第3步。
3)以结点中间的key为中心分裂成左右两部分,然后将这个中间的key插入到父结点中,这个key的左子树指向分裂后的左半部分,这个key的右子支指向分裂后的右半部分,然后将当前结点指向父结点,继续进行第3步。
B-树的删除
删除操作是指,根据key删除记录,如果B树中的记录中不存对应key的记录,则删除失败。
1)如果当前需要删除的key位于非叶子结点上,则用后继key(这里的后继key均指后继记录的意思)覆盖要删除的key,然后在后继key所在的子支中删除该后继key。此时后继key一定位于叶子结点上,这个过程和二叉搜索树删除结点的方式类似。删除这个记录后执行第2步
2)该结点key个数大于等于Math.ceil(m/2)-1,结束删除操作,否则执行第3步。
3)如果兄弟结点key个数大于Math.ceil(m/2)-1,则父结点中的key下移到该结点,兄弟结点中的一个key上移,删除操作结束。
否则,将父结点中的key下移与当前结点及它的兄弟结点中的key合并,形成一个新的结点。原父结点中的key的两个孩子指针就变成了一个孩子指针,指向这个新结点。然后当前结点的指针指向父结点,重复上第2步。
有些结点它可能即有左兄弟,又有右兄弟,那么我们任意选择一个兄弟结点进行操作即可。

B+树定义,其要解决问题
B+树的特点是能够保持数据稳定有序,其插入与修改拥有较稳定的对数时间复杂度。B+树元素自底向上插入。
(1)每个结点至多有m个子女;
(2)除根结点外,每个结点至少有[m/2]个子女,根结点至少有两个子女;
(3)有k个子女的结点必有k个关键字。
B+树的查找与B树不同,当索引部分某个结点的关键字与所查的关键字相等时,并不停止查找,应继续沿着这个关键字左边的指针向下,一直查到该关键字所在的叶子结点为止

1.6 散列查找。

哈希表的设计主要涉及哪几个内容?
哈希表定义
哈希表(Hash table,也叫散列表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
哈希冲突:对于两个关键字分别为ki和kj(i≠j)的记录,有ki≠kj,但h(ki)=h(kj)。把这种现象叫做哈希冲突
装填因子:装填因子α=存储的记录个数/哈希表的大小=n/m
α越小,冲突的可能性就越小; α越大(最大可取1),冲突的可能性就越大
哈希表构造方法
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.数字分析法
数字分析法是取数据元素关键字中某些取值较均匀的数字位作为哈希地址的方法。即当关键字的位数很多时,可以通过对关键字的各位进行分析,丢掉分布不均匀的位,作为哈希值。它只适合于所有关键字值已知的情况。通过分析分布情况把关键字取值区间转化为一个较小的关键字取值区间。
哈希表的冲突处理
(一)开放定址法:冲突时找一个新的空闲的哈希地址
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)
(二)拉链法
拉链法是把所有的同义词用单链表链接起来的方法

结合数据介绍哈希表的构造及ASL成功、不成功的计算
举例:本例H(k)=k mod 13

成功ASL:探查次数恰好等于查找到该记录所需要的关键字比较次数,分母是给出的数据个数
ASL(success)=(2+1+1+1+1+4+1+1+1+1+1)/11=1.364
不成功ASL:分母是mod后的数字
ASL(unsuccess)=(2+1+10+9+8+7+6+5+4+3+2+1+3)/13=4.692
结合数据介绍哈希链的构造及ASL成功、不成功的计算
举例:本例H(k)=k mod 7

成功ASL:成功找到第1层的结点,均需要1次关键字比较;成功找到第2层的结点,均需要2次关键字比较;分母为数据个数
ASL(success)=(15+21)/6=1.17
不成功ASL:有1个结点的单链表,不成功查找需要1次关键字比较;有2个结点的单链表,不成功查找需要2次关键字比较;分母为mod后的数字
ASL(不成功)=(14+21)/7=0.86

2.PTA题目介绍(0--5分)

介绍3题PTA题目

2.1 是否完全二叉搜索树(2分)

本题务必结合完全二叉搜索树经过层次遍历后在队列的特点去设计实现。结合图形介绍。

2.1.1 伪代码(贴代码,本题0分)

伪代码为思路总结,不是简单翻译代码。
思路:按照层次顺序遍历这颗树的过程中,对于任意一节点x:
如果x有右子树,没有左子树,这肯定不是完全二叉树
如果x有左子树,没有右子树或x左右子树都没有,那么剩余的所有节点一定要为叶子节点

输入结点数n;
定义根节点BinTree T = NULL; 
for(i=0 to n-1)
  输入a[i];
  调用T = Insert(T, a[i])函数将a[i]插入二叉搜索树;
  {
    if(空树)  a[i]直接作为根节点的值创建二叉树;
    else if(a[i]小于结点值)  递归将a[i]判断插入左子树BST->Left = Insert(T->Left, a[i]); 
    else if(a[i]大于结点值)  递归将a[i]判断插入右子树BST->Right = Insert(T->Right, a[i]);
    return T;//程序能运行到这条语句说明a[i]以插入树中或a[i]已存在,则不再重复插入
  }
end for
调用printBSTree(T)函数层序遍历输出二叉搜索树;//这步不重要,不进行详细说明
调用IsComplateTree(T)函数对二叉搜索树进行判断
{
  if(根节点不空)  入队根节点;
  初始化状态status=true;//如果status==false,则后续所有结点都只能是叶子结点
  while(队列不为空)
    出队队头结点t;
    if(t的左孩子不为空)
      if(status==false)     说明前面已经有某个结点只有左孩子没有右孩子或左、右孩子都没有,而且结点t不是叶子结点,不符合要求,return false;
      t的左孩子入队; 
    else  标记status=false;
    if(t的右孩子不为空)
      if(status==false)     说明前面已经有某个结点只有左孩子没有右孩子或左、右孩子都没有,而且结点t不是叶子结点,不符合要求,return false;
      t的右孩子入队;
    else  标记status=false;
  end while
  return true;//运行到这条语句说明所有结点均符合要求
}

2.1.2 提交列表

2.1.3 本题知识点

1.完全二叉树的概念:叶子只能出现在最下面的二层、最下层的叶子一定集中在左部的连续位置、倒数第二层若有叶子结点,一定在右部连续位置、如果结点的度为1 ,则该结点只有左孩子。
2.层序遍历:借助队列来进行层序遍历,每出队一个结点就将其左孩子和右孩子入队,直到队空为止。
3.二叉搜索树的创建:用递归对每个待插入的数据进行判断,如果比根节点小的话就将根节点的左孩子做新的根节点进行递归判断;如果比根节点大的话就将根节点的右孩子作为新的根节点进行递归判断;如果根节点为空的话则直接将该数据作为根节点的数据创建根节点。

2.2 航空公司VIP客户查询(2分)

本题结合哈希链结构设计实现。请务必自己写代码,学习如何建多条链写法。

2.2.1 伪代码(贴代码,本题0分)

伪代码为思路总结,不是简单翻译代码。
取身份证后三位做哈希地址,最后一位为x单独处理

void CreateHashChain(HashChain HC[], int n, int k)
初始化哈希链hc
for i=0 to n
	Insert(HC, k)插入关键字
end for

void Insert(HashChain HC[], int k)
输入ID和飞行里程
数据处理并计算哈希地址
while 遍历对应的单链表
	if 查找到对应的ID then break
	else p = p->next
end while
if 没查找到
	申请节点并复制
	头插法插入关键字
else
	累加飞行里程
end for

void Search(HashChain HC[], string ID)
计算哈希地址
while 遍历哈希地址对应的单链表
	if 找到 then 输出总飞行里程
	else p = p->next
end while
未找到,输出No Info

2.2.2 提交列表

2.2.3 本题知识点

1.练习哈希链的创建和查找,创建就是计算哈希地址,然后插入对应的单链表中;查找也得先计算哈希地址,然后在单链表中查找对应的信息
2.使用cin和cout比起使用scanf和pritf来输入输出,要更加耗时,在时间限制严格的题目中,应该使用scanf和pritf来输入输出
3.创建哈希链的过程中,应该注意不要让单链表的长度过长,否则查找和插入都将耗时过长
4.使用scanf来对string进行输入时,应先使用resize方法重新申请空间,因为默认申请的空间可能不足以容纳数据,比如该题的ID;同时语句的格式也应该注意,应该使用scanf("%s", &ID[0]);,而非scanf("%s", ID);,后者的格式无法输入

2.3 基于词频的文件相似度(1分)

本题设计一个倒排索引表结构实现(参考课件)。单词作为关键字。本题可结合多个stl容器编程实现,如map容器做关键字保存。每个单词对应的文档列表可以结合vector容器、list容器实现。

2.3.1 伪代码(贴代码,本题0分)

伪代码为思路总结,不是简单翻译代码。
设计思路:利用set集合来存放文档单词,这样可以保证不会插入重复数据;由于输入的文档数据可能会有换行,所以用getline()来一行一行输入数据;对于一行输入的数据,用string来存放,对单词的处理则是遍历string字符串,用isalpha()函数来排除非字母字符,先遍历该行字符串,遍历到非字母字符时停止,则该非字母前、单词首字母后的所有字符即为该单词;为方便后续对公共单词的判断,将所有单词一律转换为大写;对于公共单词的判断,通过遍历set集合,用string库中的count函数来判断一个文档中的单词是否存在于另一个文档中。
时间复杂度:O(n²)

2.3.2 提交列表

2.3.3 本题知识点

1.倒排索引表的构建:由于本题不需要考虑一个文档中存在多个重复单词,所以只需要用set集合来存放文档数据即可;否则可以定义一个二维数组string a[][]或动态数组vettora[]来存放文档信息。
对于字母与非字母的判断:如果是C语言的话,可以在头文件中加入<ctype.h>,如果是C++的话就在头文件中加入,然后用库中的函数isalpha即可快速判断字符是不是字母。
2.对于相同单词不同大小写的情况,这里可以统一转换为大写或小写,方便后续判断。
3.单词的提取:要从主串中提取子串单词,可以使用string库中的substr(子串起始位置,子串结尾位置)来提取单词,但需要知道单词的首字母位置和尾字母位置,所以需要加一条循环语句来找单词的尾字母位置。
4.对于相似单词的判断:可以用string库中的count函数来判断该单词在文档中出现过几次。因为本题不考虑一个文档中存在多个重复单词,所以doc[q].count[it]只会有两个值,如果是1的话说明文档q中存在单词it;如果是0的话说明文档q中不存在单词it。

posted @ 2021-06-14 16:07  i566  阅读(83)  评论(1编辑  收藏  举报