算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张

一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护.

14.1 动态顺序统计

顺序统计树(order-static tree) : 在红黑树的基础上, 在每个结点加上一个size域表示以该结点为根的子树(包括该结点本身)的(内)结点树, 而哨兵T.nil(叶结点)的size域定义为0, 故有等式\(x.size=x.left.size + x.right.size + 1\) .

顺序统计树可在O(lgn)时间内确定任何顺序统计量, 也可在O(lgn)时间内确定任何元素的秩, 即它在集合线性序中的位置.

在一棵顺序统计树中, 并不要求关键字各不相同, 若有关键字相同, 则秩定义为在中序遍历树时输出的位置.

查找具有给定秩的元素

# 返回以x为根的子树中秩为i的结点
os_select(x,i):
  r == x.left.size + 1
  if i == r:
    return x
  else if i < r:
    return os_select(x.left, i)
  else:
    return os_select(x.right, i-r)

每次递归调用都在顺序统计树下降一层, 故os_select运行时间为O(h)即O(lgn).

确定一个元素的秩

# 返回树T中x在中序遍历T对应的线性序中x的位置
os_rank(T,x):
  r = x.left.size + 1
  y = x
  # while循环保持下列不变式:
  # 每次循环开始前, r为以y为根的子树中x的秩
  while y != T.root:
    if y == y.p.right:
      r = r + y.p.left.size + 1
    y = y.p
  return r

对子树规模的维护

对红黑树的插入删除大体分为两个阶段: 寻找插入位置, 再作着色和旋转操作以保持红黑结构. 为了维护顺序统计数的附加信息, 在第一阶段, 对从根到插入位置路径上的每一个结点, 将size加1, 而新结点的size置1. 对第二阶段, 着色并不影响树型结构, 只有旋转影响局部结构, 只需要在旋转操作的基础上加上以下代码:

# 对 left_rotation(T,x) 和 right_rotation(T,x)
y.size = x.size
x.size = x.left.size + x.right.size + 1

显然维护顺序统计树只需要O(lgn)时间.

14.2 如何扩张数据结构

扩张数据结构的一般步骤:

  • 选择一种基础数据结构.
  • 确定基础数据结构中要维护的附加信息.
  • 检验基础数据结构上的基本修改操作能否维护附加信息.
  • 设计一些新操作.

有时扩张数据结构并不是为了设计新操作, 而是利用附加信息来加速已有的操作.

定理14.1(红黑树的扩张) : 设\(f\) 是n个结点的红黑树\(T\) 扩张的属性, 且假设对任意结点\(x\) , \(f\) 的值仅依赖于结点\(x\) , \(x.left\) 和 \(x.right\) 的信息, 还可能包括\(x.left.f\) 和\(x.right.f\) . 那么, 我们可以在插入和删除期间对\(T\) 的所有结点的\(f\) 值进行维护, 并且不影响和两个操作的O(lgn)渐近时间性能.

14.3 区间树

闭区间\([t_1,t_2]\) 可表示成一个对象\(i\) , 其中\(i.low = t_1\) 为低端点, \(i.high = t_2\) 为高端点.

任何两个(闭区间)\(i\) 和\(i^{‘}\) 满足区间三分律:

  • \(i\) 和\(i^{‘}\) 重叠: \(且i.low \le i^{‘}.high 且 i^{‘}.low \le i.high\) .
  • \(i\) 在\(i^{‘}\) 左边: \(i.high < i^{‘}.low\) .
  • \(i\) 在\(i^{‘}\) 右边: \(i^{‘}.high < i.low\) .

对于开区间和半开区间, 三分律依然成立, 只是条件中的取等有变化.

区间树(interval tree) : 一种对动态集合进行维护的红黑树, 其中每个元素\(x\) 都包含一个区间\(x.int\) . 区间树表达集合的具体方式为: 取\(x.int.low\) 为中序遍历(线性序)用的关键字; 每个结点\(x\) 中除了自身区间信息外, 还包含附加信息\(x.max\), 它是以\(x\) 为根的子树中所有区间的端点的最大值.

由于\(x.max = max(x.int.high, x.left.max, x.right.max)\) , 由定理14.1, 插入和删除操作时间为O(lgn).

区间树支持的操作:

  • interval_insert(T,x): 将包含区间属性int的元素x插入到区间树T中
  • interval_delete(T,x): 从区间树T中删除元素x
  • interval_search(T,i): 返回一个指向区间树T中元素x的指针, 使x.int与i重叠. 若此元素不存在则返回T.nil.
interval_search(T,i):
  x = T.root
  # while循环保证寻找方向是对的
  # 即如果存在与i重叠的区间, 则在哪棵子树上找一定可以找到
  while (x != T.nil) and (i does not overlap x.int):
    # 由于x的左子树上的区间的最大端点值比i的低端点大
    # 如果左子树上没有与i重叠的区间,那么可以推出
    # 对右子树上任一区间, 其低端点比i的高端点大
    # 故只能在左子树上找
    if (x.left != T.nil) and (x.left.max >= i.low):
      x = x.left
    # 如果左子树为空, 必然只能在右子树上找
    # 如果左子树上的区间的最大端点值都比i的低端点小, 左子树上也必然没有与i重叠的区间
    else:
      x = x.right
  return x
