什么是二叉排序树?
二叉排序树又叫二叉查找树、二叉搜索树、有序二叉树,再进行数据查找操作时,可以达到 O ( l o g n ) − O ( n ) O(logn) - O(n) O(logn)−O(n) 的时间复杂度。
给定一个二叉树,如果满足以下条件,那就是二叉排序树:
- 若它的左子树不空,则左子树上所有结点的值均小于它根结点的值。
- 若它的右子树不空,则右子树上所有结点的值均大于它根结点的值。
- 它的左、右子树都满足为⼆叉排序树。
二叉排序树的构建
-
二叉排序树从根节点开始构建,如果当前值比根节点小,则进入左子树,否则进入右子树。然后再递归的和左右孩子进行比较,直至将值添加到叶子节点。
-
代码实现:首先定义一个二叉树的结构类,然后通过创建二叉树的管理类对二叉树进行操作,构造出二叉排序树。
public class TreeNode {
private int value;
private TreeNode left;
private TreeNode right;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public TreeNode getLeft() {
return left;
}
public void setLeft(TreeNode left) {
this.left = left;
}
public TreeNode getRight() {
return right;
}
public void setRight(TreeNode right) {
this.right = right;
}
public TreeNode(int value) {
this.value = value;
}
@Override
public String toString() {
return "TreeNode{" +
"value=" + value +
", left=" + left +
", right=" + right +
'}';
}
}
public class BinaryTree {
public TreeNode root;
}
排序二叉树的增删查改
插入操作
- 非递归方式实现:
public void insert(int value) {
TreeNode node = new TreeNode(value);
if(root == null) {//判断根节点是否为空
root = node;
} else { //定义一个游标遍历二叉树,在定义一个pre记录游标的前一个值
TreeNode index = root;
TreeNode pre = null;
while (true) {
pre = index;
if (node.getValue() > index.getValue()) { //如果待插入值大于当前值,游标右移
index = index.getRight();
if (index == null) { //如果游标指空,说明到了叶子节点
pre.setRight(node);
break;
}
} else { //否则游标左移
index = index.getLeft();
if (index == null) { //如果游标指空,说明到了叶子节点
pre.setLeft(node);
break;
}
}
}
}
}
- 递归方式实现:
public void insert(TreeNode node, int value) {
TreeNode newNode = new TreeNode(value);
if(root == null) {//判断根节点是否为空
root = newNode;
} else {
if (node.getValue() > value) { //当前节点大于待插入值,向左递归
if (node.getLeft() == null) {
node.setLeft(newNode);
return;
}
insert(node.getLeft(), value);
} else { //向右递归
if (node.getRight() == null) {
node.setRight(newNode);
return;
}
insert(node.getRight(), value);
}
}
}
查找操作
- 非递归方式实现:
public TreeNode search(int value) {
TreeNode index = root;
while (index != null) {
if (index.getValue() == value) { //找到了
return index;
} else if (index.getValue() < value) {
index = index.getRight();
} else {
index = index.getLeft();
}
}
return null;
}
- 递归方式实现:
public TreeNode search(TreeNode node, int value) {
if (node == null) {
return null;
} else {
if (value == node.getValue()) {
return node;
} else if (value < node.getValue()) {
return search(node.getLeft(), value);
} else {
return search(node.getRight(), value);
}
}
}
删除操作
//找到要删除节点的父节点
public TreeNode searchParent(TreeNode node, int value) {
if (node == null) {
return null;
} else { //判断当前节点是否是目标节点的父节点
if(node.getLeft() != null && node.getLeft().getValue() == value || node.getRight() != null && node.getRight().getValue() == value) {
return node;
} else if (value < node.getValue()) { //如果目标值小于当前节点值(并且node不是该值的父节点),说明目标节点的父节点在当前节点的左侧
return searchParent(node.getLeft(), value);
} else {
return searchParent(node.getRight(), value);
}
}
}
public void delete(int value) {
if (root == null) {
System.out.println("空树!");
} else {
//1.找到目标节点
TreeNode target = search(value);
if (target == null) {
System.out.println("没有目标节点!");
} else {
//2.找到目标节点的父节点
TreeNode parent = searchParent(root, value);
if (target.getLeft() == null && target.getRight() == null) { //2.1删除叶子节点
if (parent == null) { //没有父节点
root = null;
} else if (parent.getLeft() != null && parent.getLeft().getValue() == value) { //是左孩子
parent.setLeft(null);
} else {
parent.setRight(null);
}
} else if (target.getLeft() != null && target.getRight() != null) { //2.2删除有两颗子树的节点
//找到目标节点右子树的最小值
TreeNode index = target.getRight();
while (index.getLeft() != null) {
index = index.getLeft();
}
//index指向target的右子树的最小值
int min = index.getValue();
//删除target右子树的最小值,并覆盖给目标值
delete(min);
target.setValue(min);
} else { //2.3删除只有一棵子树的节点
if (parent == null) {
if (target.getLeft() != null) { //判断target有左子树还是右子树
root = target.getLeft();
} else {
root = target.getRight();
}
} else { //有父节点
if (parent.getLeft() != null && parent.getLeft().getValue() == value) { //确定target是parent的左孩子还是右孩子
if (target.getLeft() != null) { //判断target自己有左子树还是右子树
parent.setLeft(target.getLeft());
} else {
parent.setLeft(target.getRight());
}
} else {
if (target.getLeft() != null) { //判断target自己有左子树还是右子树
parent.setRight(target.getLeft());
} else {
parent.setRight(target.getRight());
}
}
}
}
}
}
}
二叉排序树的遍历
深度优先遍历
深度优先遍历(dfs) | 遍历方式 | 输出结果 |
---|---|---|
先序遍历 | 先输出父节点,再输出左子树,再输出右子树 | A − > B − > D − > H − > E − > C − > F − > G A->B->D->H->E->C->F->G A−>B−>D−>H−>E−>C−>F−>G |
中序遍历 | 先输出左子树,再输出父节点,再输出右子树 | H − > D − > B − > E − > A − > F − > C − > G H->D->B->E->A->F->C->G H−>D−>B−>E−>A−>F−>C−>G |
后序遍历 | 先输出左子树,再输出右子树,再输出父节点 | H − > D − > E − > B − > F − > G − > C − > A H->D->E->B->F->G->C->A H−>D−>E−>B−>F−>G−>C−>A |
- 由定义可知:二叉排序树的中序遍历构成一个有序序列。
public void beforeOrder(TreeNode node) {
if (node == null) {
return;
} else {
System.out.print(node.getValue() + " ");
beforeOrder(node.getLeft());
beforeOrder(node.getRight());
}
}
public void middleOrder(TreeNode node) {
if (node == null) {
return;
} else {
middleOrder(node.getLeft());
System.out.print(node.getValue() + " ");
middleOrder(node.getRight());
}
}
public void afterOrder(TreeNode node) {
if (node == null) {
return;
} else {
afterOrder(node.getLeft());
afterOrder(node.getRight());
System.out.print(node.getValue() + " ");
}
}
广度优先遍历
广度优先遍历(bfs) | 输出结果 |
---|---|
从上到下打印每个二叉树的节点,同一层的节点按照从左到右的顺序进行打印 | A − > B − > C − > D − > E − > F − > G − > H A->B->C->D->E->F->G->H A−>B−>C−>D−>E−>F−>G−>H |
public void levelOrder(TreeNode node) {
Queue<TreeNode> queue = new LinkedList<TreeNode>(); //调用Java提供的队列类生成一个队列
TreeNode index = null; //记录从队列中取出的节点
if (node != null) {
queue.add(node);
while (!queue.isEmpty()) {
index = queue.poll();
System.out.print(index.getValue() + " ");
if (index.getLeft() != null) {
queue.add(index.getLeft());
}
if (index.getRight() != null) {
queue.add(index.getRight());
}
}
} else {
System.out.println("树为空!");
}
}
测试样例及代码
public class Test {
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
tree.insert(16);
tree.insert(13);
tree.insert(8);
tree.insert(20);
tree.insert(tree.root, 19);
tree.insert(tree.root,1);
tree.insert(tree.root,15);
tree.insert(tree.root,24);
tree.beforeOrder(tree.root);
System.out.println();
tree.middleOrder(tree.root);
System.out.println();
tree.afterOrder(tree.root);
System.out.println();
tree.levelOrder(tree.root);
System.out.println();
System.out.println("\n\n");
TreeNode t1 = tree.search(20);
System.out.println(t1);
TreeNode t2 = tree.search(tree.root,13);
System.out.println(t2);
System.out.println("\n\n");
tree.delete(13);
System.out.println(tree);
}
}
- 测试结果如下:
几种常见二叉树的区别
名称 | 特性 |
---|---|
完全二叉树 | 数据从上到下,从左到右依次排列 |
满二叉树 | 所有的叶子节点都在同一层,且最后一层的节点数为 2 n − 1 2^{n-1} 2n−1, n n n 表示层数 |
有序二叉树 | 左边节点的值小于当前节点,右边节点的值大于当前节点 |
平衡二叉树 | 平衡二叉树、B树、B+树、红黑树解析 |
红黑树 | 平衡二叉树、B树、B+树、红黑树解析 |