树
基本概念
节点: 树上的每个元素
根节点: 每棵树只有一个根节点
父节点: 上一层为下一层的父节点
子节点: 与上条相反
兄弟节点: 同一父节点的子节点
节点的度: 子树的个数, 例如1 的度为 5
树的度: 所有节点度中的最大值, 例如1 的度为5, 树中的最大值, 即为树的度
叶子节点: 度为0 的节点, 例如, 221, 222, 223
非叶子节点: 度不为0 的节点
层数: 根节点在第一层, 根节点的子节点在第二层
节点的深度: 从根节点到当前节点的唯一路径上的节点总数
节点的高度: 从当前节点到最远叶子节点的路径上的节点总数
树的深度: 所有节点深度中的最大值
树的高度: 所有节点高度中的最大值
树的深度等于树的高度
有序树: 树中的任意节点的自己诶但之间有顺序关系
无序树: 树中任意节点的子节点之间没有顺序关系, 也称为自由树
二叉树(Binary Tree)
二叉树的特点
每个节点的度最大为2
左右子树时有顺序的
即使某节点只有一颗子树, 也要区分左右子树
二叉树为有序树
二叉树的性质
非空二叉树的第i 层, 最多有2^(i-1) 个节点(i >= 1)
在高度为h 的二叉树上最多有2^h - 1 个节点(h >= 1)
对于任何一颗非空二叉树, 如果叶子节点个数为n0, 度为2 的节点个数为n2, 则, n0 = n2 + 1
假设度为1 的节点个数为n1, 那么二叉树的节点总数n = n0 + n1 + n2
二叉树的边数T = n1 + 2*n2 = n - 1= n0 + n1 + n2 - 1
因此n0 = n2 +1
真二叉树(Proper Binary Tree)
所有节点的度要么为0, 要么为2
满二叉树(Full Binary Tree)
所有节点的度要么为0, 要么为2, 最后一层节点的度为0, 其他节点的度都为2
同样高度的二叉树中, 满二叉树的叶子节点数量最多, 总结点数量最多
满二叉树一定是真二叉树, 真二叉树不一定是满二叉树
第i 层节点数量: 2^(i-1)
叶子节点数量: 2^(h-1)
总节点数量n
n = 2^h - 1
h = log2(n+1)
完全二叉树(Complete Binary Tree)
对节点从上至下, 左至右开始编号, 其所有编号都能与相同高度的满二叉树中的编号对应
叶子节点只会出现最后2层, 最后1 层的叶子节点都靠左对齐
完全二叉树从根节点至倒数第2层是一颗满二叉树
满二叉树一定是完全二叉树, 完全二叉树不一定是满二叉树
度为1 的节点只有左子树
度为1 的节点要么是1 个, 要么是0 个
同样节点数量的二叉树, 完全二叉树的高度最小
假设完全二叉树的高度为h, 那么
至少有2^(h-1) 个节点
对多有2^h - 1 个节点
总结点数量为n
2^(h-1) <= n < 2^h
h - 1 <= log2(n) < h
h = floor(log2(n)) + 1
二叉搜索树(Binary Search Tree)
二叉搜索树时二叉树的一种, 是应用非常广泛的一种二叉树, 简称 BST
又被称为二叉查找树, 二叉排序树
任意一个节点的值都大于其左子树所有节点的值
任意一个节点的值都小于其右子树所有节点的值
它的左右子树也是一颗二叉搜索树
二叉搜索树存储的元素必须具备可比较性
如果是自定义的类型, 需要指定的比较方式, 不允许为null
二叉树的遍历
根据节点访问顺序不同, 二叉树的常见遍历方式有4 种
- 前序遍历(Preorder Traversal)
- 中序遍历(Inorder Traversal)
- 后序遍历(Postorder Traversal)
- 层序遍历(Level Order Traversal)
前序遍历
访问顺序: 根节点 -> 前序遍历左子树 -> 前序遍历右子树
/**
* 前序遍历
*/
public void preorderTraversal() {
preorderTraversal(root);
}
private void preorderTraversal(Node node) {
if (node == null) return;
// 此处可以做要做的一些操作
System.out.println(node.element);
preorderTraversal(node.left);
preorderTraversal(node.right);
}
中序遍历
中序遍历左子树 -> 根节点 -> 中序遍历右子树
二叉搜索树的中序遍历结果是升序或者降序
/**
* 中序遍历
*/
public void inorderTraversal() {
inorderTraversal(root);
}
private void inorderTraversal(Node node) {
if (node == null) return;
inorderTraversal(node.left);
// 此处可以做要做的一些操作
System.out.println(node.element);
inorderTraversal(node.right);
}
后序遍历
后序遍历左子树 -> 后序遍历右子树 -> 根节点
/**
* 后序遍历
*/
public void postorderTraversal() {
postorderTraversal(root);
}
private void postorderTraversal(Node node) {
if (node == null) return;
postorderTraversal(node.left);
postorderTraversal(node.right);
// 此处可以做要做的一些操作
System.out.println(node.element);
}
层序遍历
从上到下 -> 从左到右, 以此访问每一个节点
/**
* 层序遍历
*/
public void levelorderTraversal() {
if (root == null) return;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
// 此处可以做要做的一些操作
System.out.println(node.element);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
遍历的应用
-
前序遍历
展示树状结构
-
中序遍历
二叉搜索树的中序遍历按升序或者降序处理
-
后序遍历
适用于一些先子后父的操作
-
层序遍历
计算二叉树的高度
判断一棵树是否为完全二叉树
树高度计算
// 使用递归计算
public int height2() {
return height(root);
}
// 递归调用
private int height(Node node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
// 使用层序遍历, 使用队列记录每一次的元素数量, 每取出头, 减一, 为零时则说明, 这一层已经遍历完成, 高度加一
public int height() {
if (root == null) return 0;
// 高度
int height = 0;
// 存储每一层的元素数量
int levelSize = 1;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
levelSize--;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (levelSize == 0) {
levelSize = queue.size();
height++;
}
}
return height;
}
是否是完全二叉树
- 如果树为空, 则返回false
- 如果不为空, 开始层序遍历二叉树, 使用队列
- 如果node.left != null, 将node.left 入队
- 如果node.left == null && node.right != null, 返回false
- 如果node.right != null, 将node.right 入队
- 如果node.right == null, 之后的遍历节点都是叶子结点, 才保证是完全二叉树, 否则false
- 遍历完成, 返回true
/**
* 判断是否完全二叉树
* @return
*/
public boolean isComplete() {
if (root == null)
return false;
Queue> queue = new LinkedList<>();
queue.offer(root);
boolean leaf = false;
while (!queue.isEmpty()) {
Node node = queue.poll();
if (leaf == true && !node.isLeaf()) {
return false;
}
if (node.left != null) {
queue.offer(node.left);
} else if (node.right != null) {
return false;
}
if (node.right != null) {
queue.offer(node.right);
} else {// node.right == null
leaf = true;
}
}
return true;
}
前驱后继节点
前驱结点: 中序遍历时的前一个节点
如果是二叉搜索树, 前驱节点是前一个比它小的值
后继节点: 中序遍历时的后一个节点
如果是二叉搜索树, 后继节点就是后一个比它大的节点
// 前驱节点
private Node predecessor(Node node) {
Node p = node.left;
// 前驱节点在左子树中, left.right.right.....
if (p != null) {
while (p.right != null) {
p = p.right;
}
return p;
}
// 从父节点, 祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.left) {
node = node.parent;
}
return node.parent;
}
// 后继节点
private Node successor(Node node) {
Node p = node.right;
// 后继节点在右子树中, right.left.left.....
if (p != null) {
while (p.left != null) {
p = p.left;
}
return p;
}
// 从父节点, 祖父节点中寻找后继节点
while (node.parent != null && node == node.parent.right) {
node = node.parent;
}
return node.parent;
}
删除节点
-
叶子节点
直接删除
node == node.parent.left, node.parent.left = null
node == node.parent.right, node.parent.right = null
node.parent == null, root == null
-
度为1 的节点
-
用子节点替代原节点的位置
-
child 是node.left 或者child 时node.right
用child 替代node 的位置
- 如果node 时左子节点
child.parent = node.parent
node.parent.left = child
- 如果node 的右子节点
child.parent = node.parent
node.parent.right = child
- 如果node 是根节点
root = child
child.parent = null
-
-
-
删除节点, 度为2 的节点
先用前驱或者后继节点的值覆盖原节点的值
然后删除相应的前驱或者后继的节点
如果一个节点的度为2 , 那么它的前驱或者后继节点的度只可能是1 或者0
// 删除节点
private void remove(Node node) {
if (node == null) return;
size--;
if (node.hasTwoChildren()) {// 度为2 的节点
// 找到后继节点
Node s = successor(node);
// 用后继节点的值覆盖度为2 的节点
node.element = s.element;
// 删除后继节点, 替换掉这个节点, 在下一步中删除它
node = s;
}
// 删除node 的节点, 度为1 或者0
Node replacement = node.left != null ? node.left : node.right;
if (replacement != null) { // 度为1 的节点
// 更改parent
replacement.parent = node.parent;
if (node.parent == null) {// node 是度为1 的节点且是根节点
root = replacement;
} else if (node == node.parent.left) {
node.parent.left = replacement;
} else {
node.parent.right = replacement;
}
} else if (node.parent == null) {// 叶子节点, 且为根节点
root = null;
} else {// node 是叶子节点, 但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { // node == node.parent.right
node.parent.right = null;
}
}
}
二叉树搜索树
接口设计
BinaryTree 代码
import java.util.LinkedList;
import java.util.Queue;
import com.yw.printer.BinaryTreeInfo;
@SuppressWarnings("unchecked")
public class BinaryTree implements BinaryTreeInfo {
protected int size;
protected Node root;
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public void clear() {
root = null;
size = 0;
}
public void preOrder(Visitor visitor) {
preOrder(root, visitor);
}
private void preOrder(Node node, Visitor visitor) {
if (node == null || visitor == null) return;
visitor.visit(node.element);
preOrder(node.left, visitor);
preOrder(node.right, visitor);
}
public void inOrder(Visitor visitor) {
inOrder(root, visitor);
}
private void inOrder(Node node, Visitor visitor) {
if (node == null || visitor == null) return;
inOrder(node.left, visitor);
visitor.visit(node.element);
inOrder(node.right, visitor);
}
public void postOrder(Visitor visitor) {
postOrder(root, visitor);
}
private void postOrder(Node node, Visitor visitor) {
if (node == null || visitor == null) return;
postOrder(node.left, visitor);
postOrder(node.right, visitor);
visitor.visit(node.element);
}
public void levelOrder(Visitor visitor) {
if (root == null || visitor == null) return;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
visitor.visit(node.element);
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
}
/**
* 判断是否完全二叉树
* @return
*/
public boolean isComplete() {
if (root == null)
return false;
Queue> queue = new LinkedList<>();
queue.offer(root);
boolean leaf = false;
while (!queue.isEmpty()) {
Node node = queue.poll();
if (leaf == true && !node.isLeaf()) {
return false;
}
if (node.left != null) {
queue.offer(node.left);
} else if (node.right != null) {
return false;
}
if (node.right != null) {
queue.offer(node.right);
} else {// node.right == null
leaf = true;
}
}
return true;
}
// /**
// * 判断是否完全二叉树
// * @return
// */
// public boolean isComplete() {
// if (root == null) return false;
// Queue> queue = new LinkedList<>();
// queue.offer(root);
// boolean leaf = false;
// while (!queue.isEmpty()) {
// Node node = queue.poll();
// if (leaf == true && !node.isLeaf()) {
// return false;
// }
//
// if (node.hasTwoChildren()) {
// queue.offer(node.left);
// queue.offer(node.right);
// } else if (node.left == null && node.right != null) {
// return false;
// } else {
// leaf = true;
// if (node.left != null) {
// queue.offer(node.left);
// }
// }
//// if (node.left != null) {
//// queue.offer(node.left);
//// }
//// if (node.right != null) {
//// queue.offer(node.right);
//// }
// }
// return true;
// }
public int height() {
if (root == null) return 0;
// 高度
int height = 0;
// 存储每一层的元素数量
int levelSize = 1;
Queue> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
Node node = queue.poll();
levelSize--;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
if (levelSize == 0) {
levelSize = queue.size();
height++;
}
}
return height;
}
public int height2() {
return height(root);
}
// 递归调用
private int height(Node node) {
if (node == null) return 0;
return 1 + Math.max(height(node.left), height(node.right));
}
protected Node predecessor(Node node) {
Node p = node.left;
// 前驱节点在左子树中, left.right.right.....
if (p != null) {
while (p.right != null) {
p = p.right;
}
return p;
}
// 从父节点, 祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.left) {
node = node.parent;
}
return node.parent;
}
protected Node successor(Node node) {
Node p = node.right;
// 前驱节点在左子树中, right.left.left.....
if (p != null) {
while (p.left != null) {
p = p.left;
}
return p;
}
// 从父节点, 祖父节点中寻找前驱节点
while (node.parent != null && node == node.parent.right) {
node = node.parent;
}
return node.parent;
}
public static interface Visitor {
void visit(E element);
}
protected static class Node {
E element;
Node left;
Node right;
Node parent;
public Node(E element, Node parent) {
this.element = element;
this.parent = parent;
}
public boolean isLeaf() {
return left == null && right == null;
}
public boolean hasTwoChildren() {
return left != null && right != null;
}
}
@Override
public Object root() {
// TODO Auto-generated method stub
return root;
}
@Override
public Object left(Object node) {
// TODO Auto-generated method stub
return ((Node)node).left;
}
@Override
public Object right(Object node) {
// TODO Auto-generated method stub
return ((Node)node).right;
}
@Override
public Object string(Object node) {
// TODO Auto-generated method stub
Node myNode = ((Node)node);
String parentStr = "null";
if(myNode.parent != null){
parentStr = myNode.parent.element.toString();
}
return myNode.element + "_p(" + parentStr + ")";
}
}
BST 代码
import java.util.Comparator;
@SuppressWarnings("unchecked")
public class BST extends BinaryTree {
private Comparator comparator;
public BST() {
this(null);
}
public BST(Comparator comparator) {
this.comparator = comparator;
}
public void add(E element) {
elementNotNullCheck(element);
// 只有一个根节点
if (root == null) {
root = new Node<>(element, null);
size++;
return;
}
// 其他节点
// 找到父节点
Node parent = root;
Node node = root;
int cmp = 0;
while (node != null) {
cmp = compare(element, node.element);
parent = node;
if (cmp > 0) {
node = node.right;
} else if (cmp < 0) {
node = node.left;
} else {
node.element = element;
return;
}
}
Node newNode = new Node<>(element, parent);
if (cmp > 0) {
parent.right = newNode;
} else {
parent.left = newNode;
}
size++;
}
public void remove(E element) {
remove(node(element));
}
public boolean contains(E element) {
return node(element) != null;
}
private int compare(E e1, E e2) {
if (comparator != null) {
comparator.compare(e1, e2);
}
return ((Comparable)e1).compareTo(e2);
}
private void remove(Node node) {
if (node == null) return;
size--;
if (node.hasTwoChildren()) {// 度为2 的节点
// 找到后继节点
Node s = successor(node);
// 用后继节点的值覆盖度为2 的节点
node.element = s.element;
// 删除后继节点
node = s;
}
// 删除node 的节点, 度为1 或者0
Node replacement = node.left != null ? node.left : node.right;
if (replacement != null) { // 度为1 的节点
// 更改parent
replacement.parent = node.parent;
if (node.parent == null) {// node 是度为1 的节点且是根节点
root = replacement;
} else if (node == node.parent.left) {
node.parent.left = replacement;
} else {
node.parent.right = replacement;
}
} else if (node.parent == null) {// 叶子节点, 且为根节点
root = null;
} else {// node 是叶子节点, 但不是根节点
if (node == node.parent.left) {
node.parent.left = null;
} else { // node == node.parent.right
node.parent.right = null;
}
}
}
private Node node(E element) {
Node node = root;
while (node != null) {
int cmp = compare(element, node.element);
if (cmp == 0) return node;
if (cmp > 0) {
node = node.right;
} else {
node = node.left;
}
}
return null;
}
private void elementNotNullCheck(E element) {
if (element == null) {
throw new IllegalArgumentException("element must not be null");
}
}
}