0%

查找排序

查找排序

查找

  • 熟练掌握顺序表和有序表(折半查找)的查找算法及其性能分析方法;
  • 熟练掌握二叉排序树的构造和查找算法及其性能分析方法;
  • 掌握二叉排序树的插入算法,掌握二叉排序树的删除方法;
  • 熟练掌握哈希函数(除留余数法)的构造;
  • 熟练掌握哈希函数解决冲突的方法及其特点

查找中的概念

  • 查找表:由同一类型的数据元素(或记录)构成的集合
  • 静态查找表:查找的同时对查找表不做修改操作(如插入和删除)
  • 动态查找表:查找的同时对查找表具有修改操作
  • 关键字:记录中某个数据项的值,可用来识别一个记录
  • 主关键字:唯一标识数据元素
  • 次关键字:可以标识若干个数据元素

平均搜索长度

ASL:关键字的平均比较次数
$$
ASL=\sum_{i=1}^{n}{p_i*c_i}
$$
pi:查找第i个记录的概率 ( 通常认为pi =1/n );

ci:找到第i个记录所需的比较次数。

线性表的查找

顺序查找

适用于静态查找表

改进:将哨兵(即待查找的key)存入表头,从后往前

空间复杂度:一个辅助空间

时间复杂度:

  • 成功:$ASLs=\frac{1}{n}*(1+2+…+n)=\frac{n+1}{2}$
  • 失败:$ASLf=\frac{1}{n}n(n+1)=(n+1)$

查找概率相等时,ASL相同。
查找概率不等时,如果从前向后查找,则按查找概率由大到小排列的有序表其ASL要比无序表ASL小。

折半查找

适用顺序存储结构的有序表

  1. 设表长为n,low、high和mid分别指向待查元素所在区间的上界、下界和中点,k为给定值
  2. 初始时,令low=1,high=n,mid=(low+high)/2
  3. 让k与mid指向的记录比较
    • 若k==R[mid].key,查找成功
    • 若k<R[mid].key,则high=mid-1
    • 若k>R[mid].key,则low=mid+1
  4. 重复上述操作,直至low>high时,查找失败

时间复杂度:
$$
O(log_2n)
$$

判定树

  • 根结点就是第一次进行比较的中间位置的记录;

  • 前子表的中间位置作为左子树的结点;

  • 后子表的中间位置作为右子树的结点;

查找成功时比较次数不超过判定树的深度d:
$$
d=\lfloor{log_2n}\rfloor-1
$$

查找失败时比较d或d-1次

分块查找

分块有序,即分成若干子表,要求每个子表中的数值都比后一块中数值小,然后将各子表中的最大关键字构成一个索引表,表中还要包含每个子表的起始地址

适用线性表既要快速查找又经常动态变化。

  1. 索引表有序→折半查找
  2. 确定了待查关键字所在的子表后,在子表内采用顺序查找法

$$
ASL=L_b+L_w
$$

$$
ASL_{bs}≈log_2(\frac{n}{s}+1)+\frac{s}{2}, (log_2n\leq ASL_bs\leq \frac{n+1}{2})
$$

树表

表结构在查找过程中动态生成。对于给定值key若表中存在,则成功返回;否则插入关键字等于key 的记录

二叉排序树

性质

  • 若其左子树非空,则左子树上所有结点的值均小于根结点的值;
  • 若其右子树非空,则右子树上所有结点的值均大于等于根结点的值;
  • 其左右子树本身又各是一棵二叉排序树

中序遍历二叉排序树能得到一个递增序列。

查找

  1. 若二叉排序树为空,则查找失败,返回空指针。
  2. 若二叉排序树非空,将给定值key与根结点的关键字T->data.key进行比较
    • 若key等于T->data.key,则查找成功,返回根结点地址;
    • 若key小于T->data.key,则进一步查找左子树;
    • 若key大于T->data.key,则进一步查找右子树。

$$
log_2n\leq ASL\leq \frac{n+1}{2}
$$

