数据结构——查找

1.查找

改变查找表中数据元素之间的关系以至于提高查找效率,即设置面向查找操作的数据结构,查找结构。

查找表-静态/动态

查找表经常进行查找(查询和检索)、插入、删除三大操作

数据项(字段)-(主/次)关键码

数据元素(记录)-(主/次)关键字

2.顺序表查找

顺序查找

优化(在查找的终点设置哨兵):减少不必要的判断语句提高程序效率

3.有序表查找

折半查找/二分查找:只适用于可以1随机存取的有序的顺序表。

由折半查找的定义,每次把一个数组从中间结点分割时,总是把数组分为结点数相差最多不超过 1 的两个子数组(且总是同一边多一,取决于mid向上还是向下取整),从而使得对应的判定树的两棵子树高度差的绝对值不超过 1 ,所以其判定树应是平衡二叉树,有n+1个查找失败结点(不计数总高度)。更适用于静态查找表。

/* 折半查找 */
int Binary_Search(int *a,int n,int key)
{
	int low,high,mid;
	low=1;	/* 定义最低下标为记录首位 */
	high=n;	/* 定义最高下标为记录末位 */
	while(low<=high)
	{
		mid=(low+high)/2;	/* 折半 */
		
		if (key<a[mid])		/* 若查找值比中值小 */
			high=mid-1;		/* 最高下标调整到中位下标小一位 */
		else if (key>a[mid])/* 若查找值比中值大 */
			low=mid+1;		/* 最低下标调整到中位下标大一位 */
		else
		{
			return mid;		/* 若相等则说明mid即为查找到的位置 */
		}
	}
	return 0;
}

*插值查找和*斐波那契查找,时间复杂度都为log2n,它们的本质为分割点的选择。

分块查找,总元素数n,当块数和块内元素数等于n^0.5时,ASL最小,为n^0.5+1。

4.*线性索引查找

索引是把一个关键字与它对应的记录相关联的过程。

一个索引项至少包含关键字与对应的记录指针。

对于m阶B树,一个结点占用一个磁盘页块,包含m/2向下取整-1至m-1个数的索引项(关键字+记录指针)。

三种线性索引:稠密索引、分块索引、倒排索引。

5.二叉排序树

二叉排序树:为了提高查找、插入、删除关键词的速度。实现动态查找表的高效率。

/* 二叉树的二叉链表结点结构定义 */
typedef	struct BiTNode
{
	int	data;
	struct BiTNode* lchild, * rchild;
}BiTNode,*BiTree;

//查找操作使用递归实现,若存在,返回值为目标结点,否则为递归访问到的最后一个结点。
//插入操作使用查找函数的返回值来完成
//删除操作和查找操作一样使用递归实现,在查找到目标结点后分为3种情况处理

查找成功/失败的平均查找长度ASL:比较次数为给定值在二叉排序树中层数,故查找性能取决于二叉排序树的形状(不确定)。

插入删除平均执行时间o(log2n)。

最理想情况,第一层到倒二层为完全二叉树,此时ASL为o(log2n),AVL还不能完全满足此条件

最佳二叉排序树:每个关键字的概率和深度之积最小。

各关键字的查找概率相等时,最佳二叉排序树应是高度最小的二叉排序树,即上述最理想情况。

        构造过程:对各关键字按值从小到大排序,仿照折半查找的判定树构造二叉排序树。

大根堆:要求根结点的关键字值大于或等于左子女的关键字值,且大于或等于右子女的关键字值。

而二叉排序树要求根结点的关键字值大于左子女的关键字值,同时小于右子女的关键字值。

两者的交集是:根结点的关键字值大于左子女的关键字值。这意味着它是一棵左斜单支树,但大根堆要求是完全二叉树,因此最后得到的只能是两个结点的二叉排序树。

6.平衡二叉树

平衡因子BF为-1、0、1。

基本思想:在构建平衡二叉树的过程(插入删除)中检查是否平衡,并对最小不平衡子树进行调整。

//平衡二叉树
typedef	struct BiTNode
{
	int	data;
	int	bf;//平衡因子
	struct BiTNode* lchild, * rchild;
}BiTNode,*BiTree;

//最小不平衡子树T调整
//BF小于-1,左旋,大于1,右旋
//若BF小于-1,但是T的右子树BF等于1,即与其符号相反,则应先对T的右子树右旋,后左旋
//BF大于1,则看T的左子树BF符号并进行相应操作。

//插入操作通过递归实现,并实时对最小不平衡子树T调整。

//删除操作也是实时对最小不平衡子树T调整。
//删除中间结点则找中序遍历前驱or后继代替,
//删除叶子结点则最小不平衡子树T调整,可能会导致树高减少并在更上层出现新的最小不平衡子树T,继续调整。

查找、插入、删除时间复杂度o(log2n)

设n(h)为高度h的AVL的最小结点数,则有n(h) = n(h-1) + n(h-2) + 1

此时所有非叶节点的平衡因子BF都为1or-1。

在插入中若打破平衡则通过重新分裂和组合实时实现平衡,即动态树表

*红黑树(AVL进一步放宽条件):红黑树相关定理及其证明 - 未雨愁眸 - 博客园 (cnblogs.com)

7.多路查找树(B-树)

针对内存和外存之间的存取而专门设计。

一次磁盘的I/O操作即读取一个结点,一个结点对应一个或多个磁盘页块,增加每个结点可存储key的数量,可以减少树的高度,从而减少磁盘的I/O操作即读取的次数,降低磁盘访问时间。

参考下文总结的。

图解:什么是B树?(心中有 B 树,做人要虚心)一文读懂B-树 - 知乎 (zhihu.com)

