二叉搜索树的优势
如果要在n个整数中搜索某个整数,查看其是否存在。
(1)假如使用动态存放元素,从第0个位置开始遍历搜索,平均时间复杂度为O(n)。
(2)假如维护一个有序的动态数组,使用二分查找,最坏时间复杂度是O(lngn)。但是添加和删除的时间复杂度仍然是O(n)。
(3)使用二叉搜索树,添加、删除、搜索的最坏时间复杂度均可优化至O(logn)。
二叉搜索树的概念
(一)二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为 BST。
- 二叉搜索树又被称为二叉查找树、二叉排序树。
- 任意一个节点的值都大于左子树的所有节点值。
- 任意一个节点的值都小于右子树的所有节点值。
(二)二叉搜索树可以大大提高搜索数据的效率。
(三)二叉搜索树存储的元素必须具备可比较性。
- 比如int、short、double等。
- 如果是自定义类型,则需要指定比较方法。
- 不允许为null。
接口设计
方法声明 | 功能介绍 |
---|---|
int size() | 元素的数量 |
boolean isEmpty() | 是否为空 |
boolean contains() | 是否包含 |
void clear() | 清空 |
void add(Node< E > node) | 添加节点 |
void remove(Node< E > node | 删除节点 |
public class BinarySearchTree<E>{
//节点类
private class Node<E>{
E element;
Node<E> left;
Node<E> right;
Node<E> parent;
public Node(E element,Node<E> parent){
this.element=element;
this.parent=parent;
}
}
//根节点
private Node<E> root;
int size;
public BinarySearchTree() {
}
public int size() {
return size;
}
public boolean isEmpty() {
return size==0;
}
public boolean contains(E element) {
return false;
}
public void clear() {
}
public void add(E element) {
}
public void remove(E element) {
}
}
add接口的实现
在进行添加操作时,必须对将要添加的节点元素与树上节点的元素进行比较,才能找到正确的放置位置,而不同的元素(可能是自定义类对象)需要有不同的比较方法,相同的元素在不同的场合也需要不同的比较方法。那么我们可以通过创建一个Comparator类型的引用,并在二叉搜索的构造方法中通过匿名内部类传入,使用它的compare方法就可以实现不同的比较方法,此外,若是不想传入comparator,则可在比较时将比较元素强转为Comparable接口类型(需要让元素实现Comparable接口,否则运行报错,且Integer等包装类已实现该接口),调用compareTo方法进行比较。
private Comparator<E> comparator;
public BinarySearchTree() {
}
//传入Comparator对象,用于比较
public BinarySearchTree(Comparator<E> comparator) {
this.comparator = comparator;
}
/**
* 用于进行元素的比较
* @param e1
* @param e2
* @return
*/
private int compareTo(E e1,E e2){
//如果比较器不为空,则使用比较器
if(null!=comparator){
return comparator.compare(e1,e2);
}else {//否则要求实现Comparable接口的compareTo方法
return ((Comparable)e1).compareTo(e2);
}
}
可以进行比较后则可完成add方法了,简单地说就是比较元素大小,寻找添加位置。
public void add(E element) {
//如果是第一个节点
if(size==0) {
root = new Node(element, null);
size++;
return;
}
//否则对元素进行比较找到放置的位置
Node<E> node=root,par=null;//记录父节点
int cmp=0;//保存比较大小
while(node!=null){
par=node;
cmp=compareTo(element,node.element);
//若元素小于当前节点
if(cmp<0){
node=node.left;
}else if(cmp>0){
node=node.right;
}else {//相同覆盖
node.element=element;
return ;
}
}
//此时找到插入位置
if(cmp<0){
par.left=new Node(element,par);
}else{
par.right=new Node(element,par);
}
size++;
}
进行简单的打印
在此通过先序遍历访问所有节点,对字符串进行拼接,然后打印。
@Override
public String toString() {//实现二叉树的简单打印
StringBuilder sb=new StringBuilder();
toString(root,sb,"");
return sb.toString();
}
/**
* 通过先序遍历的顺序拼接字符
* @param node
* @param sb
* @param preStr 需要在节点前拼接的字符
*/
private void toString(Node<E> node,StringBuilder sb,String preStr){
if(null==node)return;
//处理元素,拼接字符
sb.append(preStr).append(node.element).append('\n');
toString(node.left,sb,preStr+"left---");
toString(node.right,sb,preStr+"right--");
}
整体代码
import java.util.Comparator;
public class BinarySearchTree<E>{
//节点类
private class Node<E>{
E element;
Node<E> left;
Node<E> right;
Node<E> parent;
public Node(E element,Node<E> parent){
this.element=element;
this.parent=parent;
}
}
//根节点
private Node<E> root;
int size;
private Comparator<E> comparator;
public BinarySearchTree() {
}
//传入Comparator对象,用于比较
public BinarySearchTree(Comparator<E> comparator) {
this.comparator = comparator;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size==0;
}
public boolean contains(E element) {
return false;
}
public void clear() {
}
public void add(E element) {
//如果是第一个节点
if(size==0) {
root = new Node(element, null);
size++;
return;
}
//否则对元素进行比较找到放置的位置
Node<E> node=root,par=null;//记录父节点
int cmp=0;//保存比较大小
while(node!=null){
par=node;
cmp=compareTo(element,node.element);
//若元素小于当前节点
if(cmp<0){
node=node.left;
}else if(cmp>0){
node=node.right;
}else {//相同覆盖
node.element=element;
return ;
}
}
//此时找到插入位置
if(cmp<0){
par.left=new Node(element,par);
}else{
par.right=new Node(element,par);
}
size++;
}
public void remove(E element) {
}
/**
* 用于进行元素的比较
* @param e1
* @param e2
* @return
*/
private int compareTo(E e1,E e2){
//如果比较器不为空,则使用比较器
if(null!=comparator){
return comparator.compare(e1,e2);
}else {//否则要求实现Comparable接口的compareTo方法
return ((Comparable)e1).compareTo(e2);
}
}
@Override
public String toString() {//实现二叉树的简单打印
StringBuilder sb=new StringBuilder();
toString(root,sb,"");
return sb.toString();
}
/**
* 通过先序遍历的顺序拼接字符
* @param node
* @param sb
* @param preStr 需要在节点前拼接的字符
*/
private void toString(Node<E> node,StringBuilder sb,String preStr){
if(null==node)return;
//处理元素,拼接字符
sb.append(preStr).append(node.element).append('\n');
toString(node.left,sb,preStr+"left---");
toString(node.right,sb,preStr+"right--");
}
}
进行测试
public class BSTTest {
public static void main(String[] args) {
BinarySearchTree<Integer> bst=new BinarySearchTree<>();
int[] arr={4,5,6,2,3,9};
for(int i=0;i<6;i++)
bst.add(arr[i]);
System.out.println(bst);
}
}
输出结果为:
也就是: