数据结构与算法复习笔记

1. 算法分析

2. 表、栈和队列

2.1 抽象数据类型

  • 什么是ADT:

2.2 表ADT

  • 前继和后继
  • ArrayList和LinkedList特点:
  • ArrayList查找和修改速度快,插入和删除速度慢,因其使用数组实现,对于插入/删除点后的元素需要重新给id赋值,但查找和修改可以根据下标直接操作。
  • LinkedList插入和删除速度快,查找速度慢,因其用双向链表,插入/删除不只影响相邻点,但查找/修改需要从头/尾遍历。

3.3栈

  • 先进后出
  • 中缀转后缀:
    • 操作数直接转输出
    • 操作符使用栈处理
    • 新操作符与栈顶元素比较,若优先级小于等于栈顶,则栈顶弹出。然后新操作入栈,否则新操作符直接压入。
    • 对于”(“,直接入栈,只有在新操作符为“)”时,将其上所有元素弹出。
  • 链表实现和数组实现

3.4 队列

  • 先进先出
  • 数组实现
  • 用于服务器区块的响应,使用排队论解决响应与阻塞问题。

3. 树

  • 大部分树拥有 O(logN) 的平均深度。
  • 深度:从根到 nk 的最短路径,高度:从 nk 到树叶的最长路径
  • 根的深度为0,树叶的高度为0。
  • n1 nk 的节点序列为 n1 nk 的路径。
  • 三种遍历:
    • 先序遍历:先父节点,后左,最后右
    • 中序遍历:先左,后父节点,最后右
    • 后序遍历:先左,后右,最后父节点

3.1 二叉树

  • 每个节点的子节点不超过2两个
  • 一般使用二叉查找树

3.2 ADT查找树

  • 左子树每一个节点小于右子树的每一个节点
  • 二叉树类总是支持comparable 的接口,使用compareTo来比较树内的每一项
  • boolean contains(),BinaryNode findMin(),BinaryNode findMax()
  • BinaryNode insert():查找,若不重复则插入到最后查找点的左/右子节点。
  • BinaryNode remove():
    • 若删除节点只有一个子节点,该子节点替代之
    • 若有两个子节点,则寻找 右边最小项 替代之
  • 对于有重复项的树,使用额外的空间(如表)记录节点频率
  • 懒惰删除:仅标记为删除,节点频率减一,不实际从树中删去

3.3 AVL树

  • 带有平衡条件的二叉查找树
  • |H(leftTree)H(rightTree)|1
  • 除了可能的插入操作外,其他操作(查询,修改,删除)的时间复杂度都为 O(logN)
  • 插入新点后再平衡的策略为:
    • 从高度最大的不平衡子树的根节点开始:
    • 若新插入点在左儿子的左子树或右儿子的右子树,使用单旋转
      • 设不平衡子树的根节点为 k1 ,过深子树的儿子为 k2
      • k1 父节点作为 k2 父节点, k2 作为 k1 的父节点, k1 作为 k2 的左/右子节点
      • k2 的左/右节点成为 k1 的右/左节点
    • 若新插入点在左儿子的右子树或右儿子的左子树,使用双旋转,双旋转等于两次单旋转,第一次对于不平衡子树的较深子树进行,第二次对于整个不平衡子树进行
      • 设不平衡子树根节点为 k1 ,该子树的较深子树根节点为 k2 ,以 k2 作为根节点的子树的较深子树为 k3
      • 先对较深子树( k2 )进行一次单旋转,完成旋转后 k3 为该子树的根节点
      • 再对以 k1 为根节点, k3 为子节点的子树进行单旋转,结果 k3 成为根节点。

3.4 伸展树

  • 保证M次操作时间: O(MlogN)
  • 对于某一节点的访问将激活伸展操作
    • 伸展操作的结果是该节点上升到根,收益为减少下次访问时间,同时可能减小树的深度
    • 伸展操作分为“一字型”和“之字形”
    • 设操作节点为 XP,PG
    • 一字型: XPPG
    • 之字型: XPPG
    • 若为之字形,从 XG ,否则从 GX 做两次单旋转,最终结果相同, X
    • 删除时,可访问某一节点,将其升到根节点后删去,并取其左子树的最大叶节点替代

3.5 树的遍历

  • 先序遍历:中左右
  • 中序遍历:左中右
  • 后序遍历:左右中
  • 层序遍历:从上到下

