一、二分搜索树简介
二分搜索树(英语:Binary Search Tree),也称为 二叉查找树 、二叉搜索树 、有序二叉树或排序二叉树。满足以下几个条件:
- 若它的左子树不为空,左子树上所有节点的值都小于它的根节点。
- 若它的右子树不为空,右子树上所有的节点的值都大于它的根节点。
它的左、右子树也都是二分搜索树。
如下图所示:
二、具体实现
如上图所示:二分搜索树是由一个个节点组成,
节点由俩部分组成:
- 1.元素
- 2.指向左右子节点的指针
根据上面节点的组成,定义节点如下:
private class Node{
public E e;
public Node left,right;
public Node(E e){
this.e = e;
left = null;
right =null;
}
}
由于二分搜索树是由根节点进行排序,则需要记录根节点
则创建一个二分搜索树类BST:
//为了使元素支持各种类型,采用泛型
//因为需要排序,而元素E还应支持比较大小,则继承Comparable
public class BST<E extends Comparable<E>> {
private Node root;
private int size;
public BST(){
root = null;
size = 0;
}
1.添加元素
a.分析业务逻辑
在进行添加元素时,跟当前元素进行比较,
若小,则看其左节点是否为null,为null直接赋值到左节点上,否则继续递归。
若大,则看其右节点是否为null,为null直接赋值到右节点上,否则继续递归。
b.代码实现
方式一
//向二分搜索树中添加某一元素
public void add(E e){
if(root ==null){
root = new Node(e);
size++;
}else{
add(root,e);
}
}
private void add(Node node,E e){
if(e.equals(node.e)){
return;
} else if(e.compareTo(node.e)<0 && node.left == null){
node.left = new Node(e);
size++;
return;
}else if(e.compareTo(node.e)>0 && node.right == null){
node.right = new Node(e);
size ++;
return;
}
if(e.compareTo(node.e)<0){
add(node.left,e);
}else if(e.compareTo(node.e)>0){
add(node.right,e);
}
}
方式二
//向二分搜索树中添加某一元素
public void add(E e){
root = add(root,e);
}
private Node add(Node node,E e){
if(node == null){
size ++;
return new Node(e);
}
if(e.compareTo(node.e)<0){
node.left = add(node.left,e);
}else if(e.compareTo(node.e)>0){
node.right = add(node.right,e);
}
return node;
}
2.遍历二分搜索树
方式一:深度优先遍历
//二分搜索树前序遍历
public void preOrder(){
preOrder(root);
}
private void preOrder(Node node){
if(node ==null){
return;
}
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
方式二:广度优先遍历(由上到下一层层遍历)
a.分析业务逻辑
1.当程序走到根节点时,节点中的元素可以拿到
2.当遍历第二层时,需要通过上一步节点中的左右节点获取元素
3.当遍历第三层时,还是需要通过上一步节点中的左右节点获取元素
…
由此看出,再执行第一步时,需要将根节点的左右节点放在一个地方进行存储,再执行第二步时,取出上一步放的节点node1,从而获取元素,并将node1节点的左右节点存储方便下一步进行取用。
根据以上特性,可以将左右节点存放在队列中
逻辑如下:读取root节点,取出root节点的元素,将root节点的左节点、右节点分别放入队列中,遍历第二层时,先取出左节点,取出节点元素,将此节点的左右节点分别放入队列中;再取出root的右节点执行上面操作…当队列中没有节点时,也就遍历结束。
b.代码实现
//广度优先遍历
public void levelOrder(){
Queue<Node> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty()){
Node cur = queue.remove();
System.out.println(cur.e);
if(cur.left != null){
queue.add(cur.left);
}
if(cur.right != null){
queue.add(cur.right);
}
}
}
3.删除元素
a.分析业务逻辑
1.删除的节点没有左右节点时,直接删除
2.删除的节点node有左节点或者右节点时,将node的左节点挂在node父节点上、或将node的右节点挂在node父节点上
3.删除的节点左右节点都存在时,这时就不能直接将该节点的左右节点挂在父节点上。需要从删除的节点node下,找出最小元素所在的节点放在node节点位置处。
b.代码实现
//删除二分搜索树中元素e
public void remove(E e){
root = remove(root,e);
}
private Node remove(Node node,E e){
//需要删除的元素未找到
if(node == null){
return null;
}
//需要删除的元素比当前节点上元素小,则需要去其左节点进行删除
if(e.compareTo(node.e)<0){
node.left = remove(node.left,e);
return node;
//需要删除的元素比当前节点上元素大,则需要去其右节点进行删除
}else if(e.compareTo(node.e)>0){
node.right = remove(node.right,e);
return node;
//当前节点就是需要删除的
}else {
//如果只存在右节点,则将右节点返回
if(node.left == null){
Node nodeRight = node.right;
node.right = null;
size --;
return nodeRight;
}
//如果只存在左节点,则将左节点返回
if(node.right == null){
Node nodeLeft = node.left;
node.left = null;
size--;
return nodeLeft;
}
//找到当前节点的右节点下最小元素所在节点
Node middleNode = findMinimum(node.right);
//将需要删除的node节点的左节点挂载
middleNode.left = node.left;
middleNode.right = removeMin(node.right);
node.right = node.left = null;
return middleNode;
}
}
//查询node节点下最小的元素,返回最小元素所在的节点
private Node findMinimum(Node node){
if(node.left == null){
return node;
}
return findMinimum(node.left);
}
//删除最小元素所在节点,返回其右节点
private Node removeMin(Node node){
if(node.left == null){
Node nodeRight = node.right;
node.right = null;
size --;
return nodeRight;
}
node.left = removeMin(node.left);
return node;
}