前面介绍的 树 是普通树,父节点可以有多个子节点。二叉树是父节点最多只有两个子节点的树。子节点中左边的节点称为左节点,右边的节点称为右节点。
下图就是一棵二叉树
实现
代码实现其实和普通树差不多,只是子节点需要修改一下,下面是实现代码:
class BinaryNode<Element> {
var value: Element
var leftChild: BinaryNode? // 左节点
var rightChild: BinaryNode? // 右节点
init(value: Element) {
self.value = value
}
}
上面二叉树结构图的代码:
var tree: BinaryNode<Int> = {
var zero = BinaryNode(value: 0)
var two = BinaryNode(value: 2)
var three = BinaryNode(value: 3)
var five = BinaryNode(value: 5)
var seven = BinaryNode(value: 7)
var eight = BinaryNode(value: 8)
two.leftChild = zero
two.rightChild = three
five.leftChild = two
five.rightChild = seven
seven.rightChild = eight
return five
}()
一个辅助输出
为了更好的查看二叉树的输出,下面二叉树的辅助输出代码:
extension BinaryNode: CustomStringConvertible {
public var description: String {
diagram(for: self)
}
private func diagram(for node: BinaryNode?,
_ top: String = "",
_ root: String = "",
_ bottom: String = "") -> String {
guard let node = node else {
return root + "nil\n"
}
if node.leftChild == nil && node.rightChild == nil {
return root + "\(node.value)\n"
}
return diagram(for: node.rightChild,
top + " ", top + "┌──", top + "│ ")
+ root + "\(node.value)\n"
+ diagram(for: node.leftChild,
bottom + "│ ", bottom + "└──", bottom + " ")
}
}
查看刚刚创建的树是这样的:
print(tree)
输出:
┌──8
┌──7
│ └──nil
5
│ ┌──3
└──2
└──0
遍历
在前面介绍树的时候我们实现了深度优先遍历和层级优先遍历。下面对二叉树实现不一样的遍历。
中序遍历
-
如果当前节点有左节点,递归遍历访问完左节点;
-
访问当前节点;
-
如果当前节点有右节点,递归遍历访问完右节点。
访问顺序为:左节点 --> 当前节点 --> 右节点。
实现:
extension BinaryNode {
func traverseInOrder(visit: (Element) -> Void) {
leftChild?.traverseInOrder(visit: visit)
visit(value)
rightChild?.traverseInOrder(visit: visit)
}
}
测试:
tree.traverseInOrder(visit: { print($0) })
输出:
0
2
3
5
7
8
前序遍历
-
访问当前节点;
-
如果当前节点有左节点,递归遍历访问完左节点;
-
如果当前节点有右节点,递归遍历访问完右节点。
访问顺序为:当前节点 --> 左节点 --> 右节点。
实现:
extension BinaryNode {
func traversePreOrder(visit: (Element) -> Void) {
visit(value)
leftChild?.traversePreOrder(visit: visit)
rightChild?.traversePreOrder(visit: visit)
}
}
测试:
tree.traversePreOrder(visit: { print($0) })
输出:
5
2
0
3
7
8
后序遍历
-
如果当前节点有左节点,递归遍历访问完左节点;
-
如果当前节点有右节点,递归遍历访问完右节点;
-
访问当前节点。
访问顺序为:左节点 --> 右节点 --> 当前节点。
实现:
extension BinaryNode {
func traversePostOrder(visit: (Element) -> Void) {
leftChild?.traversePostOrder(visit: visit)
rightChild?.traversePostOrder(visit: visit)
visit(value)
}
}
测试:
tree.traversePostOrder(visit: { print($0) })
输出:
0
3
2
8
7
5
练习
计算树的高度:
如果只有一个根节点的树,高度为0;
如果有一个子节点的树,高度为1.
实现的关键是递归计算左节点和右节点的最大高度
实现:
extension BinaryNode {
// O(n)
var height: Int {
if leftChild == nil && rightChild == nil { return 0 }
return 1 + max(leftChild?.height ?? 0, rightChild?.height ?? 0)
}
}