什么是二叉查找树
二叉查找树是一种树结构。首先是二叉树。它表示每个节点最多有两个子节点,而二叉查找树,它还要求左子节点必须比右子节点小。
对于这个结构,我们以下几个操作的实现:
树中的元素必须能比较
这里的比较,我们不用equals和==,我们要判断它与节点中元素的大小关系,所以这里我们有2种实现方案:
1. 元素实现Comparable接口,重写它的compareTo方法
2. 在二叉树的构造方法中传入特定的Comparator比较器进行比较
/**
* 比较器
*/
private Comparator<T> comparator;
public BinarySearchTree() {
root = null;
comparator = null;
}
/**
* 带有指定比较器的构造方法
* @param comparator
*/
public BinarySearchTree(Comparator<T> comparator) {
root = null;
this.comparator = comparator;
}
/**
* 自己实现一个compare方法
* 如果在构造时传入了相应的比较器,则用传入比较器的compare方法
* 若没有传,则强转成comparable进行比较
*
* @param t1
* @param t2
* @return
*/
private int compare(T t1, T t2) {
/**
* 如果比较器不为null
*/
if (comparator != null) {
return comparator.compare(t1, t2);
}
return ((Comparable) t1).compareTo(t2);
}
insert操作
当执行插入操作时,思路就是比较插入元素和节点元素的大小,若插入元素大,则往节点右边走,相反则往节点左边走,最后当节点的左边或右边为null的时候,就把元素插入进去了.若碰到插入元素重复的情况则不管
public void insert(T t) {
root = insert(t, root);
}
private BinaryNode<T> insert(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
BinaryNode node = new BinaryNode(t, null, null);
return node;
}
Object element = binaryNode.element;
int compare = compare(t, (T) element);
if (compare == -1) {
// 说明t 比 element 小
binaryNode.left = insert(t, binaryNode.left);
} else if (compare == 1) {
// 说明t 比 element 大
binaryNode.right = insert(t, binaryNode.right);
} else ;
/**
* 若t和element一样大则不做任何事情
*/
/**
* 把根返回
*/
return binaryNode;
}
contains操作
和insert操作基本一个思路,就是比较元素和节点元素大小来判断往左走还是往右走,若碰到元素和节点元素相等的情况则返回true,否则返回false
public boolean contains(T t) {
return contains(t, root);
}
private boolean contains(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
return false;
}
Object element = binaryNode.element;
int compare = compare(t, (T) element);
if (compare == 1) {
return contains(t, binaryNode.right);
} else if (compare == -1) {
return contains(t, binaryNode.left);
} else {
return true;
}
}
findMin和findMax操作
很简单,findMin就是一直往左走,知道节点的左节点为null时,则该节点的元素最小,findMax则相反,这里我们findMax用递归实现,findMin用循环实现
public T findMin() {
BinaryNode min = findMin(root);
if (min != null) {
return (T) min.element;
}
return null;
}
public T findMax() {
BinaryNode max = findMax(root);
if (max != null) {
return (T) max.element;
}
return null;
}
private BinaryNode findMin(BinaryNode binaryNode) {
if (binaryNode != null) {
while (binaryNode.left != null) {
binaryNode = binaryNode.left;
}
}
return binaryNode;
}
private BinaryNode findMax(BinaryNode binaryNode) {
if (binaryNode == null) {
return null;
}
if(binaryNode.right != null) {
return findMax(binaryNode.right);
}
return binaryNode;
}
remove操作
这个比较麻烦,因为有几种情况:
- 删除的节点没有子节点。。。。这个比较简单,直接删除即可
- 删除的节点有一个子节点。。。。我们通过把要删除节点的父节点的链绕过该节点,在删除
- 删除的节点有两个子节点。。。这种情况就比较麻烦了,我们先拿到这个节点的右子节点,然后一直往左拿到最小的那个节点,用这个最小节点的数据代替我们要删除的节点,然后把这个最小的节点删除即可
public boolean remove(T t) {
BinaryNode remove = remove(t, root);
if (remove != null) {
return true;
}
return false;
}
private BinaryNode remove(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
return binaryNode;
}
int compare = compare(t, (T) binaryNode.element);
if (compare == -1) {
binaryNode.left = remove(t, binaryNode.left);
} else if (compare == 1) {
binaryNode.right = remove(t, binaryNode.right);
} else if (binaryNode.left != null && binaryNode.right != null) {
/**
*这儿注释的三局代码,findMin会搜索一遍,remove又会搜索一遍,所以我们写一个特殊的removeMin方法来避免这个操作
*/
// BinaryNode min = findMin(binaryNode.right);
// binaryNode.element = min.element;
// binaryNode.right = remove((T) min.element, binaryNode.right);
removeMin(binaryNode);
} else {
binaryNode = binaryNode.left == null ? binaryNode.right : binaryNode.left;
}
return binaryNode;
}
private void removeMin(BinaryNode binaryNode) {
if (binaryNode == null) {
return ;
}
BinaryNode node = binaryNode.right;
BinaryNode parentNode = null;
while (true) {
if(node.left != null) {
parentNode = node;
node = node.left;
} else {
Object element = node.element;
binaryNode.element = element;
parentNode.left = node.right;
break;
}
}
}
代码
package collection;
import java.util.Comparator;
/**
* Created by sunhan on 2018/1/14.
*/
public class BinarySearchTree<T> {
private static class BinaryNode<T> {
private BinaryNode<T> left;
private BinaryNode<T> right;
private T element;
public BinaryNode(T t) {
this(t, null, null);
}
public BinaryNode(T t, BinaryNode left, BinaryNode right) {
this.element = t;
this.left = left;
this.right = right;
}
}
/**
* 先序遍历
*/
public void suffix(){
midIterator(root);
}
private void suffixIterator(BinaryNode node){
System.out.println(node.element.toString());
if(node.left != null){
suffixIterator(node.left);
}
if(node.right != null){
suffixIterator(node.right);
}
}
/**
* 中序遍历
*/
public void mid(){
midIterator(root);
}
private void midIterator(BinaryNode node){
if(node.left != null){
midIterator(node.left);
}
System.out.println(node.element.toString());
if(node.right != null){
midIterator(node.right);
}
}
/**
* 后序遍历
*/
public void back(){
backIterator(root);
}
private void backIterator(BinaryNode node){
if(node.left != null){
backIterator(node.left);
}
if(node.right != null){
backIterator(node.right);
}
System.out.println(node.element.toString());
}
/**
* 根
*/
private BinaryNode<T> root;
/**
* 比较器
*/
private Comparator<T> comparator;
public BinarySearchTree() {
root = null;
comparator = null;
}
/**
* 带有指定比较器的构造方法
* @param comparator
*/
public BinarySearchTree(Comparator<T> comparator) {
root = null;
this.comparator = comparator;
}
/**
* 自己实现一个compare方法
* 如果在构造时传入了相应的比较器,则用传入比较器的compare方法
* 若没有传,则强转成comparable进行比较
*
* @param t1
* @param t2
* @return
*/
private int compare(T t1, T t2) {
/**
* 如果比较器不为null
*/
if (comparator != null) {
return comparator.compare(t1, t2);
}
return ((Comparable) t1).compareTo(t2);
}
public void insert(T t) {
root = insert(t, root);
}
private BinaryNode<T> insert(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
BinaryNode node = new BinaryNode(t, null, null);
return node;
}
Object element = binaryNode.element;
int compare = compare(t, (T) element);
if (compare == -1) {
// 说明t 比 element 小
binaryNode.left = insert(t, binaryNode.left);
} else if (compare == 1) {
// 说明t 比 element 大
binaryNode.right = insert(t, binaryNode.right);
} else ;
/**
* 若t和element一样大则不做任何事情
*/
/**
* 把根返回
*/
return binaryNode;
}
public T findMin() {
BinaryNode min = findMin(root);
if (min != null) {
return (T) min.element;
}
return null;
}
public T findMax() {
BinaryNode max = findMax(root);
if (max != null) {
return (T) max.element;
}
return null;
}
private BinaryNode findMin(BinaryNode binaryNode) {
if (binaryNode != null) {
while (binaryNode.left != null) {
binaryNode = binaryNode.left;
}
}
return binaryNode;
}
private BinaryNode findMax(BinaryNode binaryNode) {
if (binaryNode == null) {
return null;
}
if(binaryNode.right != null) {
return findMax(binaryNode.right);
}
return binaryNode;
}
public boolean contains(T t) {
return contains(t, root);
}
private boolean contains(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
return false;
}
Object element = binaryNode.element;
int compare = compare(t, (T) element);
if (compare == 1) {
return contains(t, binaryNode.right);
} else if (compare == -1) {
return contains(t, binaryNode.left);
} else {
return true;
}
}
public boolean remove(T t) {
BinaryNode remove = remove(t, root);
if (remove != null) {
return true;
}
return false;
}
private BinaryNode remove(T t, BinaryNode binaryNode) {
if (binaryNode == null) {
return binaryNode;
}
int compare = compare(t, (T) binaryNode.element);
if (compare == -1) {
binaryNode.left = remove(t, binaryNode.left);
} else if (compare == 1) {
binaryNode.right = remove(t, binaryNode.right);
} else if (binaryNode.left != null && binaryNode.right != null) {
// BinaryNode min = findMin(binaryNode.right);
// binaryNode.element = min.element;
// binaryNode.right = remove((T) min.element, binaryNode.right);
removeMin(binaryNode);
} else {
binaryNode = binaryNode.left == null ? binaryNode.right : binaryNode.left;
}
return binaryNode;
}
private void removeMin(BinaryNode binaryNode) {
if (binaryNode == null) {
return ;
}
BinaryNode node = binaryNode.right;
BinaryNode parentNode = null;
while (true) {
if(node.left != null) {
parentNode = node;
node = node.left;
} else {
Object element = node.element;
binaryNode.element = element;
parentNode.left = node.right;
break;
}
}
}
}