查找

这个作业属于哪个班级 数据结构--网络2011/2012
这个作业的地址 DS博客作业05--查找
这个作业的目标 学习查找的相关结构
姓名 陈垚嘉

0.PTA得分截图

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

1.1 查找的性能指标

ASL(Average Search Length),即平均查找长度,在查找运算中,由于所费时间在关键字的比较上,所以把平均需要和待查找值比较的关键字次数称为平均查找长度。
它的定义是这样的:

其中n为查找表中元素个数,Pi为查找第i个元素的概率,通常假设每个元素查找概率相同,Pi=1/n,Ci是找到第i个元素的比较次数。
当然,有查找成功,就有查找不成功,即要查找元素不在查找表中。针对不同查找方式的查找成功与不成功,我接下来会说,这也是一我一开始有点乱的地方。
一个算法的ASL越大,说明时间性能差,反之,时间性能好,这也是显而易见的。

1.2 静态查找

静态查找表在查找的过程中不改变表的状态——不插不删。他适合用于不变动或不常变动的表的查找。

顺序查找:在顺序查找(Sequence Search)表中,查找方式为从头扫到尾,找到待查找元素即查找成功,若到尾部没有找到,说明查找失败。所以说,Ci(第i个元素的比较次数)在于这个元素在查找表中的位置,如第0号元素就需要比较一次,第一号元素比较2次......第n号元素要比较n+1次。所以Ci=i;所以

可以看出,顺序查找方法查找成功的平均 比较次数约为表长的一半。当待查找元素不在查找表中时,也就是扫描整个表都没有找到,即比较了n次,查找失败

二分查找又称折半查找:首先待查找表是有序表,这是折半查找的要求。在折半查找中,用二叉树描述查找过程,查找区间中间位置作为根,左子表为左子树,右子表为右子树,,因为这颗树也被成为判定树(decision tree)或比较树(Comparison tree)。查找方式为(找k),先与树根结点进行比较,若k小于根,则转向左子树继续比较,若k大于根,则转向右子树,递归进行上述过程,直到查找成功或查找失败。在n个元素的折半查找判定树中,由于关键字序列是用树构建的,所以查找路径实际为树中从根节点到被查结点的一条路径,因为比较次数刚好为该元素在树中的层数。所以

Pi为查找k的概率,level(Ki)为k对应内部结点的层次。而在这样的判定树中,会有n+!种查找失败的情况,因为将判定树构建为完全二叉树,又有n+1个外部结点(用Ei(0<=i<=n)表示),查找失败,即为从根结点到某个外部结点也没有找到,比较
次数为该内部结点的结点数个数之和,所以

1.3 二叉搜索树

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

什么是二叉查找树:
根节点的值大于其左子树中任意一个节点的值,小于其右节点中任意一节点的值,这一规则适用于二叉查找树中的每一个节点。
二叉搜索树的构造过程,也就是将节点不断插入到树中适当位置的过程。该操作过程,与查询节点元素的操作基本相同,不同之处在于:
查询节点过程是,比较元素值是否相等,相等则返回,不相等则判断大小情况,迭代查询左、右子树,直到找到相等的元素,或子节点为空,返回节点不存在
插入节点的过程是,比较元素值是否相等,相等则返回,表示已存在,不相等则判断大小情况,迭代查询左、右子树,直到找到相等的元素,或子节点为空,则将节点插入该空节点位置。
由此可知,单个节点的构造复杂度和查询复杂度相同,为O(log2N)~O(n)
删除复杂度
二叉搜索树的节点删除包括两个过程,查找和删除。查询的过程和查询复杂度已知,这里说明一下删除节点的过程。
节点的删除有以下三种情况:
待删除节点度为零;
待删除节点度为一;
待删除节点度为二。
第一种情况如下图所示,待删除节点值为 “6”,该节点无子树,删除后并不影响二叉搜索树的结构特性,可以直接删除。即二叉搜索树中待删除节点度为零时,该节点为叶子节点,可以直接删除;

第二种情况如下图所示,待删除节点值为 “7”,该节点有一个左子树,删除节点后,为了维持二叉搜索树结构特性,需要将左子树“上移”到删除的节点位置上。即二叉搜索树中待删除的节点度为一时,可以将待删除节点的左子树或右子树“上移”到删除节点位置上,以此来满足二叉搜索树的结构特性。

第三种情况如下图所示,待删除节点值为 “9”,该节点既有左子树,也有右子树,删除节点后,为了维持二叉搜索树的结构特性,需要从其左子树中选出一个最大值的节点,“上移”到删除的节点位置上。即二叉搜索树中待删除节点的度为二时,可以将待删除节点的左子树中的最大值节点“移动”到删除节点位置上,以此来满足二叉搜索树的结构特性

之前提到二叉搜索树中节点的删除操作,包括查询和删除两个过程,这里称删除节点后,维持二叉搜索树结构特性的操作为“稳定结构”操作,观察以上三种情况可知:
前两种情况下,删除节点后,“稳定结构”操作的复杂度都是常数级别,即整个的节点删除操作复杂度为O(log2n)~O(n)
第三种情况下,设删除的节点为 ,“稳定结构”操作需要查找 节点左子树中的最大值,也就是左子树中最“右”的叶子结点,即“稳定结构”操作其实也是一种内部的查询操作,所以整个的节点删除操作其实就是两个层次的查询操作,复杂度同为O(log2n)~O(n).

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

二叉搜索树的插入

typedef struct node \\结构体
{
KeyType key;
InfoType data;
struct node* left, * right;
}BstNode
bool InserBST(BSTNode*& bt, KeyType k)\\插入
{
if (bt == NULL)
{
	bt = (BSTNode*)malloc(sizrof(BSTNode));
		bt->key = k;
		bt->left = bt->right = NULL;
	return ture;
}
else if (k == bt->key)
return false;
else if
return InsertBST(bt->left,k);
else
return InsertBST(bt->right,k);
}

二叉搜索树的构建

BSTNode* CreateBST(KeyType a[], int n)
{
	BSTNode* bt = NULL;
	int i = 0;
	while (i < n) 
	{
		InsertBST(bt, a[i]);
		i++;
	}
	return bt;
}

二叉搜索树的删除

bool deletek(BSTNode*& bt, KeyType k)
{
	if (bt != NULL)
	{
		if (k == bt->key)
		{
			deletep(bt);
			return ture;
		}
		else if (k < bt->key)
			deletek(bt->left, k);
		else
			deletek(bt->right, k);
	}
	else
		return false;
}

优点:非常高效的方法,不太好理解,就把一棵树看成只有三个节点能相对简单些。

1.4 AVL树

AVL树: 最早的平衡二叉树之一。应用相对其他数据结构比较少。windows对进程地址空间的管理用到了AVL树。
特点:任何两个子树的高度差最大是1
如果在AVL树中进行插入或删除节点后,可能导致AVL树失去平衡。这种不平衡可能出现在下面四种情况中:
对a的左儿子的左子树进行一次插入。(LL)
对a的左儿子的右子树进行一次插入。(LR)
对a的右儿子的左子树进行一次插入。(RL)
对a的右儿子的右子树进行一次插入。(RR)

LL旋转
在左子树上插入左孩子导致AVL树失衡,"根的左子树的高度"比"根的右子树的高度"大2。针对该情况,我们需要进行单右旋转来完成对树的调整。

图中左边是旋转之前的树,右边是旋转之后的树。从中可以发现,旋转之后的树又变成了AVL树,而且该旋转只需要一次即可完成。

RR旋转
在右子树插入右孩子导致AVL失衡时,我们需要进行单左旋调整。旋转围绕最小失衡子树的根节点进行。

