第六讲 查找

第六讲 查找

相关定义

查找表由同一类型的数据构成的集合。由于集合中的数据元素之间存在着松散的关系,因此查找表是一种应用灵便的结构。
关键字:用来标识一个数据元素的某个数据项的值。
主关键字:可唯一的标识一个记录的关键字是主关键字。
次关键字:用以识别若干记录的关键字是次关键字。
查找的目的:
1,查询某个特定的数据元素是否在查找表中。
2,检索某个特定的数据元素的各种属性。
3,在查找表中插入一个数据元素。
4,删除查找表中某个数据元素。
查找表可分为两类:一类是静态查找表,只能做查询操作,一种是动态查找表,可以做删除与插入操作。

顺序查找

应用范围:顺序表,线性表表示的静态链表,表内元素无序。
从表尾开始比较,逐一比较

int search(int key)
{
	for(int i=n;i>=1;i--)
	{
		if(i<1)
		break;
		if(a[i]==key)
		{
			return i;
		}
	}
	return 0;
}

上面的代码是一般形式,每次循环都要比较两次,看看有没有越界,所以我们可以在a[0]设置一个哨兵,就可以不用判断是否越界了,相等就自动停下来了。下面是优化的代码

int search(int key)
{
	a[0]=key;
	for(int i=n;i>=0;i--)
	{
		if(a[i]==key)
		{
			return i;
		}
	}
}

ASL=(n+1)/2;
顺序查找算法简单,逻辑次序无要求,但是时间效率太低。

折半查找法

在这里插入图片描述
加入这样一组数,找21,我们把1设为左端,11设为右端,然后通过mid=(low+high)得到中间位置
在这里插入图片描述
与a[mid]做比较,如果想等就返回,如果小于就把寻找的区间缩短到左半边来寻找,如果大于就缩短到右半边寻找。此时21小于a[mid]
所有要缩短到右半边
在这里插入图片描述
此时low指向5,high指向37.
后面的过程也是这样的,直到找到,或者没找到返回0.

int search(int key)
{
	int low=1;
	int high=n;
	while(low<high)
	{
		int mid=(low+high)/2;
		if(key<a[mid])
			high=mid-1;
		else if(key>a[mid])
			low=mid+1;
		else return mid;
	}
	return 0;
}

折半查找法效率高,但是只适用于有序表,而且只能是顺序存储结构的表

二叉排序树

定义:
二叉排序树又称二叉搜索树,二叉查找树。
二叉排序树满足下面性质:
1,若其左子树非空,则左子树上所有结点的值均小于根结点的值;
2,若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
3,其左右子树本身又各是一棵二叉排序树。
如果中序遍历二叉排序树,那么得到的是从小到大的排列
二叉排序树的操作——查找
若查找的关键字等于根结点,成功,否则若小于根结点,查其左子树,若大于根结点查其右子树,左右子树上的操作类似。
在这里插入图片描述
上图就是查找的一个过程。
二叉排序树的递归查找算法思想:
若树为空则查找失败,返回空指针,若非空,将给定值key与根结点的关键字T->data进行比较,若等于则查找成功,返回根结点地址,若小于则进一步查找左子树,若大于则进一步查找右子树

tree search(tree T,int key)
{
	if(T==NULL||T->data==key)
	{
		return T;
	}
	else if(T->data>key)
	{
		return tree(T->left,key);
	}
	else
	return tree(T->right,key);
}

二叉排序树上查找某关键字等于给定值的结点过程,其实就是走了一条从根到该结点的路径。
比较的关键字的次数=此节点所在层数
最多的比较次数=树的深度
二叉排序树的操作——插入
过程:若二叉排序树为空,则插入结点作为根结点插入到空树中,否则,继续在左右子树上查找,树里面已经有了则不能再插,查找直到某个叶子结点的左子树或右子树为空为止,则插入结点应为这个叶子结点的左孩子或右孩子,插入的元素一定在叶子结点上。
写一下伪代码

Status charu(tree t,int key)
{
	tree p,s;
	if(查找不成功)
	{	
		s=(tree)malloc(sizeof(tree));
		s->date=key;
		s->left=s->right=NULL;
		if(为空树)
		{
			t=s;
		}
		else if(查找到当前结点的左子树为空)
		{
			p->left=s;
		}
		else
		p->right=s;
		return true;
	}
	else
	return false
	
}