时间: 2024-08-10 15:09:28

算法导论读书笔记-第十四章-数据结构的扩张的相关文章

C primer plus 读书笔记第十四章

这一章主要介绍C语言的结构和其他数据形式,是学习算法和数据结构的重点. 1.示例代码 /*book.c -- 仅包含一本书的图书目录*/ #include <stdio.h> #define MAXTITL 41 #define MAXAUTL 31 struct book { /* data */ char title[MAXTITL]; char author[MAXAUTL]; float value; }; int main(void) { struct book library; /

算法导论笔记——第十二~十四章 数据结构(二)树

第十二章 二叉搜索树 >=左子树的所有key,<=右子树的所有key 在一棵高度为h的二叉搜索树上,动态集合上的操作SEARCH,MINIMUM,MAXIMUM,SUCCESSOR,PREDECESSOR,INSERT和DELETE可以在O(h)时间内完成. h>=(lgn向下取整) 和快速排序算法一样,其平均性能更接近于最好情形. 随机构建二叉搜索树期望高度为O(lgn). 各种操作请自行查阅. 第十三章 红黑树 是一种(近似)平衡的二叉搜索树.可以保证在最坏情况下基本动态集合操作的时

算法导论读书笔记(16)

算法导论读书笔记(16) 目录 动态顺序统计 检索具有给定排序的元素 确定一个元素的秩 区间树 步骤1:基础数据结构 步骤2:附加信息 步骤3:维护信息 步骤4:设计新操作 动态顺序统计 之前介绍过 顺序统计 的概念.在一个无序的集合中,任意的顺序统计量都可以在 O ( n )时间内找到.而这里我们将介绍如何在 O ( lg n )时间内确定任意的顺序统计量. 下图显示的是一种支持快速顺序统计量操作的数据结构.一棵 顺序统计树 T 通过在红黑树的每个结点中存入附加信息而成.在一个结点 x 内,增

算法导论读书笔记(13)

算法导论读书笔记(13) 目录 红黑树 旋转 插入 情况1 : z 的叔父结点 y 是红色的 情况2 : z 的叔父结点 y 是黑色的,而且 z 是右孩子 情况3 : z 的叔父结点 y 是黑色的,而且 z 是左孩子 删除 情况1 : x 的兄弟 w 是红色的 情况2 : x 的兄弟 w 是黑色的,且 w 的两个孩子都是黑色的 情况3 : x 的兄弟 w 是黑色的, w 的左孩子是红色的,右孩子是黑色的 情况4 : x 的兄弟 w 是黑色的,且 w 的右孩子是红色的 红黑树 红黑树 是一种二叉查

算法导论读书笔记之钢条切割问题

算法导论读书笔记之钢条切割问题 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 给定一段长度为n英寸的钢条和一个价格表 pi (i=1,2, -,n),求切割钢条的方案,使得销售收益rn最大.注意,如果长度为n英寸的钢条价格pn足够大,最优解可能就是完全不需要切割. 若钢条的长度为i,则钢条的价格为Pi,如何对给定长度的钢条进行切割能得到最大收益? 长度i   1   2    3   4     5      6     7     8  

算法导论读书笔记(15) - 红黑树的具体实现

算法导论读书笔记(15) - 红黑树的具体实现 目录 红黑树的简单Java实现 红黑树的简单Java实现 /** * 红黑树 * * 部分代码参考自TreeMap源码 */ public class RedBlackTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; private static

算法导论读书笔记(17)

算法导论读书笔记(17) 目录 动态规划概述 钢条切割 自顶向下的递归实现 使用动态规划解决钢条切割问题 子问题图 重构解 钢条切割问题的简单Java实现 动态规划概述 和分治法一样, 动态规划 (dynamic programming)是通过组合子问题的解而解决整个问题的.分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题并不独立的情况,即各子问题包含公共的子子问题.在这种情况下,分治法会重复地求解公共的子子问题.而动态

算法导论读书笔记(14) - 二叉查找树的具体实现

算法导论读书笔记(14) - 二叉查找树的具体实现 目录 二叉查找树的简单Java实现 二叉查找树的简单Java实现 /** * 二叉查找树 * 部分代码参考自TreeMap的源码 */ public class BinarySearchTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; pub

算法导论读书笔记(18)

算法导论读书笔记(18) 目录 最长公共子序列 步骤1:描述最长公共子序列的特征 步骤2:一个递归解 步骤3:计算LCS的长度 步骤4:构造LCS LCS问题的简单Java实现 最长公共子序列 某给定序列的子序列,就是将给定序列中零个或多个元素去掉后得到的结果.其形式化定义如下:给定一个序列 X = < x1 , x2 , - , xm >,另一个序列 Z = < z1 , z2 , - , zk >,如果 Z 满足如下条件则称 Z 为 X 的 子序列 (subsequence),