RL旋转
在右子树上插入左孩子导致AVL树失衡,此时我们需要进行先右旋后左旋的调整

LR旋转

STL总共实现了两种不同结构的管理式容器:树形结构和哈希结构。
树形结构的关联式容器主要有四种:map,set,multimap,multiset。 这四种容器的共同特点是:使用平衡搜索树(即红黑树)作为底层结果,容器中的元素是一个有序的序列。
map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
红黑树是一种二叉查找树,但在每个节点增加一个存储位表示节点的颜色,可以是红或黑(非红即黑)。
通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍,因此,红黑树是一种弱平衡二叉树,相对于要求严格的AVL树来说,它的旋转次数少,所以对于搜索,插入,删除操作较多的情况下,通常使用红黑树。

1.5 B-树和B+树

区别:平衡二叉树(AVL树):是一种二叉排序树(BST),其中每个节点的左子树和右子树的高度差至多等于1.也就是说AVL树的平衡因子只可能是1,0,-1。
多路查找树(B-树):是一种平衡的多路查找树,2-3树和2-3-4树都是B树的特例。节点最大的孩子称为B-树的阶(order),因此,2-3树是3阶B树,2-3-4树是4阶B树。

B-树定义:B-树的定义
节点的孩子节点的最大数称为阶用m表示
所有的叶子节点在同一层,并且不带信息
每个节点最多含有m颗子树,最多含有m-1个关键字
根节点不是终端节点那么根节点至少有两个子树
除根节点以外其他非叶子节点至少有m/2向上取整个子树
每个非叶子节点的结构为:n,p0,k1,p1,k2,p2,k3,p3…kn,pn
其中n为关键字个数 m/2-1<n<m-1,ki为关键字ki<ki+1,pi为该节点的孩子节点的指针pi所指的节点的、关键字大于等于ki小于ki+1,pn所指的关键字大于kn。
下面就是一棵B树:

插入关键字
向一棵与二叉查找树插入新结点一样,需要查找插入新关键字的叶结点的位置。如果待插入的关键字已经存在,则返回该关键字位置 (x, i),不用再插入。与二叉查找树不同的是,B树的插入不能随便新建叶结点,否则会导致违反B树性质,所以在已有叶结点中插入。但是如果插入叶结点 y 是满的(full),则需要按其中间关键字y.keyt被提升到 y 的父结点,以标识两棵新树的划分点。但是如果 y 的父结点也是满的,则其父结点也需要分裂,以此类推,最终满结点的分裂会沿着树向上传播。
上面过程可能需要一下一上两个操作过程:
1.自上而下查找插入叶结点位置;
2.自下而上分裂满结点。可以对该过程稍作修改,从树根到叶结点这个单程向下过程中将关键字插入B树中。为此,不是等到找出插入过程中实际要分裂的满结点时才做分裂,而是自上而下查找插入位置时,就分裂沿途遇到的每个满结点(包括叶结点),这样,当分裂一个满结点 y 时,可以保证它的父结点不是满的。分裂一个 t = 4 的结点 x 示意图如下:

删除关键字
删除是B树相对较难的操作过程。相较非叶(内部)结点,叶子结点的删除操作更加简单(需要考虑的情况更少)。因此对于非叶结点,我们将其与最接近的叶子结点交换关键字,于是删除对应的叶子结点即可。(最接近的叶子结点可以是被删除结点关键字最大的左叶关键字或者最小的右叶关键字)

接下来要删除关键字12

直接通过搜索获取位置,删除该关键字。检测到该结点的关键字数少于1([3/2]-1),需要调整。由于其左唯一兄弟的关键字数不多于1,该结点只能向左合并。

从父结点处下移分割两结点的关键字11,被删除结点向左合并,将被删除结点的关键字(此处无关键字)与子树(此处无子树)过继给左兄弟。检测到父结点关键字数少于1,父结点需要调整。

