查找技术总结(数据结构C++,大二下写,初学者)

这段时间,我学到了查找技术和排序技术,这两种操作在许多程序中应用,例如大一写的核酸作业就用到了这两种技术。这两章主要是学习查找和排序的各种算法,拓宽我的思路,并且非常的实用,也非常的有趣。关于查找技术和排序技术的知识,我总结为三方面:课本上学到的知识、上机实现课本上的例子的过程所学到的知识和做课后题学到的知识和技巧。

查找技术:

查找,课本上给出的定义是在具有相同类型的记录中找出满足给定条件的记录。不难理解,查找是在相同类型的记录中进行匹配的过程。查找分为静态查找和动态查找,区别在于动态查找涉及插入和删除(即被查集合的元素会发生变化),而静态查找不涉及。

线性查找

顺序查找

首先我学到了线性查找(顺序查找),这种查找技术我在大一已经使用过了,该查找也是最简单的查找,其核心思想就是遍历并匹配。以前使用的顺序查找是在线性表中依次遍历每次都需要判断是否越界,现在我学到了可以设置“哨兵”来避免判断越界。具体实现就是将要查元素放在线性表的开头,然后从后往前遍历,此时不需要考虑越界,因为就算除开头外找不到,那在开头位置就会找到,返回0。由于该查找算法过于简单,就不再模拟实现过程,代码实现如下:

折半查找(二分查找)

比顺序查找性能更好的是折半查找(二分查找),其时间复杂度为log2n,这种查找大一其实也使用过。折半查找的对象是一组有序的元素,基本思想是,设置左右指针以及中间点指针,每次判断所查元素值与中间点指针所指值的大小关系,若大,则所查元素在右半区,让左指针指向中间点指针右边所指的元素,然后中间点指针指向右半区的中间点,若小,则所查元素在左半区,让右指针指向中间点指针左边所指的元素,然后中间点指针指向左半区的中间点。重复上述步骤,直到中间点元素为所查元素。

模拟实现过程:

数据为{1,2,4,7,18,29,34,45,65,78},所查元素为78

  • left=1,right=10,mid=5,此时data[5]<78
  • left=6,right=10,mid=8,此时data[8]<78
  • left=9,right=10,mid=9,此时data[9]<78
  • left=10,right=10,mid=10,此时data[10]==78,return 10;

非递归代码实现如下:

递归代码实现如下:

使用数据例子如下:

顺序查找和折半查找的运行结果如下:

树表的查找技术

接着学到的就是树表的查找技术,这是一种动态的查找,分为二叉排序树和平衡二叉树。

二叉排序树

二叉排序树,首先要知道什么是二叉排序树,就是在二叉树的基础上,每个结点的值都大于左子树(非空时)所有的结点值,小于右子树(非空时)所有的结点值。还有一个特点就是,中序遍历二叉排序树得到的是一个递增的有序序列,其就像把二叉排序树从上到下进行投影一样。实现细节:二叉排序树的查找根据二叉排序树的自身性质,通过递归很容易就可实现,即从根结点出发,若为空,返回空,若值为所查元素值,返回该结点,若大于所查元素值,递归的调用其左子树,否则,调用右子树进行查找。在插入操作中,首先我们要清楚一点,新插入的结点必为叶子节点!!!这一点非常的有趣。插入时通过递归找到该插的位置,此时指针所指的必为空,然后动态申请空间,赋值建立叶子节点即可。构造函数就是依次调用插入函数的过程。接下来就是二叉排序树中最有意思的操作——删除操作,删除操作中我首先要知道所删结点及其双亲结点并且要求所删结点为双亲结点的左孩子,根据树的结构,我在删除时要分为四种情况讨论,即所删结点为叶子节点时,直接删除并让双亲结点的左孩子为空。当所删结点只有左子树时,其双亲结点的左指针指向所删结点的左子树,然后删除即可。当所删结点只有右子树时,与只有左子树情况类似,不再赘述。当所删结点既有左子树也有右子树时,此时就不仅是单纯的删除问题,还要考虑其左右子树如何处理,根据二叉排序树的特点,我们可以知道两个方案,一是右子树中最小的元素做根结点(即所删结点),再将右子树中最小的那个结点删除。二是左子树中最大的元素做根结点(即所删结点),再将左子树中最大的那个结点删除。我选择的是方案一,找右子树中最小的结点(特点是不存在左子树)也很简单,只需找出右子树中最左边的那个结点,即从右子树根结点左指针开始,一直遍历,知道左子树为空就结束。该方案会存在一种特殊情况就是所删结点的右孩子就是右子树中最小的,这时特殊处理。实现代码如下:

插入操作:

查找操作:

删除操作:

运行结果(数据为{12,65,34,91,74,58,32,8,9} )如下:

平衡二叉树

