【数据结构总结笔记(二)----查找与排序】

目录

❼查找结构

顺序查找表

有序表查找

折半查找/二分查找(binary search)

插值查找(interpolation search)

斐波那契查找(fibonacci search)

线性索引查找

稠密索引

分块索引

倒排索引

 二叉排序树(binary sort tree)

二叉平衡树(AVL树)

多路查找树(B树)

B+树

 散列表查找(哈希表)

❽排序:使序列成为一个按key有序的序列

 内排序与外排序

冒泡排序(bubble sort)

简单选择排序(simple selection sort)

直接插入排序(straight insertion sort)

希尔排序(shell sort)

堆排序(heap sort)

归并排序(merging sort)

快速排序(quick sort)


❼查找结构

查找表: 需要被查的元素的集合

关键字/键值(key):数据元素中某个数据项的值

  • 若此key可以唯一标识一个记录,则为主关键字(primary key),所在数据项称为主关键码
  • 若此key可识别多个记录,则为次关键字(secondary key),所在数据项称为次关键码

查找表分类

静态查找表(static search table):只做查找操作

  • 查询某“特定”数据元素是否在查找表中
  • 检索某“特定”数据元素和各种属性

动态查找表(dynamic search table):查找同时可以插入或删除

  • 查找时插入(不存在)数据元素
  • 查找时删除(已存在)数据元素

 查找的存储结构

  • 静态查找表:线性表结构存储;顺序查找算法(再对主key排序可应用折半查找)
  • 动态查找表:二叉排序树
  • 散列表结构

顺序查找表

  • 设置哨兵(a\left [ 0\right ]=key),循环判断下标是否越界(a\left [ i\right ] != key)
  • 时间复杂度:O(n)

缺:效率低下,所以应用于小型数据查找

有序表查找

(1)折半查找/二分查找(binary search):线性表必须采取顺序存储

  • 代码:mid = (low+high)/2
  • 时间复杂度:O(logn)

多用于静态查找表,用于动态查找表时,由于频繁插入或删除,维护有序的排序需要不小的工作量

(2)插值查找(interpolation search):要查找的key与查找表中最大最小记录的key比较后查找

  •  插值公式:\frac{key-a[low]}{a[high]-a[low]}
  • 代码:mid = low+(high-low)*(key-a[low])/(a[high]-a[low])
  • 时间复杂度:O\left ( logn \right )

对于表长较长,且key分布较均匀的查找表,插值查找好于二分查找;反之,\left \{ 0,2,20001,...,9999999 \right \}这种分布不均匀的,插值查找并不合适。

(3)斐波那契查找(fibonacci search)

  1. key=a[mid] :查找成功
  2. key<a[mid] :   新范围是第low~第mid-1,范围个数为F(k-1)-1个
  3. key>a[mid] :   新范围是第m+1~第high,范围个数为F(k-2)-1个
  4. 代码:mid = low+F(k-1)-1
  5. 时间复杂度:O\left ( logn \right )

平均性能优于二分查找,但最坏情况,低于二分查找

线性索引查找

关于索引:

(1)稠密索引:将数据集中的每个记录对应一个索引项

 索引项有序,证明查找可使用二分、插值、斐波那契查找,大大提高了效率;但如果数据集非常大,索引项与数据集记录个数相同,查找性能反而下降。

(2)分块索引:每块对应一个索引项

  • 块需要满足的需求:块内无序、块间有序
  • 时间复杂度:介于O\left ( n \right )O\left ( logn \right )之间。

索引项结构的三个数据项:

  • 最大关键码
  • 存储了块中的记录个数,以便于循环使用
  • 用于指向块首数据元素的指针,便于开始对这一块中记录进行遍历

 查找步骤

  • 在分块索引表中查找要查关键字所在的块(块间有序,所以二分、插值等算法查找)
  • 根据块首指针找到相应块,并在块中顺序查找关键码(块内无序,只能顺序查找)

(3)倒排索引:记录号表存具有相同次key的所有记录的记录号(指向记录的指针/记录主key)

  •  最基础的搜索技术
  • 每项:属性值+属性值的各记录的地址
  • 不是由记录确定属性值,而是属性值来确认记录位置,故为倒排

