树(Tree)是由多个节点(Node)的集合组成,每个节点又有多个与其关联的子节点(Child Node)。子节点就是直接处于节点之下的节点,而父节点(Parent Node)则位于节点直接关联的上方。树的根(Root)指的是一个没有父节点的单独的节点。
所有的树都呈现了一些共有的性质:
- 只有一个根节点;
- 除了根节点,所有节点都有且仅有一个父节点;
- 无环。将任意一个节点作为起始节点,都不存在任何回到该起始节点的路径(正是前两个性质保证了无环的成立)。
二叉树及其性质
二叉树(Binary Tree): 每个节点最多有两个子节点,分别称为左子节点和右子节点。
-
性质1:二叉树的第i层上至多有2i-1(i≥1)个节点。
-
性质2:深度为h的二叉树中至多含有2h-1个节点。
-
性质3:若在任意一棵二叉树中,有n0个叶子节点,有n2个度为2的节点,则必有n0=n2+1。
-
性质4:具有n个节点的满二叉树深为log2n+1。
-
性质5:若对一棵有n个节点的完全二叉树进行顺序编号(1≤i≤n),那么,对于编号为i(i≥1)的节点:
-
当i=1时,该节点为根,它无双亲节点。
-
当i>1时,该节点的双亲节点的编号为i/2。
-
若2i≤n,则有编号为2i的左节点,否则没有左节点。
-
若2i+1≤n,则有编号为2i+1的右节点,否则没有右节点。
-
完全二叉树
树中的结点按从上至下、从左到右的顺序进行编号
除了最后一层外,每一层的节点都是满的,并且最后一层的节点都尽可能地靠左排列。
若从上至下、从左至右编号,则编号为 i 的节点,其左子节点编号必为 2i,其右子节点编号必为 2i+1;其父节点的编号必为 i/2(i=1 时为根,除外)
满二叉树
每个节点都有 0 或 2 个子节点。
二叉树数组实现
使用数组(顺序表)实现二叉树。数组元素表示二叉树的节点, 利用数组的索引关系构建二叉树的结构。
一般来说,数组中节点的索引从 1 开始,那么给定节点的索引为 i,其左子节点的索引为 2*i,右子节点的索引为 2*i + 1。
import 'dart:core';
class BinaryTree {
late List _trees;
BinaryTree(int size) {
_trees = List.generate(size, (index) => null);
}
// 插入节点
void insert(int index, int value) {
if (index > _trees.length - 1 || index < 1) {
throw RangeError('Index out of bounds');
}
_trees[index] = value;
}
// 获取根节点
int? getRoot() {
if (_trees.isNotEmpty) {
return _trees[1];
}
return null;
}
// 获取指定节点的左子节点
int? getLeftChild(int index) {
int leftChildIndex = 2 * index;
if (leftChildIndex < _trees.length) {
return _trees[leftChildIndex];
}
return null;
}
// 获取指定节点的右子节点
int? getRightChild(int index) {
int rightChildIndex = 2 * index + 1;
if (rightChildIndex < _trees.length) {
return _trees[rightChildIndex];
}
return null;
}
// 打印
display() {
print('_trees: $_trees');
}
}
void main() {
BinaryTree binaryTree = BinaryTree(10);
binaryTree.insert(1, 1);
binaryTree.insert(2, 2);
binaryTree.insert(3, 3);
binaryTree.insert(4, 4);
binaryTree.insert(5, 5);
print('Root: ${binaryTree.getRoot()}');
print('Left child of root: ${binaryTree.getLeftChild(1)}');
print('Right child of root: ${binaryTree.getRightChild(1)}');
}
二叉树队列实现
使用队列实现二叉树的构建、遍历和其他操作。队列是一种先进先出(FIFO)的数据结构,非常适合用于广度优先遍历(BFS)二叉树。
class TreeNode {
int value;
TreeNode? left;
TreeNode? right;
TreeNode(this.value);
}
class BinaryTree {
TreeNode? root;
// 插入节点
void insert(int value) {
root = _insertRec(root, value);
}
TreeNode _insertRec(TreeNode? root, int value) {
if (root == null) {
root = TreeNode(value);
return root;
}
if (value < root.value) {
root.left = _insertRec(root.left, value);
} else if (value > root.value) {
root.right = _insertRec(root.right, value);
}
return root;
}
// 广度优先遍历
void bfs() {
if (root == null) return;
Queue<TreeNode> queue = Queue<TreeNode>();
queue.add(root!);
while (queue.isNotEmpty) {
TreeNode current = queue.removeFirst();
print(current.value);
if (current.left != null) {
queue.add(current.left!);
}
if (current.right != null) {
queue.add(current.right!);
}
}
}
}
void main() {
BinaryTree binaryTree = BinaryTree();
binaryTree.insert(4);
binaryTree.insert(2);
binaryTree.insert(6);
binaryTree.insert(1);
binaryTree.insert(3);
binaryTree.insert(5);
binaryTree.insert(7);
binaryTree.bfs(); // 广度优先遍历
}
广度优先遍历(Breadth-First Traversal),简称 BFS,是一种用于遍历或搜索树或图的算法。在二叉树中,广度优先遍历是按层级顺序逐层遍历节点,从根节点开始,先访问根节点,然后逐层访问每一层的节点,直到遍历完所有节点为止。