数据结构之二叉搜索树(java语言版)
之前介绍了树,主要实现了二叉树的代码。在二叉树的基础上有许多衍生的树,如二叉搜索树、哈夫曼树等,今天学习一下二叉搜索树。
二叉搜索树
二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST
又被称为:二叉查找树、二叉排序树
特点
任意一个节点的值都大于其左子树所有节点的值
任意一个节点的值都小于其右子树所有节点的值
它的左右子树也是一棵二叉搜索树
◼ 二叉搜索树可以大大提高搜索数据的效率
◼ 二叉搜索树存储的元素必须具备可比较性
java语言实现
有了之前的二叉树的实现,此时实现二叉搜索树就比较简单。
节点类
1、节点类中的data要求必须具有可比较性,我在此使用int型,如果使用的数据是其他对象,需要经过处理使其具有可比较性。
2、二叉搜索树需要有三条引用链,左节点、右节点、父节点。父节点在删除数据时需要使用。
public class Node {
int data;
Node left;
Node right;
Node parent;
public Node(int data, Node left, Node right, Node parent) {
this.data = data;
this.left = left;
this.right = right;
this.parent = parent;
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
方法
二叉搜索树的方法:
作为接口BinaryTree
public interface BinaryTree {
void add(int e);
void remove(int e);
void replace(int e1, int e2);
Node search(int e);
boolean isEmpty();
int compare(int e1, int e2);
void display(Node root);
}
二叉搜索树实现
构造方法
public class BST implements BinaryTree {
int size;
Node root;
public BST() {
System.out.println("BST init....");
}
@Override
public boolean isEmpty() {
return size == 0;
}
}
compare()方法
通过compare方法比较大小,决定节点的位置。
/**
*
* 将两个节点传入,进行比较,
* 如果e1>e2,return 1
* e1<e2,return -1
* 相等 return 0
*/
@Override
public int compare(int e1, int e2) {
if (e1 == e2) {
return 0;
}
return e1 > e2 ? 1 : -1;
}
add方法
add方法用于加入数据.
调用了compare()方法用于比较大小。
@Override
public void add(int e) {
if (root == null) {
root = new Node(e, null, null, null);
System.out.println("add root succcessful!!!");
return;
}
Node head = root;
Node parent = root;
int cap = 0;
while (head != null) {
parent = head;
cap = compare(head.data, e);
if (cap > 0) {
head = head.left;
} else if (cap < 0) {
head = head.right;
} else {
System.out.println("=====已有相关节点");
return;
}
}
Node newNode = new Node(e, null, null, parent);
if (cap > 0) {
parent.left = newNode;
} else {
parent.right = newNode;
}
System.out.println("add successfu!!!");
size++;
}
先序遍历
@Override
public void display(Node root) {
if (root == null) {
return;
}
System.out.print(" " + root);
display(root.left);
display(root.right);
}
测试
用一个数组作为数据测试一下:
public static void main(String[] args) {
int[] arr = new int[] { 8, 6, 10, 4, 7, 9, 13 };
BST bst = new BST();
for (int i : arr) {
bst.add(i);
}
bst.display(bst.root);
}
按测试数据的输入,得到的树应该是右边的树。
得到如下结果:
BST init....
add root succcessful!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
add successfu!!!
Node [data=8] Node [data=4] Node [data=6] Node [data=7] Node [data=9] Node [data=10] Node [data=13]
如果需要得到左边的树、那么需要以下顺序输入:
8,6,10,4,7,9,13
此时的树与节点输入顺序有关系,右边的树虽然是二叉搜索树,但已经不平衡了。不平衡的树有许多缺点,所以有了平衡二叉树。
search方法
通过search方法得到元素所在的节点。
@Override
public Node search(int e) {
if (isEmpty()) {
System.out.println("null tree");
return null;
}
Node head = root;
Node parent = root;
int cap = 0;
while (head != null) {
parent = head;
cap = compare(head.data, e);
if (cap > 0) {
head = head.left;
} else if (cap < 0) {
head = head.right;
} else {
return head;
}
}
return null;
}
replace方法
replace这个方法在使用是注意约束条件,要控制替换的数据满足二叉搜索树的定义。
@Override
public void replace(int e1, int e2) {
Node node = search(e1);
if (node == null) {
System.out.println("not node could be replace");
return;
}
// 代替的位置主要要满足约束条件
int cap;
if (node.parent != null) {
cap = compare(node.parent.data, e2);
if (cap >= 0) {
System.out.println("not node could be replace");
return;
}
}
if (node.left != null) {
cap = compare(node.left.data, e2);
if (cap >= 0) {
System.out.println("not node could be replace");
return;
}
}
if (node.right != null) {
cap = compare(node.right.data, e2);
if (cap <= 0) {
System.out.println("not node could be replace");
return;
}
}
node.data = e2;
}
remove方法
删除节点需要用到parent节点,节点被删除后,其子节点和父节点的相关引用都会改变。
并且根据删除的节点的位置,需要有一些细微的操作。
删除叶子节点
@Override
public void remove(int e) {
Node node = search(e);
if (node == null) {
System.out.println("not node could be remove");
return;
}
size--;
Node parent = node.parent;// 得到父节点
if (node.left == null && node.right == null) {// 叶子节点
if (parent.right == node) {
parent.right = null;
} else {
parent.left = null;
}
System.out.println("叶子节点删除。。。");
return;
}
if (node.left != null && node.right != null) {// 度为2的节点
if (parent == null) {// 为root节点时,默认选择前驱节点为new root
Node newRoot = maxChildNode(node);
System.out.println(" new root: " + newRoot);
if (newRoot != null) {
node.data = newRoot.data;
} else {
newRoot = minChildNode(node);
node.data = newRoot.data;
}
// 消灭newRoot
if (newRoot.parent.right == newRoot) {
newRoot.parent.right = null;
} else {
newRoot.parent.left = null;
}
}
if (parent != null) {
//.........
}
System.out.println("度为2的节点删除");
return;
} else {// 度为1
if (parent == null) {// root节点
if (node.right != null) {
node = node.right;
node.right.parent = null;
} else {
node = node.left;
node.left.parent = null;
}
}
//.................
System.out.println("度为1的节点删除");
}
}