任何数据结构都可以用顺序存储和链式存储
class TreeNode<T> {
T data;
TreeNode<T> left;
TreeNode<T> right;
}
class BinTree<T>{
TreeNode<T> root;
}
最常用的存储方式
$n$个结点的二叉链表中,有$n+1$个空链域
class BinTree<T>{
T[] elements;
}
将二叉树转为完全二叉树
虚拟出空结点
将完全二叉树上编号(i)的结点存储在数组的(i-1)位置
空结点对应的数组元素存null
如果是单边树,会很浪费空间,一般不使用这种方法
class Node<T> {
T data;
int parent; //父节点的下标
}
class BinTree<T>{
Node<T>[] elements;
}
每个结点都记住父结点在数组中的下标
是数组和链表结合的存储方法
每个结点需要存储它的子结点在数组中的下标链表
class Node<T> {
T data;
LinkedList<T> children;
}
class BinTree<T>{
Node<T>[] elements;
}
将双亲表示法和孩子表示法结合起来
class Node<T> {
T data;
LinkedList<T> children;
int parent; //父节点的下标
}
class BinTree<T>{
Node<T>[] elements;
}
另一种链式存储方法
结点的左孩子为从左往右的第一个子结点,右孩子为其兄弟结点
class Node<T> {
T data;
Node<T> firstChild;
Node<T> brothers;
}
class BinTree<T>{
Node<T> root;
}
常用于树转换为二叉树,森林转换为二叉树
任何一个与树对应的二叉树,右子树都为空
先将每个树转为二叉树,再将每个二叉树根节点连接
以二叉链表结构为例
class TreeNode<T> {
T data;
TreeNode<T> left;
TreeNode<T> right;
}
class BinTree<T>{
TreeNode<T> root;
}
void preOrderTraversal(TreeNode<T> node) {
if (node == null) {
return;
}
System.out.println(node.data);
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
void preOrderTraversal() {
Stack<TreeNode<T>> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode<T> node = stack.pop();
System.out.println(node.data);
// 先push右孩子再push左孩子
// 左孩子先出栈
if(node.right != null) {
stack.push(node.right);
}
if(node.left != null) {
stack.push(node.left);
}
}
}
void inOrderTraversal(TreeNode<T> node) {
if (node == null) {
return;
}
inOrderTraversal(node.left);
System.out.println(node.data);
inOrderTraversal(node.right);
}
void inOrderTraversal() {
Stack<TreeNode<T>> stack = new Stack<>();
TreeNode<T> node = root;
while(node != null || !stack.isEmpty()){
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
System.out.println(node.data);
node = node.right;
}
}
void postOrderTraversal(TreeNode<T> node) {
if (node == null) {
return;
}
postOrderTraversal(node.left);
postOrderTraversal(node.right);
System.out.println(node.data);
}
void postOrderTraversal() {
Stack<TreeNode<T>> stack1 = new Stack<>();
Stack<TreeNode<T>> stack2 = new Stack<>();
stack1.push(root);
while (!stack1.isEmpty()) {
TreeNode<T> node = stack1.pop();
stack2.push(node);
if(node.left != null) {
stack1.push(node.left);
}
if(node.right != null) {
stack1.push(node.right);
}
}
while (!stack2.isEmpty()) {
TreeNode<T> node = stack2.pop();
traversal.apply(node.data);
}
}
void levelOrderTraversal() {
int level = 1;
while (levelOrderTraversal(root, level)) {
level++;
}
}
boolean levelOrderTraversal(TreeNode<T> node, int level) {
if (node == null) {
return false;
}
if (level == 1) {
System.out.println(node.data);
return true;
}
boolean left = levelOrderTraversal(node.left, level - 1);
boolean right = levelOrderTraversal(node.right, level - 1);
return left || right;
}
void levelOrderTraversal() {
Queue<TreeNode<T>> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode<T> node = queue.poll();
System.out.println(node.data);
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
}
深度优先遍历dfs即为先序遍历,广度优先遍历bfs即为层序遍历
如果增加一些辅助数据域,比如父结点或访问位,也可以不借助其他数据结构进行遍历
使用完全二叉树的性质,编号为$i$的结点,左孩子结点的编号为$2i$,右孩子结点的编号为$2i+1$
数组下标从0开始,树的序号从1开始,所以再加1
<T> TreeNode<T> fromLevelOrder(T[] arrays, int index) {
if (index < arrays.length) {
TreeNode<T> node = new TreeNode<>(arrays[index]);
node.left = fromLevelOrder(arrays, index * 2 + 1);
node.right = fromLevelOrder(arrays, index * 2 + 2);
return node;
}
return null;
}
<T> BinTree<T> fromLevelOrderByQueue(T[] arrays) {
BinTree<T> tree = new BinTree<>();
for (T t : arrays) {
tree.insert(t);
}
return tree;
}
void insert(T data) {
if (root == null) {
root = new TreeNode<>(data);
return;
}
Queue<TreeNode<T>> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()) {
TreeNode<T> tmp = queue.poll();
if (tmp.left == null) {
tmp.left = new TreeNode<>(data);
break;
} else {
queue.add(tmp.left);
if (tmp.right == null) {
tmp.right = new TreeNode<>(data);
break;
} else {
queue.add(tmp.right);
}
}
}
}
Huffman树的带权路径长度最短
Huffman树没有度为1的结点,是严格的二叉树
度为2的树与二叉树的区别
$n$个结点的二叉链表中有多少空链域
一个含有$n$个结点的$k$叉树,可能的最大深度和最小深度是多少
对于非叶子结点都有左右子树的二叉树,有$n$个叶子结点的树中总共多少个结点
找出满足下列条件的二叉树