插入

  • 树中已有,不再插入
  • 树中没有,查找直至某个叶子结点的左子树或右子树为空为止,则插入结点应为该叶子结点的左孩子或右孩子:插入的元素一定在叶结点上

生成

插入→生成

树的形态与插入次序有关

删除

  • 删除叶节点:将双亲结点指针清除即可;
  • 被删结点缺左/右子树:用其右/左子女顶替delete结点;
  • 被删结点的左右子树都存在:
    1. 用p的直接前驱结点代替p;
    2. 用p的直接后继结点代替p;

平衡二叉树AVL

平衡因子:该结点左子树与右子树的高度差

平衡二叉树:任一结点的平衡因子只能是-1、0、1,即左右高度差不超过1.

平衡旋转

插入:插入后进行调整,最后仍是平衡二叉树。

B-树

m阶B-树:

  • 每一个结点分支数≤m→关键字数≤m-1;
  • 根结点分支数≥2(要求此根结点不为叶子结点)
  • 其余分支结点的分支数≥ m/2 (向上取整)
  • 所有叶子结点在同一层;
  • 有序性,看PPT71

插入

若溢出,分解结点。?

  • 将中间值往上提一层,调整
  • 如果向上提后又溢出,继续上提

删除

若删除的关键字不在叶子结点中,用其前驱或后继来替换,删除后进行调整?,使其仍为B-树。

哈希表

哈希方法:

哈希函数:记录的存储位置与关键字之间存在对应关系

哈希冲突:不同的关键码映射到同一个哈希地址

同义词:具有相同函数值的两个关键字

哈希函数的构造

  • 直接定址法
    • 关于关键码的线性函数:Hash(key) = a·key + b
  • 数字分析法;
    • 取关键字的若干位或组合作为哈希地址
  • 平方取中法
    • 将关键字平方后取中间几位作为哈希地址
  • 折叠法
    • 将关键字分割成位数相同的几部分(最后一部分可以不同),然后取这几部分的叠加和作为哈希地址
      数位叠加
    • 移位叠加:低位对齐相加
    • 间界叠加:从一端到另一端沿分割界来回折迭,然后对齐相加。
  • 除留余数法
    • Hash(key)=key mod p,p≤表长且为质数
  • 随机数法

评价因素

  • 构造简单
  • 均匀映射

冲突处理

开放定址法

有冲突时就去寻找下一个空的哈希地址

  • 线性探测法

    • (Hash(key)+1)mod p
    • “聚集”现象
  • 二次探测法

    • $$
      (Hash(key)\pm{1^2})
      $$
  • 伪随机探测法

    • (Hash(key)+random)mod m,m表示表长

链地址法

相同哈希地址的记录链成一单链表,m个哈希地址就设m个单链表,然后用用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构

查找效率

ASL取决于:

  • 哈希函数
  • 处理冲突的方法
  • 哈希表的装填因子α=表中填入记录数/哈希表的长度
    • α越大,发生冲突的可能性就越大,查找时比较次数就越多

$$
ASL≈1+\frac{α}{2} (拉链法)
$$

$$
ASL≈\frac{1}{2}(1+\frac{1}{1-α})(线性探测法)
$$

$$
ASL≈-\frac{1}{α}ln(1-α)(随机探测法)
$$

排序

  • 掌握排序的基本概念和各种排序方法的特点,并能加以灵活应用

  • 熟练掌握直接插入排序折半插入排序、起泡排序、直接选择排序、快速排序的排序算法及其性能分析

  • 掌握希尔排序、归并排序、堆排序、基数排序的方法及其性能分析

  • 掌握外部排序方法中败者树的建立及归并方法,掌握置换-选择排序的过程和最佳归并树的构造方法。

内部排序&外部排序

算法评价=时间效率+空间效率+稳定性

img

插入排序

每步将一个待排序的对象,按其关键码大小,插入到前面已经排好序的一组对象的适当位置上,直到对象全部插入为止。

直接插入排序

基于顺序查找,=n-1次插入

  1. 在R[1..i-1]中查找R[i]的插入位置,R[i].key< R[j+1..i-1].key;
  2. 将R[j+1..i-1]中的所有记录均后移一个位置;
  3. 将R[i] 插入到R[j+1]的位置上。

折半插入排序

基于折半查找,在插入第 i 个对象时,需要经过log2i+1(向下取整) 次关键码比较,才能确定它应插入的位置 。

希尔排序

基于逐次缩小增量

先将整个待排记录序列分割成若干子序列,分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。

子序列的构成:相隔d增量的记录组成→逐渐缩短增量直至d=1。

最后一个增量值必须为1

不宜在链式存储结构上实现。

交换排序

两两比较,如果发生逆序则交换,直到所有记录都排好序为止

起泡排序

每趟不断将记录两两比较,并按“前小后大” 规则交换

快速排序

  • 任取一个元素 (如第一个) 为中心
  • 所有比它小的元素一律前放,比它大的元素一律后放,形成左右两个子表;
  • 对各子表重新选择中心元素并依此规则调整,直到每个子表的元素只剩一个

从两头向中间交替式逼近

递归

选择排序

每一趟在后面 n-i +1个中选出关键码最小的对象, 作为有序序列的第 i 个记录

简单选择排序

树形选择排序

优势:利用上次选择的结果,选出次最小者。

堆排序

将序列看成完全二叉树,非终端结点的值均小于或大于左右子结点的值。(实际上是线性空间)

堆顶元素为max(大根堆)或min(小根堆)

建堆

有n 个结点的完全二叉树,最后一个分支(非终端)结点的标号是 n/2(向下取整)

设x=n/2(向下取整),从第x个元素起,至第一个元素止,进行反复筛选。

筛选法

(以小根堆为例):

  1. 将序列画成完全二叉树;
  2. 从最后一个分支点(n/2)向下取整 开始调整;
  3. 比较该结点与其左右子结点的大小,找出最小的那个置于该结点(交换位置),并观察位置变化后的左右子树是否符合小根堆的要求,递归调整。

调整

在输出堆顶元素后重新调整为新堆。

  1. 输出堆顶元素后,以堆中最后一个元素替代之
  2. 将根结点与左、右子树根结点比较,并与小者交换
  3. 重复直至叶子结点,得到新的堆

归并排序

将两个或两个以上的有序表组合成一个新有序表

2-路归并排序

  1. 初始序列看成n个有序子序列,每个子序列长度为1
  2. 两两合并,得到 n/2 (向下取整)个长度为2或1的有序子序列
  3. 再两两合并,重复直至得到一个长度为n的有序序列为止

类比多项式相加

基数排序

最高位优先MSD

  1. 先对最高位关键字k1(如花色)排序,将序列分成若干子序列,每个子序列有相同的k1值;
  2. 然后让每个子序列对次关键字k2(如面值)排序,又分成若干更小的子序列;
  3. 依次重复,直至就每个子序列对最低位关键字kd排序,就可以得到一个有序的序列。

eg.十进制数比较

最低位优先LSD

  1. 首先依据最低位排序码Kd对所有对象进行一趟排序。
  2. 再依据次低位排序码Kd-1对上一趟排序结果排序。
  3. 依次重复,直到依据排序码K1最后一趟排序完成,就可以得到一个有序的序列。

链式基数排序

条件:

  • 知道各级关键字的主次关系
  • 知道各级关键字的取值范围

步骤:

  1. 首先对低位关键字排序,各个记录按照此位关键字的值‘分配’到相应的序列里。
  2. 按照序列对应的值的大小,从各个序列中将记录‘收集’,收集后的序列按照此位关键字有序。
  3. 在此基础上,对前一位关键字进行排序

外部排序

  1. 按可用内存大小,利用内部排序方法,构造若干个记录的有序子序列写入外存,通常称这些记录的有序子序列为 “归并段”;
  2. 通过“归并”,逐步扩大(记录的)有序子序列的长度,直至外存中整个记录序列按关键字有序为止。