3.5 B树

  • B树是具有阶数M的不定叉树
  • B树主要处理的目的是使得树在一定程度上更扁平化,因为判断分支所的计算花销要远小于读取数据指令,通过增加分叉树来减少读取数据的次数。
  • 一个具有M阶的B树具有以下特性:
    • 根节点必须是一个叶节点或拥有2到M个子节点
    • 每个非叶非根节点必须拥有 [M2,M]
    • 每个非叶节点使用M-1个关键字划分出M个子树
    • 所有叶节点拥有同样的高度
    • 每个叶节点包含一个数据块,包含 [L2,L]
  • 一个M阶B树的结构定性如下:
    • 设每个磁盘区块容量为D字节,每个数据占用X个字节
    • 对于每个节点含有M-1个关键值,占用 X(M1) ,同时分出M个子节点,使用4个字节记录每个子节点的磁盘地址。所以每个节点占用 X(M1)+4M=(X+4)MX ,受限于区块容量限制,有 (X+4)MXD ,由此解出M。
    • 对于叶节点,同样受限于区块容量限制,有 LXD,L
  • B树的插入和删除
    • B树操作的最坏时间由 logM2N 近似给出有。
  • B树的插入操作:
    • 简单的插入可能存在叶节点数据量大于 L 的问题。
    • 当叶节点容量不足以加入新数据时,向上取父节点P,若P的关键数小于M-1,添加新的关键值进行分裂。否则,再继续向上尝试分裂父节点,直到成功为止。若到达根节点,则向上增加一层。
    • 这是B树唯一的增高方式,也是根节点允许只拥有两个子树的原因。
    • 另一种方式插入方式:
      • 改变父节点的关键值,将合适的子树转入与父节点相邻的子树下。该方法使叶节点的数量区域饱和,适用于长时间运行的项目。
  • B树的删除操作:
    • 简单删除存在叶节点的数据量小于 L2 的问题
    • 同样的,可以在删除后从父节点的相邻子树领养一个数据来解决,当相邻节点已达到最小值时 L2 ,则将该节点与相邻节点合并
    • 节点合并可能会导致父节点的子树少于 M2 的情况,因此需要继续合并或领养。
    • 当合并到达根节点的时候,不允许根节点仅有一个子节点,此时删除根节点,树高度减一。
    • 这是唯一的减少树高度的方法。

3.6 Set和Map

  • Set接口是一种不允许值重复的Collection, TreeSet是保证数值有序的Set的实现
  • TreeSet提供自然排序和定制排序:
    • 自然排序默认使用Object自带的Compare函数进行比较以确定是否重复。
    • 定制排序在实例化时传入Camparetor实例来定义比较结果,0为相同
  • TreeSet由于数据的顺序性,提供first(),last(), subset(Obj, Obj), headset(Obj), tailSet(Obj), higher(Obj), lower(Obj)方法。
  • Map接口是由键值对构成的Collection,其中键是不可重复的,但是值可以,TreeMap是SortedMap的实现,其中的键是有序排列的。

4. 散列

5. 优先队列(堆)

5.1 堆概述

  • 堆的作用是对最小元素进行常数时间的读取
  • 堆一般主要用于顺序管理
  • 堆的两性:
    • 堆序性质:父节点恒小于等于左右子节点
    • 结构性质:总是完全二叉树,叶节点从左到右填满

5.2 二叉堆:

  • 二叉堆可以使用数组实现:任意节点 i 的子节点位于 2i 2i+1
  • 二叉堆的插入/上滤(最坏O(logN)):
    1. 先插入末尾空位
    2. 检查是否符合堆序性质
    3. 若不符合则与父节点换位
    4. 重复2,3直到符合堆序性质
  • 二叉堆的堆顶删除/下滤:
    1. 先删除堆顶节点
    2. 将末尾节点填入堆顶
    3. 检查是否符合堆序性质
    4. 若不符合,和较小的子节点换位
    5. 重复3,4直到满足堆序性质

5.3 左式堆

  • 左式堆满足堆的堆序性质
  • 左式堆的节点除了记录根节点,左右子节点外,还需要记录零路径长。
  • 左式堆的结构性质:
    • 零路径长npl(null path length):到最近不含双子节点的节点的距离
    • 左式堆的任意左子节点的零路径长大于等于兄弟右节点的零路径长
  • 左式堆的优点:
  • 左式堆的合并排序
    • 合并操作使用递归进行
    • 将 根节点较大的子树 H1 与 根节点较小子树的右子树 H2 合并,返回值作为 H2 的新右子树
    • 若其中一个子树为空,则返回另一子树
    • H2 左子树为空,则将 H1 作为 H2 的左子树,并返回
    • 在迭代返回中,检测每个左右节点的npl,若不满足左式堆性质,则交换左右子树,并更新npl

排序

不相交集类

图论算法

算法设计技巧

点赞