索引项通用结构

  • 次关键码:例如下图“英语单词”
  • 记录号表:例如下图“文章编号” 

 二叉排序树(binary sort tree):提高查找和插入删除key的速度

  • 空树
  • 若左子树不空:左子树上所有结点值均小于它的根结构的值
  • 若右子树不空:右子树上所有结点值均大于它的根结构的值
  • 左右子树分别为二叉排序树

二叉排序树插入

  • 将关键字放到树中的合适位置

二叉排序树删除

  • 叶子结点直接删除
  • 仅有左/右子树的结点:独子承父业
  • 左右子树都有的结点:找到结点的直接前驱/直接后继,来替换删除结点(如下图)

二叉排序树的存储结构:链表(插入删除仅需修改链接指针)

比较平衡的二叉排序树的时间复杂度:O\left ( logn \right )

斜树的时间复杂度:O\left ( n \right )

二叉平衡树(AVL树):每节点左右子树高度差至多为1的二叉排序树

(平衡因子)BF:二叉树结点的左子树深度减去右子树深度的值

AVL树的BF只能为:-1、0、1

最小不平衡子树:距离插入结点最近的,且BF绝对值大于1的结点为根的子树 

 构建AVL树

  • BF值为正:整棵树右旋(顺时针旋转)
  • BF值为负:整棵树左旋(逆时针旋转)
  • 最小不平衡子树的BF与它的子树的BF符号相反时:先旋一次使符号相同,再反向旋一次

查找、插入、删除时间复杂度O\left ( logn \right )

比较理想的一种动态查找表算法

多路查找树(B树):每结点可有两个以上孩子,且每个结点处可存储多个元素

  • 2-3树和2-3-4树都是B树的特例
  • 结点最大的孩子数目为B树的阶(order):2-3树为3阶B树

在B树上查找是一个顺指针查找结点和结点中查找关键字的交叉过程

2-3树:每结点都有2/3个孩子(2结点/3结点),且所有叶子都在同一层次上

2-3树的插入

  • 空树:直接插入一个2结点
  • 将结点插入到2结点的叶子上:将其升级为3结点
  • 将结点插入到3结点上:拆分3结点,再从树中两元素或插入元素三者中选择其一向上移动一层

2-3树的删除

(1)结点位于3结点的叶子结点上:直接删除

(2)结点位于2结点上:删除一个只有一个元素的结点

  • 此结点双亲也是2结点,且拥有一个3结点的右孩子:旋转
  • 此结点双亲是2结点,它的右孩子也是2结点:整棵树变形,再旋转
  • 此结点双亲是3结点:结点拆分再合并
  • 若当前树为满二叉树:减少层数

 

(3)结点位于非叶子的分支结点:中序遍历得到前驱或后继,再补位

  • 删除的分支节点为2结点
  • 删除的分支节点为3结点的某一元素

 2-3-4树:4结点包含小中大三个元素和四个孩子(或没有孩子)

  • 4结点要么没有孩子,要么4个孩子

B+树:应文件系统所需而出的一种B树的变形树

  • 插入删除同B树:只不过操作元素都在叶子结点上进行

随机查找与B树相同,但若从最小关键字进行从小到大的顺序查找,B+树很有优势;B+树结构特别适合带有范围查找,比如查我们学校18~22学生人数,从根节点找到第一个18岁学生,然后在叶子结点按顺序查找到符合范围的所有记录。

 散列表查找(哈希表)

散列技术

  • 通过key查找时不用比较就可获得记录存储位置
  • 是一种存储方法,也是一种查找方法(散列是面向查找的存储结构)
  • 记录间不存在逻辑关系,只与key有关联
  • 最适合的求解问题是查找与给定值相等的记录
  • 冲突(collision):key_{1}\neq key_{2},却有f\left (key _{1} \right ) = f\left ( key_{2} \right ),则key_{1}key_{2}为同义词

同key对应很多记录:不适用散列技术;只有如用班级学生的学号/身份证号来散列存储,此时一个号码唯一对应一个学生才比较合适

散列表也不适合范围查找:如查找18~22岁同学;无法获得表中记录的排序;无法计算最大值、最小值等结果

散列函数的构造方法

直接定址法:

  • 取key的某个线性函数值为散列地址
  • 需要事先知道key的分布情况,适合查找表较小且连续的情况

