二叉搜索树
又名二叉查找树,因为二叉查找树不是完全二叉树,因此通常使用链表方式存储。一个分布平均的二叉查找树的查找,插入,删除操作时间复杂度均为O(logn)
;在极度不平衡的情况下,二叉查找树会退化为链表。
对二叉查找树进行中序遍历可以得到有序数组
结构要求
对于二叉查找树中的任意一个节点,其左子树中的每个节点的值,都要小于这个节点的值,右子树节点的值都大于这个节点的值。
插入操作
如果要插入的数据比节点的数据大,并且节点的右子树为空,就将新数据直接插到右子节点的位置;如果不为空,就再递归遍历右子树,查找插入位置。同理,如果要插入的数据比节点数值小,并且节点的左子树为空,就将新数据插入到左子节点的位置;如果不为空,就再递归遍历左子树,查找插入位置。
删除操作
- 要删除的节点没有子节点:直接将父节点中,指向要删除节点的指针置为 null
- 要删除的节点只有一个子节点(只有左子节点或者右子节点):只需要更新父节点中,指向要删除节点的指针,让它指向要删除节点的子节点
- 要删除的节点有两个子节点:找到这个节点的右子树中的最小节点,把它替换到要删除的节点上。然后再删除掉这个最小节点,因为最小节点肯定没有左子节点
如何处理重复数据
- 通过链表和支持动态扩容的数组等数据结构,把值相同的数据都存储在同一个节点上
- 每个节点仍然只存储一个数据。在查找插入位置的过程中,如果碰到一个节点的值,与要插入数据的值相同,将待插入数据放到这个节点的右子树,也就是说,把这个新插入的数据当作大于这个节点的值来处理。当要查找数据的时候,遇到值相同的节点,并不停止查找操作,继续在右子树中查找,直到遇到叶子节点,才停止。
// 二叉搜索树类
public class BinarySearchTree {
// 节点类
private class Node {
// 数据域
int data;
// 右子树
Node right;
// 左子树
Node left;
}
// 根节点
private Node root;
// 插入操作
private void insert(int key) {
Node p = new Node();
p.data = key;
if (null == root) {
root = p;
} else {
Node parent = new Node();
Node current = root;
while (true) {
parent = current;
if (key > current.data) {
current = current.right;
if (null == current) {
parent.right = p;
return;
}
} else {
current = current.left;
if (null == current) {
parent.left = p;
return;
}
}
}
}
}
// 查找节点
public Node find(int key) {
Node current = root;
while (current.data != key) {
if (key > current.data) {
current = current.right;
} else {
current = current.left;
}
if (null == current) {
return null;
}
}
return current;
}
//寻找要删除节点的中序后继结点
private Node getSuccessor(Node delNode) {
Node successorParent = delNode;
Node successor = delNode;
Node current = delNode.right;
//用来寻找后继结点
while (current != null) {
successorParent = successor;
successor = current;
current = current.left;
}
//如果后继结点为要删除结点的右子树的左子,需要预先调整一下要删除结点的右子树
if (successor != delNode.right) {
successorParent.left=successor.right;
successor.right=delNode.right;
}
return successor;
}
// 删除结点
public boolean delete(int key) {
Node current = root;
Node parent = new Node();
boolean isRightChild = true;
while (current.data != key) {
parent = current;
if (key > current.data) {
current = current.right;
isRightChild = true;
}
else {
current = current.left;
isRightChild = false;
}
if (current == null) {return false;} // 没有找到要删除的结点
}
// 此时current就是要删除的结点,parent为其父结点
// 要删除结点为叶子结点
if (current.right == null && current.left == null) {
if (current == root) {
root = null; // 整棵树清空
}
else {
if (isRightChild)
parent.right = null;
else
parent.left = null;
}
return true;
}
//要删除结点有一个子结点
else if(current.left==null) {
if(current==root)
root=current.right;
else if(isRightChild)
parent.right=current.right;
else
parent.left=current.right;
return true;
}
else if(current.right==null) {
if(current==root)
root=current.left;
else if(isRightChild)
parent.right=current.left;
else
parent.left=current.left;
return true;
}
//要删除结点有两个子结点
else {
Node successor=getSuccessor(current); //找到要删除结点的后继结点
if(current==root)
root=successor;
else if(isRightChild)
parent.right=successor;
else
parent.left=successor;
successor.left=current.left;
return true;
}
}
}
Project Links: