8 > 查找

基本概念

  1. 查找表:用于查找的数据集合称为查找表,由同类元素组成。
    1. 静态查找表:只需要进行查找操作。
    2. 动态查找表:还可以进行增删操作。
  2. 唯一表示数据元素的数据项。
  3. 算法好坏评价:
    1. 查找长度:在查找运算中需要对比关键字多少次。反映了查找操作时间的复杂度。

    2. 平均查找长度:所有查找过程中进行关键字比较的平均值。(通常考虑查找成功和失败)

      A S L = ∑ i = 1 n P i C i        ( P :查找 i 号元素的概率, C :查找 i 号元素的查找长度 ) ASL=\sum^n_{i=1}P_iC_i~~~~~_{(P:查找i号元素的概率,C:查找i号元素的查找长度)} ASL=i=1nPiCi     (P:查找i号元素的概率,C:查找i号元素的查找长度)

查找

  1. 顺序查找(线性查找):(通常用于线性表)
    1. 执行效率(平均查找长度):(时间复杂度: O ( n ) O(n) O(n)
      1. 成功: 1 + n 2 \frac{1+n}{2} 21+n
      2. 失败: n + 1 n+1 n+1(需要进行 n+1 次对比)
    2. 优化:
      1. 如果表中元素有序,当 当前元素大于目标元素即可判定失败。
      2. 如果表中元素概率分布不均,按照概率从大到小排布可提升成功查找率。
  2. 二分查找:(仅适用于有序表,且数据为任意类型,但要能排序)
    1. 执行效率:(时间复杂度: O ( log ⁡ 2 n ) O(\log_2n) O(log2n)
      1. 成功: ⌈ log ⁡ 2 ( n + 1 ) ⌉ − 1 \lceil\log_2(n+1)\rceil-1 log2(n+1)⌉1
      2. 失败: A S L = ∑ i = 1 n P i C i ASL=\sum^n_{i=1}P_iC_i ASL=i=1nPiCi
    2. 查找判定树的构建:(是一个二叉排序树、平衡二叉树)
      1. 所构建的二叉排序树 右子树 - 左子树 = 0、1;(前提是向下取整)
      2. 树高: ⌊ log ⁡ 2 n ⌋ + 1 \lfloor\log_2n\rfloor+1 log2n+1
  3. 分块查找:(索引顺序查找)
    1. 特点:分块内可以无序,分块间必须序。块内元素个数没有要求。

    2. 步骤:

      1. 在索引表中确定目标元素的分块位置,(块间采用二分查找)

        注:若在进行分块间查找时没有目标元素,则将在 rear 指针上取得目标分区;当 rear 指针指空时,则直接查找失败。

      2. 找到目标分块时,再在块内查找。(块内采用暴力搜索)

    3. 执行效率:

      1. 成功: A S L = ∑ i = 1 n P i C i ASL=\sum^n_{i=1}P_iC_i ASL=i=1nPiCi (二分和顺序查找不一样)
      2. 失败:
    4. 设 n 个记录,均匀分为 b 块,每块 s 个记录,则查找效率为:

      顺序查找: A S L = b + 1 2 + s + 1 2 ,当 s = n 时, A S L m i n = n + 1 二分查找: A S L = ⌈ log ⁡ 2 ( n + 1 ) ⌉ + s + 1 2 顺序查找:ASL=\frac{b+1}{2}+\frac{s+1}{2},当s=\sqrt{n}时,ASL_{min}=\sqrt{n}+1\\二分查找:ASL=\lceil\log_2(n+1)\rceil+\frac{s+1}{2} 顺序查找:ASL=2b+1+2s+1,当s=n 时,ASLmin=n +1二分查找:ASL=log2(n+1)⌉+2s+1

二叉排序树(BST)

  1. 左子树值 < 根节点值 < 右子树值
  2. 排序二叉树经过中序遍历为一个递增数列。
  3. 查找效率:很大程度取决于二叉树高度。
    1. 最坏情况: O ( n ) O(n) O(n) 成功,(树高为 n )
    2. 最好情况: 平均查找高度: O ( log ⁡ 2 n ) 平均查找高度:O(\log_2n) 平均查找高度:O(log2n) 成功。(接近完全二叉树)

平衡二叉树(AVL)

  1. 属于二叉排序树。
  2. 左子树和右子树高度之差不超过 1 。
  3. 平衡二叉树高度 = 理想二叉搜索树高度 + 1
  4. 节点平衡因子:左子树 - 右子树 = 平衡因子(0、1、-1)
  5. 每次调整的子树都是最小不平衡子树。(从下往上找,找到左右子树高度差大于因子的点,调整它的最高的儿子)
  6. 指定高度的平衡二叉树的最少节点个数: n h = n h − 1 + n h − 2 + 1 n_h=n_{h-1}+n_{h-2}+1 nh=nh1+nh2+1
  7. 平衡处理:
    1. 单向右旋平衡处理(LL):在左子树插入节点导致不平衡,将最小不平衡子树进行顺时针旋转,用最小不平衡树的左节点充当新的根。

      //f为当前最小树的根节点,p为当前树的左孩子,gf为当前f的父节点
      f.left=p.right;
      p.right=f;
      gf.left=p;         
      //gf.right=p;
      
    2. 单向左旋平衡处理(RR):在右子树插入节点导致不平衡,将最小不平衡子树进行逆时针旋转,用最小不平衡树的右节点充当新的根。

      f.right=p.left;
      p.left=f;
      gf.left=p;
      //gf.right=p;
      
    3. 双旋平衡处理:

      • 先左后右(LR):先左旋再右旋
      • 先右后左(RL):先右旋再左旋
  8. 平均查找效率: O ( log ⁡ 2 n ) O(\log_2n) O(log2n)
    1. 假设以 n h 表示深度为 h 的平衡树中含有的最少节点数为 : n h = n h − 1 + n h − 2 + 1 假设以n_h表示深度为h的平衡树中含有的最少节点数为:n_h=n_{h-1}+n_{h-2}+1 假设以nh表示深度为h的平衡树中含有的最少节点数为:nh=nh1+nh2+1
  9. 元素的删除:(删除之后还必须调成为平衡二叉树)
    1. 时间复杂度: O ( log ⁡ 2 n ) O(\log_2n) O(log2n)
  10. 应用场景:以查为主,很少进行修改的场景。

红黑树(RBT)

  1. 对比AVL树:(红黑树和AVL树都是自平衡的二叉搜索树)
    1. 插入和删除开销优于AVL树,尤其处理大量数据时,总体性能优于AVL树;
    2. 查询性能略低于AVL树;(对比AVL树多一层不平衡层)
    3. 在频繁查找下,AVL树的优势更大;
  2. 特性:
    1. 每个节点只能是红色或者黑色;
    2. 根节点为黑色;
    3. 叶子节点(NULL节点)均是黑色;(不是真叶子)
    4. 路径上不存在两个相连的红色节点;(可以存在两个相邻的黑节点)
    5. 从任一节点到任一叶子的所有简单路径都包含相同数目的黑色节点(简称黑高)。
  3. 黑高:从任一节点出发,到达任一“叶子”节点的黑节点总数,称为该元素的黑高。(不包含节点自己)
    1. 若节点黑高为 h ,则内部节点数最少为 2 h − 1 2^h-1 2h1个。(必须为满树形态,非满树不符合 e )
  4. 性质:
    1. 从根节点到达“叶子”节点的最长路径 ≤ 2倍最短路径。(左右子树高度比不超过2)
    2. 有 n 个内部节点,则其高度为: h ≤ 2 ⌈ log ⁡ 2 ( n + 1 ) ⌉ h\le2\lceil\log_2(n+1)\rceil h2log2(n+1)⌉。(根节点黑高必定大于 h 2 \frac{h}{2} 2h,⇒ n ≥ 2 ( h / 2 ) − 1 n\ge 2^{(h/2)-1} n2(h/2)1 h ≤ 2 ⌈ log ⁡ 2 ( n + 1 ) ⌉ h\le2\lceil\log_2(n+1)\rceil h2log2(n+1)⌉
  5. 红黑树的插入:

8 > 查找_第1张图片

  1. 红黑树的删除:(考察率:0.01%)

    8 > 查找_第2张图片

  2. 增、删、查时间复杂度: O ( log ⁡ 2 n ) O(\log_2n) O(log2n)

  3. 应用场景:增删数据频繁、处理大量数据的场景,适用红黑树;查询频繁的场景,适用AVL树。

B 树(B- 树)

  1. 数据结构:

    //(MAXSIZE+1)叉查找树
    struct node{
    		DATATYPE data[MAXSIZE];         //有MAXSIZE+1个隔板
    		struct node *child[MAXSIZE+1];       //每个隔板连接下一个分支
    		int len;
    };
    
  2. 保证效率的策略:

    1. m 叉查找树中规定除根节点以外,其他任何节点至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2个分叉,即至少含有 ⌈ m / 2 ⌉ − 1 \lceil m/2\rceil-1 m/21个关键字。
    2. m 叉查找树中规定任何一个节点,它的所有子树的高度都相同。(绝对平衡)
  3. B 树特性:(一颗 m 阶B树或空树)(阶为多少个分叉)(每个关键字都包含记录信息)

    1. 树中每个节点至多有 m 个子树,至多有 m-1 个关键字。
    2. 若根节点不是终端节点(实叶子节点),则至少有两个子树。(绝对平衡)
    3. 除根节点之外的所有非叶子节点(NULL节点)至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2个子树,至少含有 ⌈ m / 2 ⌉ − 1 \lceil m/2\rceil-1 m/21个关键字。
    4. 所有叶子节点在同一层。
    5. 所有节点中元素有序。
    6. 所有非叶子节点结构为【子树0<关键字1<子树1<关键字2<子树2<…】相间排列。(指针 i-1 所指子树中的关键字均小于元素 i,指针 i 所指的子树的关键字均大于 元素 i)
  4. 含 n 个关键词的 m 阶B树的高度:( m 个关键字,m-1 个分叉)

    1. 最小高度(全部装满): h ≥ ⌈ log ⁡ m ( n + 1 ) ⌉ h\ge\lceil\log_m(n+1)\rceil hlogm(n+1)⌉

      n ≤ ( m − 1 ) ( 1 + m + m 2 + . . . + m h − 1 ) = m h − 1 n\le(m-1)(1+m+m^2+...+m^{h-1})=m^h-1 n(m1)(1+m+m2+...+mh1)=mh1

    2. 最大高度(全部按最小个数装): h ≤ ⌈ log ⁡ ⌈ m / 2 ⌉ n + 1 2 + 1 ⌉ h\le\lceil\log_{\lceil m/2\rceil}\frac{n+1}{2}+1\rceil hlogm/22n+1+1

      节点数 : 第一层 1 ,第二层 2 ,第三层 2 ⌈ m / 2 ⌉ ,第四层 2 ⌈ m / 2 ⌉ ⌈ m / 2 ⌉ = > 第 h 层 : 2 ( ⌈ m / 2 ⌉ ) h − 2 = > n 个关键字节点必有 n + 1 个叶子 ( n u l l ) 节点 = > n + 1 ≥ 2 ( ⌈ m / 2 ⌉ ) h − 2 = > h ≤ ⌈ log ⁡ ⌈ m / 2 ⌉ n + 1 2 + 1 ⌉ 节点数:第一层1,第二层2,第三层2\lceil m/2\rceil,第四层2\lceil m/2\rceil\lceil m/2\rceil\\=>第h层:2(\lceil m/2\rceil)^{h-2}\\=>n个关键字节点必有n+1个叶子(null)节点=>n+1\ge2(\lceil m/2\rceil)^{h-2}\\=>h\le\lceil\log_{\lceil m/2\rceil}\frac{n+1}{2}+1\rceil 节点数:第一层1,第二层2,第三层2m/2,第四层2m/2m/2=>h:2(⌈m/2)h2=>n个关键字节点必有n+1个叶子(null)节点=>n+12(⌈m/2)h2=>hlogm/22n+1+1

    3. B 树的删除插入:(宗旨就是保证B树的特性和每个节点的元素顺序)

    4. 插入:如果插入的元素超过节点上限,则按 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2进行分裂, ⌈ m / 2 ⌉ \lceil m/2\rceil m/2前的元素加入 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2元素的左分支,后的元素加入 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2的右分支, ⌈ m / 2 ⌉ \lceil m/2\rceil m/2则加入父节点并按由小到大顺序排序(如果父节点未满),如果父节点超过上限则父节点分裂向上生长。每次插入都在树的最下层,保证每个NULL叶节点都在同一层。

    5. 删除:

      1. 删除节点不为终端节点:如果删除元素后节点元素个数小于 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2,则找到删除元素的直接前趋,用直接前趋替代删除元素位置;也可以找到直接后继,用直接后继替代删除元素位置。其他元素依次前移保持顺序排列。
      2. 删除节点为终端节点:如果需删除元素的兄弟节点有超过 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2个元素时,则将需删除元素的后继(前趋)元素移动追加(挤入)到当前节点,需删除元素的后继(前趋)的后继替代它的后继位置。如果左右相邻兄弟节点不够用,则进行节点合并,节点合并可能向上传递。

B+ 树(数据库索引)

  1. 对于 m 阶B+树的特性:
    1. 每个节点最多有 m 个子树(或元素)。(每个关键字对应一个分支)
    2. 非叶根节点至少有两个子树,其他分支节点至少有 ⌈ m / 2 ⌉ \lceil m/2\rceil m/2个子树。(保证查找效率)
    3. 节点子树个数和节点元素个数相等。
    4. 每个叶子节点(实叶子节点)包含全部关键字及其指向相应记录的指针,在叶子节点中将关键字按序排列,相邻叶子节点按大小顺序相互链接。
    5. 每个分支节点包含它的各个子节点中的关键字的最大值以及指向各个子节点的指针。
  2. MYSQL为什么采用B+树:
    1. 采用B+树存储数据可以进行顺序和随机检索,因为所有叶子节点存在链。B树只能进行随机检索。
    2. B+树只存储键值,所有记录存储在叶子节点,磁盘利用率高,可以提高IO速度。B树每个节点既存储键值又存储记录。
    3. B+树查询稳定,树越矮查询越快。
    4. B+树可以进行范围查询,只需要遍历叶子节点即可。B树只能进行单个查询。
    5. B+树可以快速增删记录,它的所有记录以有序链表进行存储。

散列查找

  1. 概念:散列表(哈希表):数据元素的关键字与其存储地址直接相关。
    1. 若不同关键字通过散列函数映射到同一个值,则称它们为“同义词”,通过散列函数确定的位置已经存放了其他元素,则称这种情况为“冲突”。
    2. 装填因子:表中记录数 / 哈希表长度( n / L n/L n/L),会直接影响散列表的查找效率。(装填因子越大冲突可能 越大)
    3. 查找长度:ASL值与“同义词“多少有关,越少越快。
    4. 哈希函数设计的越好,ASL 就会越低。
  2. 常见哈希函数:(设计目标:尽可能减少冲突)
    • 除留余数法:散列表表长m,取一个 ≤m 的最大质数 p ⇒ H(key) = key % p
    • 直接定址法:H(key) = a*key + b || H(key)=key,其中a,b为常数。适合关键字分布基本连续的情况,且不会产生冲突。如果分布不连续,则会造成空位多,空间浪费。(学号)
    • 数字分析法:选取数码分布较为均匀的若干位作为散列地址。设关键字为 r 进制,而 r 个数码在各个节点上出现的频率不一定相同,可能在某些位上分布均匀。(电话号)
    • 平方取中法:取关键字的平方的中间几位为散列地址。具体位数要根据情况而定,这种方法得到的散列地址与关键字的每位都有关系。(适用于关键字的每位取值都不够均匀或均小于散列地址所需的位数)(身份证号)
  3. 链地址法:将所有“同义词”串成一个链。
  4. 开放定址法:(所有数据都存储在散列表中,所有的删除只能进行逻辑删除,防止查找失败)
    1. 公式: H i = ( H ( e l e m ) + d i ) % m         ( m 为表长, d i 为增量序列,表示发生第 i 次冲突 ) H_i=(H(elem)+d_i)\% m~~~~~~_{(m为表长,di为增量序列,表示发生第i次冲突)} Hi=(H(elem)+di)%m      (m为表长,di为增量序列,表示发生第i次冲突)
    2. 线性探测法:当发生冲突时,依次向后探测下一个单元是否为空(删除元素时不能清空元素,做删除标记)(易造成同义词、非同义词“扎堆聚集”,严重影响查找效率)
    3. 平方探测法(二次线性探测法):发生冲突后对增量序列( k ≤ m / 2 k\le m/2 km/2)进行平方,正负双向探测,若负方向探测长度不够则循环探测表尾。(散列表长度 m 必须为 4*j + 3 的质数,才能探测到所有位置)
    4. 伪随机序列法: d i d_i di是一个伪随机序列,按照这个序列依次探测。
  5. 再散列法:多准备几个哈希函数,当第一次发生冲突,就再用新哈希函数进行散列计算,直到不再冲突为止。

你可能感兴趣的:(算法,c语言)