数字分析法:例如选择号码后四位称为散列地址

  • 抽取:使用key的一部分来计算散列存储位置的方法
  • 避免抽取出现冲突:抽取数字反转、右环位移、左环位移、叠加等
  • 常用于key位数比较大的情况,若事先知道key分布且key的若干位分布均匀,使用数字分析法

平方取中法:

  • 适合不知道key分布,且key位数不是很大的情况

折叠法:

  • 将key从左到右分割成位数相等的几部分,并叠加求和,按表长取后几位为散列地址
  • 适合不知道key分布,且key位数很大的情况

除留余数法:

  • mod取模(求余数)
  • 最常用

随机数法:

  • f\left ( key\right )=random\left ( key \right )
  • 适用于key长度不等的情况

处理散列表冲突

堆积:本不是同义词的两个key争夺一个地址 

开放定址法:一旦冲突就寻找下一个空的散列地址,只要散列表够大总有空散列地址存记录

  • 线性探测法
  • 二次探测法:增加平方运算,不让key都聚集在某一区域
  • 随机探测法:随机函数计算得到位移量

再散列函数法:冲突就换一个散列函数计算

链地址法:同义词key存在一个单链表中(同义词子表),散列表中只存所有同义词子表的头指针

公共溢出区法:为所有冲突的key建立一个公共的溢出区来存放

❽排序:使序列成为一个按key有序的序列

 内排序与外排序

内排序:插入排序、交换排序、选择排序、归并排序

冒泡排序(bubble sort)

  • 让每个key都与其后的每个key比较,如果大则交换,这样第一位置的key在一次循环后一定变成最小值
  • 改进冒泡算法:增加一个标记变量flag来判断此序列是否已经有序
  • 时间复杂度:O\left ( n^{2} \right )

简单选择排序(simple selection sort):

  • 顺序扫描序列中的元素,先找出最小的,再找出第二小的(从除了上一次找出来的元素,也就是剩下的元素中找出最小的),直至排序完成
  • 时间复杂度:O\left ( n^{2} \right )

直接插入排序(straight insertion sort)

  • 将数组划分为有序和无序部分,将无序中记录依次插入到有序部分的合适位置上,得到一个新的、记录数加1的有序表
  • 时间复杂度:O\left ( n^{2} \right )
  • 三者均为O\left ( n^{2} \right ),但直接插入 优于 简单选择 优于 冒泡排序

希尔排序(shell sort)

  • 直接插入排序的改进版,也被称为缩小增量排序
  • 将相距某个增量的记录组成一个子序列(分组),对每组直接插入排序(排序)。随着增量逐渐减少,每组包含的key越来越多,当增量减至1时,整个文件恰被分成一组,算法终止。
  • 不稳定
  • 时间复杂度:O\left ( n^{1.3-2} \right )

堆排序(heap sort)

堆是具有以下性质的完全二叉树:

  • 大顶堆:每个结点的值都大于等于其左右孩子结点的值
  • 小顶堆:每个结点的值都大于等于其左右孩子结点的值

堆结构:将大/小顶堆层序遍历存入数组

  • 简单选择排序的一种改进
  • 构建堆的时间复杂度为O\left ( n \right ),重建堆为O\left ( nlogn \right ),堆排序时间复杂度为O\left ( nlogn \right )
  • 不稳定
  • 不适合待排序序列较少的情况

归并排序(merging sort)

  •  时间复杂度:O\left ( nlogn \right )
  • 递归实现归并排序
  • 非递归实现归并序列

快速排序(quick sort)

是任取待排序序列的一个元素作为中心元素(可以用第一个,最后一个,也可以是中间任何一个),习惯将其称为pivot,枢轴元素;
将所有比枢轴元素小的放在其左边;
将所有比它大的放在其右边;
形成左右两个子表;
然后对左右两个子表再按照前面的算法进行排序,直到每个子表的元素只剩下一个。

  • 冒泡排序的升级
  • 时间复杂度:O\left ( nlogn \right )
  • 不稳定

快速排序的优化

优化选取枢轴:

  • 三数取中法(去三key先排,将中间数作为枢轴,一般取左右端、中间三个数)
  • 九数取中法

优化不必要的交换

优化小数组时的排序方案

优化递归操作:尾递归优化

关于其他数据结构的总结:上一篇笔记

【数据结构总结笔记(一)--针对概念】__Carpediem的博客-CSDN博客

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值