FDS Collection I¶
约 1570 个字 250 行代码 预计阅读时间 8 分钟
Chapter 1 Algorithm Analysis¶
Chapter 2 Linear List¶
2.3 其他操作¶
Chapter 3 Trees¶
3.1 Concepts and Terminology¶
Degree of a node: the number of subtrees of the node.
Degree of a tree: the maximum degree of all nodes.
Parent: a node that has subtrees.
Children: the roots of the subtrees of a parent.
Silblings: the children of the same parent.
Leaf/terminal node: a node that has no children.
Path from node A to node B: a (unique) sequence of nodes starting from A and ending at B, such that each node is a child of the previous one.
Length of path: the number of edges on the path.
Depth of a node: the length of the unique path from the root to the node.
Height of a node: the length of the longest path from the node to a leaf.
Height of a tree: the height of the root.
Ancestors of a node: all nodes on the path from the node up to the root.
Descendants of a node: all nodes in its subtrees.
Every tree can be transformed into a binary tree with FirstChild-NextSibling representation.
3.2 Binary Trees¶
A binary tree is a tree in which no node can have more than two children.
可以使用 First-Child-Next-Sibling 表示法来将任意树转换为二叉树,将 FirstChild 作为左子节点,NextSibling 作为右子节点。
Expression Trees: 每个节点都是一个操作符,叶子节点是操作数,可以表示算数表达式。
3.2.1 节点实现¶
3.2.2 基本操作¶
Info
这种版本的节点的定义包括了节点的高度,但是在一般的树中,这是没有必要的,只是在AVL树中,节点高度才尤其重要.在下面的操作中就干脆省略了.
3.2.3 遍历¶
遍历时间复杂度都是\(O(n)\),迭代的空间复杂度是\(O(n)\).
层序遍历从顶部到底逐层遍历二叉树,并且按照从左到右的顺序遍历每一层的节点,从本质上来讲,层序遍历其实属于广度优先遍历/Breadth-first Traversal.我们一般借助队列来实现层序遍历.
先访问根节点,再访问子树,左子树优先于右子树.
先访问子树,左子树先于右子树,再访问根节点.
先访问左子树,再访问根,最后访问右子树.
什么?你还想折磨自己?
你需要在循环里边手动建一个堆栈来模仿系统堆栈的行为,想想都觉得受不了,消停写你的迭代版得了。
3.3 Binary Search Trees¶
二叉搜索树满足以下性质:
- 所有的节点都有一个以整数表示的键值,且互不相同;
- 所有节点的左节点的值都小于这个节点的值;
- 所有节点的右节点的值都大于这个节点的值;
- 所有节点的左右子树都是二叉搜索树;
- 二叉搜索树的中序遍历是一个递增的序列。
删除分为三种情况:
- 删除叶节点:直接删除即可;
- 删除只有一个儿子的节点:直接删除,然后把儿子接上;
- 删除有两个儿子的节点:
- 将该节点替换为左子树的最大值,或右子树的最小值;
- 递归删除左子树的最大值,或右子树的最小值。
3.4 Threaded Binary Trees¶
- 如果一个节点的左子节点为空的,那么将左指针指向中序遍历的前驱节点;
- 如果一个节点的右子节点为空的,那么将右指针指向中序遍历的后继节点。
- 一定有一个 Head Node,其左子节点是根,右子节点是自身;被中序遍历的第一个节点的左指针和最后一个节点的右指针共同指向。
3.5 Heap/Priority Queue¶
3.5.1 二叉堆¶
二叉堆首先是一个满足堆性质的完全二叉树,对于最大堆为例,所谓堆性质是:某个树的所有子树的根节点值都大于等于子节点值。同理,我们可以定义最小堆。
3.5.2 堆的实现¶
堆其实是一个完全二叉树,完全二叉树又很容易表示成数组,所以堆基于数组来实现,但是存储数组的时候很有讲究:数组的元素代表二叉树的节点值,索引代表层序遍历中节点在二叉树中的位置,所有的索引将由 1
开始,这样就可以很方便的通过索引来找到节点的父节点和子节点。数据结构如下:
下面是获取父节点、左子节点、右子节点的函数:
3.5.3 基本操作¶
典中典:线性建堆算法,时间复杂度为 \(O(n)\)。
上滤和下滤,逻辑是先把要移动的元素存起来,然后慢慢腾位置,最后把原来的值放到合适的位置。这里的代码是最小堆的代码。
3.6 Disjoint Set¶
并查集支持两种操作:
- Union:合并两个集合,或者说将一棵树的根节点作为另一棵树的根节点的子节点。
- Find:查找某个元素所在的集合,或者查询两个元素是否属于同一个集合,这要求我们找到这个节点的根节点。
使用数组来表示并查集,S[i]
的值表示节点 i
的父节点,一般会将父节点作为负数,其绝对值表示父节点代表的集合的大小。
3.6.1 路径压缩¶
在查找的过程中将路径上的所有节点都指向根节点,这样可以减少查找的时间复杂度与树的高度。
或者一种更简单的方法:
以循环实现:
3.6.2 按大小合并¶
将节点数较小的树合并到节点数较大的树上。
按大小压缩形成的拥有 \(N\) 个节点的树的高度不会超过 \(\log_{2}N+1\),因此进行 \(N\) 次 Union 操作和 \(M\) 次 Find 操作的时间复杂度为 \(O(N + M\log_{2}N)\)。
3.6.3 按高度合并¶
按高度合并需要记录树的高度,将高度较小的树合并到高度较大的树上。
按高度合并不能和压缩路径一起使用。