B树的特性

0.元素个数+1=子树个数

1.根节点:非叶的根节点,至少两个子树。至多m个子树

2.非根分支节点:包含元素k-1和子树k:且k满足m/2向上取整 <= k <= m,而终端节点子树指针都为空,即没有子树,但是元素个数一样。终端结点指的是最底层的非叶节点,而不是叶子结点。

3.叶子结点:位于同一层,且不含任何信息(查找失败的结点,指向这些结点的指针为空)。

4.分支节点结构:

//(n,A0,K1,A1,K2,A2---)A代表指向子树根节点的指针,K代表关键字,n代表关键字的个数,范围是k-1的范围
//Ai-1都小于Ki    Ai都大于Ki

typedef struct BTNode
{
	int keynum; /*  结点中关键字个数,即结点的大小 */ 
	struct BTNode *parent; /*  指向双亲结点 */ 
	struct Node /*  结点向量类型 */ 
	{
		 int key; /*  关键字向量 */ 
		 struct BTNode *ptr; /*  子树指针向量 */ 
		 int recptr; /*  记录的指针向量 指向该关键字对应的记录的指针*/ 
	}node[m+1]; /*  key,recptr的0号单元未用 存储m个关键字,m+1个子树	子节点结构*/ 
}BTNode,*BTree; /*  B树结点和B树的类型 */ 

查找:在B树中找结点(磁盘),在结点中找关键字(内存)

插入:定位(利用查找算法找到最底层非叶结点,即插入位置),插入(判断关键字个数范围,超出则分裂)

        分裂:m/2向上取整位置的关键字(从1开始计)插入原结点的父结点,左半部分在原结点,右半部分在新结点,若父节点超出范围,则继续分裂,以此类推,若到了根结点,则B树高度+1。

删除:若删除关键字不在终端结点,则用中序遍历前驱or后继代替,转化为删除终端结点的问题。

1.直接删除:删除后关键字个数满足B树特性,即该结点删除前关键字个数大于m/2向上取整。

2.兄弟够借:左or右相邻兄弟借一个到父节点,父节点那个关键字(删除结点和兄弟结点之间的)下来代替删除关键字。

要求兄弟借一个后关键字个数满足B树特性,即兄弟删除前关键字个数大于m/2向上取整。

3.兄弟不够借:

将结点、左or右相邻兄弟、父节点那个关键字(删除结点和兄弟结点之间的)合并。

合并后相当于父节点删除一个关键字,则重复操作。

若父结点为根结点且减少为0,则合并的结点成为根。

有n个关键字的m阶B树一定有n+1个叶结点,叶节点不带信息,代表查找失败结点。

对“有n个关键字的m阶B树有n+1个叶结点”的理解_n个关键字的b树必有n+1个叶子结点_「江太白」的博客-CSDN博客

由此条件推出同样关键字的个数n,B树的最大高度h(即按照最小子树数存储时的h+1层的结点数应小于等于n+1,小于则代表在此时的高度h按照最小子树数存储时B树存储的关键字个数小于给定值n;若前者计算出来h小于等于非整数,由于所有结点的平衡因子都为0,则最大高度h向下取整,意味着有个别结点无法按照最小子树数存储,至此高度为h的B树的关键字个数为n,且h为最大高度)

最小高度公式:按照每一个结点都有m个子树标准得出:m^h-1>=n,解出h取最小整数

最大高度公式:按照每个结点都有m/2向上取整(除根节点外,根节点有2个子树)个子树的标准得出。即实际叶子节点数n+1>=(按照此标准的h+1层的结点数,即此标准的理论叶子结点数)

此时解出h小于某值,取最大整数。


 

*B+树、王道P290

每个关键字对应一棵子树。

B +树是应文件系统(数据库)所需而产生的 B 树的变形,前者比后者更加适用于实际应用中的操作系统的文件索引和数据库索引,因为前者的磁盘读写代价更低,查询效率更加稳定。

B+树比B树更适合做文件索引的原因_mine_song的博客-CSDN博客

8.散列表查找(哈希表)(重点)

通过关键字得到相应的记录的内存存储地址。addr = Hash(key)

散列技术既是存储方法,又是访问方法。

1.散列函数常用构造方法

(计算简单、散列地址分布均匀,定义域为全部关键字、值域为散列表大小或地址范围)

直接定址法、数字分析法、平方取中法、除留余数法、随机数法、折叠法、

2.散列的冲突处理方法(产生冲突的关键词是这个散列函数的同义词

开放定址法(线性探测法、平方探测法、双散列法、伪随机序列法)

再散列函数法、链地址法(拉链法)、公共溢出区法

聚集(堆积)是因选取不当的处理冲突的方法,而导致不同关键字的元素对同一散列地址进行争夺的现象。用线性再探测法时,容易引发聚集现象。

3.散列查找表的实现

散列表中删除一个记录,在拉链法情况下可以物理地删除。但在开放定址法情况下,不能物理地删除,只能做删除标记。该地址可能是该记录的同义词查找路径上的地址,物理地删除就中断了查找路径,因为查找时碰到空地址就认为是查找失败。

4.散列表查找的性能分析

即散列查找的平均查找长度影响因素:散列函数、冲突解决策略、散列表装填因子)

取决于装填因子

查找成功的分母是存入散列表的元素个数。

查找失败的分母是散列函数能够映射到哈希表中的位置的个数。

查找失败的平均查找长度有两个不同的角度:一,认为比较到空结点才算失败,所以比较次数等于冲突次数加 1 ;二,认为只有与关键字的比较才算比较次数。

装填因子也叫存储效率:关键字个数/表长,链地址法也是如此计算,代表每个链表的平均长度

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值