一.概念
二叉搜索树又称二叉排序树(纯 key 模型,且 key值不能重复,二叉搜索树的中序遍历是有序的),它或者是一棵空树**,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树
二.搜索树操作
1.查找:在搜索树中查找 key 值,若找到返回该节点,未找到返回空节点。时间复杂度 O(log(N)) - O(N)
思路:
public Node search(int key){
Node cur = root;
while(cur != null){
if(key == cur.key){
return cur;
}else if(key > cur.key){
cur = cur.right;
}else{
cur = cur.left;
}
}
return null;
}
2.插入:如果树为空,直接插入,否则按照查找逻辑确定插入位置进行插入
思路:
public boolean insert(int key){
// 如果该树是一个空节点,直接插入,即让该节点为 根结点 root
if(root == null){
root = new Node(key);
return true;
}
// 寻找 key 值的合理位置,若该值已有则插入失败
Node cur = root;
Node parent = null;
while(cur != null){
if(key == cur.key){
return false;
}else if(key < cur.key){
parent = cur;
cur = cur.left;
}else{
parent = cur;
cur = cur.right;
}
}
// 插入
Node node = new Node(key);
if(key > parent.key){
parent.right = node;
}else{
parent.left = node;
}
return true;
}
3.删除
设待删除结点为 cur, 待删除结点的双亲结点为 parent
1. cur.left == null
- cur 是 root,则 root = cur.right
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
2. cur.right == null
- cur 是 root,则 root = cur.left
- cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
- cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
3. cur.left != null && cur.right != null
需要使用替换法进行删除,即在它的右子树中寻找关键码最小的(或者在左子树寻找关键码最大的),用它的值填补到被删除节点中,再来处理该结点的删除问题
public boolean remove(int key){
Node cur = root;
Node parent = null;
// 先找到待删除节点以及他的父亲节点
while(cur != null){
if(cur.key == key){// 找到,准备删除
removeNode(parent,cur);
return true;
}else if(key > cur.key){
parent = cur;
cur = cur.right;
}else{
parent = cur;
cur = cur.left;
}
}
return false;
}
private void removeNode(Node parent, Node cur) {
// 进行删除
if(cur.left == null){
if(cur == root){
root = cur.right;
}else if(cur == parent.left){
parent.left = cur.right;
}else{
parent.right = cur.right;
}
}else if(cur.right == null){
if(cur == root){
root = cur.left;
}else if(cur == parent.left){
parent.left = cur.left;
}else{
parent.right = cur.left;
}
}else{// 替换法:在右子树寻找最小的(或者左子树中最大的)
Node goatParent = cur;
Node goat = cur.right;
while(goat.left != null){
goatParent = goat;
goat = goat.left;
}
cur.key = goat.key;
// 判断 goat 是 goatParent 的左孩子还是右孩子
if(goat == goatParent.left){
goatParent.left = goat.right;
}else{
goatParent.right = goat.right;
}
}
}
三.电话本 key - Value 模型
基本操作:查找,插入,更新。与搜索树思路相同,只是多考虑一个 Value。(key 允许重复,Value 不允许重复)
public class Contact {
public static class Node{
String name;
String phone;
Node left;
Node right;
public Node(String name, String phone) {
this.name = name;
this.phone = phone;
}
}
private Node root = null;
// 查找,没有找到返回 null,否则返回这个人的电话
public String search(String name){
Node cur = root;
while(cur != null){
if(name == cur.name){
return cur.phone;
}else if(name.compareTo(cur.name) > 0){
cur = cur.right;
}else{
cur = cur.left;
}
}
return null;
}
// 插入,若不存在则插入,若存在则插入失败
public boolean insert(String name, String phone){
Node cur = root;
Node parent = null;
while(cur != null){
if(name == cur.name){
return false;
}else if(name.compareTo(cur.name) > 0){// String类型不能直接比较
parent = cur;
cur = cur.right;
}else{
parent = cur;
cur = cur.left;
}
}
Node node = new Node(name,phone);
if(parent.name.compareTo(name) > 0){
parent.left = node;
}else{
parent.right = node;
}
return true;
}
// 更新电话本,若存在该用户更新他的电话,不存在返回 false
public boolean update1(String name,String phone){
Node cur = root;
while(cur != null){
if(name == cur.name){
cur.phone = phone;
return true;
}else if(name.compareTo(cur.name) > 0){
cur = cur.right;
}else{
cur = cur.left;
}
}
return false;
}
// 更新电话本,若存在该用户更新他的旧电话,否则返回 null
public String update2(String name,String phone){
Node cur = root;
while(cur != null){
if(name == cur.name){
String oldPhone = cur.phone;
cur.phone = phone;
return oldPhone;
}else if(name.compareTo(cur.name) > 0){
cur = cur.right;
}else{
cur = cur.left;
}
}
return null;
}
}