通过二叉排序树的插入操作,我们也可以将一个序列生成二叉排序树
二叉排序树的操作——删除
删去一个结点不能把以该节点为跟的子树都删去,只能删去该结点,并且还要保证删除后的二叉树还是二叉排序树。删除的过程中还要避免高度增加。
删除分三种情况考虑
1,被删去的结点是叶子结点,那直接删去该结点就行了
2,被删除的结点只有左子树或者只有右子树,那么可以用其左子树或者右子树代替
在这里插入图片描述
3,被删去的结点既有左子树又有右子树
有两种解决方式,第一种是以其中序前趋值替换,然后再删除该前趋结点,前趋是左子树中最大的结点。还有一种是以后继替换,然后删去后继结点,后继结点是右子树中最小的结点
在这里插入图片描述
在这里插入图片描述
上面这张图是包括了刚刚说的三种情况

平衡二叉树

因为二叉排序树的查找和二叉树形态有关系,所以就需要平衡二叉树,让结构更平衡。
平衡二叉树又称AVL树,性质是左子树和右子树的高度之差的绝对值小于等于1,左子树和右子树也是平衡二叉树。
为了方便,给每个结点附加一个数字,给出该结点左子树与右子树的高度差。这个数字称为结点的平衡因子,平衡因子=该结点左子树高度-该结点右子树高度。
失衡二叉树的调整:
当我们在插入一个结点的时候可能会导致失衡,所以有时候就需要我们调整,下图是失衡的四种类型,c是插入的结点,因为c的插入导致失衡
在这里插入图片描述
在学习调整之前,我们要先记住调整的原则,1,降低高度,2,保持二叉排序树性质
第一种,LL型调整过程,b结点带着左子树一起上升,a结点称为b结点的右孩子,原来b结点的右子树作为a结点的左子树。
在这里插入图片描述
第二种,RR型调整过程,b结点带着右子树一起上升,a结点成为b结点的左孩子,原来b结点的左子树作为a的右子树
在这里插入图片描述
第三种,LR型调整过程,c结点穿过a,b结点上升,b结点成为c的左孩子,a成为c的右孩子,原来c结点的左子树作为b的右子树,原来c的右子树作为a的左子树
在这里插入图片描述
第四种,RL型c调整过程,结点穿过a,b结点上升,a结点成为c的左孩子,b成为c的右孩子,原来c结点的左子树作为a的右子树,原来c的右子树作为b的左子树
在这里插入图片描述
上面就是几种调整方式。

散列表查找

基本思想:记录的存储位置与关键字之间存在对应关系——hash函数
查找步骤:
1,在存储时,通过散列函数计算记录的散列地址,并按此散列地址存储该记录。
2,当查找记录时,我们通过同样的散列函数计算记录的散列地址。
所以说,散列技术既是一种存储方法,也是一种查找方法
一些术语:
1,散列方法:选取某个函数,依该函数按关键字计算元素的存储位置,并按此存放,查找时由同一个函数对给定k计算地址。
2,散列函数:散列方法中使用的转换函数。
3,散列表:按上述思想构造的查找表。
4,冲突:不同的关键码映射到同一个散列地址。
5,同义词:具有相同函数值的多个关键字。
构造散列函数要考虑的因素:
1,执行速度
2,关键字长度
3,散列表的大小
4,关键字分布情况
5,查找频率

构造散列表的方法
方法一:直接定址法
Hash(key)=a*key+b;(ab为常数)
优点:以关键码的某个线性函数值为散列地址,不会冲突
缺点:要占用连续地址空间,空间效率低
方法二:除留余数法
Hash(key)=key mod p(p是一个整数)
取p为小于等于表长的质数。

处理冲突的方法
1,开放定址法
基本思想:有冲突时就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将数据元素存入
例如:Hi=(Hash(key)+d) mod m;
常用方法:
线性探测法 d 为1,2,3……m-1线性序列,一旦冲突就寻找下一个地址
二次探测法 d为11,22,……二次序列
伪随机探测法 d为随机数序列
一个线性探测法的例子
在这里插入图片描述

2,链式地址法
基本思想:相同散列地址的记录链成一单链表,m个散列地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态结构。
下图是一个例子
在这里插入图片描述
优点是不会冲突产生聚集现象,也适合不确定长度的那种。

散列表的查找
在这里插入图片描述
几点结论:
散列表技术具有很好的平均性能,链地址法优于开地址法,除留余数法优于其它函数。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值