然后我学到的就是平衡二叉树,首先要只要什么是平衡二叉树,根结点的左子树和右子树的深度最多相差1,根结点的左子树和右子树都是平衡二叉树,满足上述性质的二叉排序树就是平衡二叉树。可见,平衡二叉树是在二叉排序树的基础上进行变换的,引入平衡二叉树是为了让我们构造的二叉排序树具有很好的性能,避免出现斜树这种极端情况。然而构造平衡二叉树的代码实现过于复杂,以我的能力还不能实现,且不要求掌握,所以这里重点学习了平衡二叉树的逻辑构造过程。首先引入平衡因子的概念,即结点的左子树的深度与右子树的深度之差,根据定义显而易见,平衡因子只能取1,0,-1。在构造平衡二叉树的过程中,逐个插入构造二叉排序树,新插入的点可能会破坏原本的平衡二叉树,这时就需要通过旋转来调整,旋转的操作可谓非常的巧妙!!!同时注意旋转的结果仍要满足二叉排序树的性质!!!如果三个结点处于一条直线上,则采用单旋转进行平衡化(若为左斜树,则最上面的结点顺时针旋转下来变成中间点的右儿子,反之变成左儿子),如果这三个结点处于一条折线上,则采用双旋转进行平衡化。

为了描述方便,我将PPT中的图片截取下来如下:

左单旋转:如图上所示ACE不平衡了,需要进行左旋将A逆时针旋转下来变成中间点C的左儿子,同时C原来的左子树肯定比A大比C小,所以作为A的右子树。右单旋转与左单旋转类似。

先右后左双旋转:如上图所示,我可以清楚的看到ACD三个点为一条折线,当G新插入一个结点破坏平衡时,需要双旋转,这种类似大于号“>”的折线,需要进行先右旋转,将D旋到AC之间,此时D的右子树中所有值肯定大于D小于C,所以旋转后变为C的左子树。然后在进行左单旋转,最终变为平衡二叉树。

先左后右双旋转与上述操作类似,如上图所示,这种类似小于号“<”的折线,需要进行先左旋转,将E旋到AB之间,此时E的右子树中所有结点的值肯定大于B小于E,所以旋转后变为B的右子树。然后在进行右单旋转,最终变为平衡二叉树。

散列表

最后在查找技术当中学到的就是散列表,也叫哈希表,散列既是一种查找技术,也是一种存储技术,其核心思想是,在存储时通过一种函数来映射,找到应该存放的位置,然后在查找时根据同样的映射函数来进行查找。但这样会出现一个问题,不同的数据在经过同一个函数映射后可能会得到同样的结果,这也就产生了冲突,此时必须另外再找一个地方来存放数据。在散列函数当中,我学到了直接定址法(H(key) = a ´ key + b)、除留余数法(H(key)=key  mod p)、平方取中法(将关键码进行平方后取中间几位为散列地址)、折叠法(将关键码从左到右分成位数相同的及部分,然后相加取后几位做散列地址)。关于处理冲突的方法,我学到了开放定址法和拉链法。

开放定址法

开放定址法:基本思想:将散列函数得到的地址加上一个常数(如1)进行试探,若为该地址所在散列表为空则存,否则继续试探,直到找到空位为止。该加常数进行试探的方法称为线性探测法,若加的常数为平方数则称为二次探测法,若为随机数列,则称为随机探测法。代码实现细节:散列函数我采用的是除留余数法,除数我取的是9。通过数组来实现散列表,初始化都为0,查找操作和插入操作的思想都很简单,实现起来很容易,这里就不再赘述,我主要想说一下删除操作,普通的删除会让该地址其他元素查找不到并且会无法使用,所以这里的删除不是真正的删除,而是将要删除地址的值改为一个标记数值,表明该位有值但是是无用值可以进行插入,我设置的是-9999作为标记数值来进行操作的。这种删除思想称为懒惰删除,我觉得非常的巧妙。

实现代码如下:

删除操作:

运行结果如下:

拉链法

拉链法:基本思想,存储结构与第六章的邻接表相似,采用数组和链表相结合的方式,数组用于找到对应地址,然后将多个相同地址用链表来储存,查找时只需找到对应数组所指向的链表,依次遍历该链表就能找到,这种思想非常的有趣。代码实现细节:在实现当中我尝试了两种方法,一种定义的数组是结构体类型的,另一种是结构体指针类型的数组。后一种是我在书本上学到的,当数组为指针类型时,通过数组下标返回的数组内容其实是指向对应链表的第一个元素的指针,所以在删除操作当中需要判断所删元素是不是链表中的第一个元素,分成两种情况来处理。实现代码如下:

定义结构体指针类型的数组:

运行结果如下:

查找技术课后题

1.设计顺序查找算法,将哨兵设在下标高端

2.编写算法求给定结点在二叉排序树中所在的层数

3.编写算法,在二叉排序树上找出任意两个不同结点的最近公共那个祖先

4.设计算法,判断一棵二叉树是否是二叉排序树

98. 验证二叉搜索树 - 力扣(LeetCode)只要明白一点,二叉树中序遍历的结果为升序时就为二叉排序树。

5. 编写算法确定二叉树中各结点的平衡因子。

思想:分别让每个结点作根结点时,计算左右子树的深度,两者的差就是该结点的平衡因子。遍历每个结点时用三种递归的遍历都可,我使用的是后序遍历。

6. 在用线性探测解决冲突的散列表中,设计算法实现闭散列表的懒惰删除操作。

该操作我在上述散列表中已经实现,这里就不再赘述。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值