当前结点向右合并,父结点关键字14下移,将当前结点的关键字与子树过继给右兄弟。检测到父结点(根结点)为空,继续调整。

由于当前结点为根结点且无关键字,将根结点设置为当前结点的子结点,删除当前结点。删除结束。

m阶B+树
每个分支至多有m棵子树
根节点没有子树或者至少有两颗
除了根节点其他节点至少有m/2向上取整个子
有n棵子树的节点有n个关键字
叶子节点包含全部关键字,以及只想相应记录的指针,而叶子节点按关键字大小顺序链接( 每个叶子节点可以看成是一个基本索引块,它的指针指向数据文件中的记录)
所有分支节点(可以看成是索引的索引)中仅仅包含他的各个子节点中最大关键字以及指向子结点的指针。

解决问题
B+树的磁盘读写代价更低。B+树的内部结点并没有指向关键字具体信息的指针,其内部结点比B树小,盘块能容纳的结点中关键字数量更多,一次性读入内存中可以查找的关键字也就越多,相对的,IO读写次数也就降低了。而IO读写次数是影响索引检索效率的最大因素。B+树的查询效率更加稳定。

1.6 散列查找。

哈希表
根据设定的哈希函数 H(key)和所选中的处理冲突的方法,将一组关键字映射到一个有限的、地址连续的地址集 (区间) 上,并以关键字在地址集中的“映像”作为相应记录在表中的存储位置,如此构造所得的查找表称之为“哈希表”.

比方说
将关键字序列(7、8、30、11、18、9、14)散列存储到散列表中。H(key) = (keyx3) MOD 7。装填(载)因子为0.7

查找成功的平均查找长度= (1+1+1+1+3+3+2)/7 = 12/7
查找不成功的平均查找长度 = (3+2+1+2+1+5+4)/7 = 18/7
它的哈希链形式

查找成功的平均查找长度= (14+23)/7=10/7
查找不成功的平均查找长度 = (11+23)/7=1

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

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

如图 a) 所示是一棵完全二叉树,图 b) 由于最后一层的节点没有按照从左向右分布,因此只能算作是普通的二叉树。

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

定义一个二叉树T;
定义一个队列Q;
定义数组A;
If(树为空)返回真 end if;
定义节点p且指向树根节点;
初始化队列;
p入队;
While(队列不为空){
出队;
                  对头元素赋值给p;
                  If(p为空) break;end if
                  p的左子树入队;
                  p的右子树入队;
}end 
While(队列不为空){
出队;
                  队头指针指向p;
                  数值赋给数组A;
                  If(p)为空  返回错误;end if
}
If(数组为升序)
     返回真;
Else
     返回假;
End if

2.1.2 提交列表

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

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

定义链表;
定义哈希链;
构造哈希链表;
初始化哈希链表;
定义节点p;
链表第一个节点赋值给p;
While(p不为空&&关键字与p指向的数据域相等)
{
p指向p的后继;
      返回p;
}end
输入:查找关键字;
for(遍历变量i未超过客户数)
{
if(查找成功)
{
返回;
}
else
{
未找到;
}end 
if
}end for
End

2.2.2 提交列表

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

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

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

for i->num   //做单词个数的循环
输入 str
while str不为#   //本次的单词输入没有停止
if 是字母,单词长度<10
转换成统一字母大小写
end if
  else
     if长度<2
     加入map容器
     end if
  清空字符
  end else
输入新字符
end while
end for

//计算单词的重复率
for 从begin->end
if 是重复单词
num_count++//重复词汇++
num_sum++//总数++
end if
else if 非重复单词
总数++
end else
end for
输出单词重复率 cout/sum;

2.3.2 提交列表

2.3.3 本题知识点

本题使用的是map容器,总的感觉很方便,直接使用就可以
学习新的东西很好,但一时间没办法很熟悉。

posted @ 2021-06-15 18:58  山高念做垚  阅读(232)  评论(0编辑  收藏  举报