BST定义
public class BST<E extends Comparable<E>>{
private class Node {
public E e;
public Node left, right;
public Node(E e) {
this.e = e;
this.left = null;
this.right = null;
}
}
与链表类似,链表有一个next 指向下一个节点 。bst有两个节点,分别指向左右两个孩子节点。
注意 泛型类定义如果用到compareT方法需要继承接口,写法上,否则报错。
add()方法:向二叉树中添加元素
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 add(node.right ,e);//0已经在终止条件排除
}
add改进
//添加新元素e
public void add(E e) {
root = add(root, e);
}
//向node为根节点的树插入新元素,返回插入后树的跟根结点
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);}//如果等于0就什么都不做
return node;//根不变
}
添加方法需要充分理解递归调用,size++在调用结束,如果重复元素则不会维护。
contains()方法:确定是否存在某个元素
//查询元素
public boolean contains(E e){
return contains(root,e);
}
private boolean contains (Node node, E e){
if(node==null)
return false;
if(e.compareTo(root.e)==0)
return true;
else if (e.compareTo(root.e)<0)
return contains(root.left,e);
else
return contains(root.right,e);
}
//二叉树的前序遍历
public void preOrder(){
preOrder(root);
}
//前序遍历的递归操作
public void preOrder(Node node){
if(node==null)//结束条件
return;
System.out.println(node.e);
preOrder(node.left);
preOrder(node.right);
}
中序遍历:二叉排序树的由来,会按照排序后顺序操作
//中序遍历
public void inOrder(){
inOrder(root);
}
private void inOrder(Node node){
if (node==null)
return;
inOrder(node.left);
System.out.println(node.e);
inOrder(node.right);
}
后序遍历:即最后处理根节点,先处理孩子节点
//后序遍历,先处理孩子节点,再处理孩子本身,如内存处理
public void postOrder(){
postOrder(root);
}
private void postOrder(Node node){
if (node==null)
return;
inOrder(node.left);
inOrder(node.right);
System.out.println(node.e);
}
//层序遍历 广度优先遍历
public void levelOrder(){
Queue<Node> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()){
Node cur = q.remove();
System.out.println(cur.e);
if(cur.left!=null)
q.add(cur.left);
if(cur.right!=null)
q.add(cur.right);
获得最小元素
//找到二叉树的最小值
public E minimum() {
if (root == null)
throw new IllegalArgumentException("bst为空");
return minimum(root).e;
}
private Node minimum(Node node) {
if (node.left == null)
return node;
else return minimum(node.left);
}
删除最小元素
//删除最小元素所在节点,接口设计原则:如果用户没有指定删除哪个元素,则返回被删除元素
public E removeMin() {
E ret = minimum();
root = removeMin(root);
return ret;
}
//删除最小元素所在节点,返回删除后树的根节点
private Node removeMin(Node node) {
if (node.left == null) {//往左走到头了
Node rightNode = node.right;//右节点
node.right = null;//把右节点从根节点摘下来,自己成为根节点
size--;//重点:理解此时的树 node为根 左子树为空 右子树摘下来等于删除根节点
return rightNode;
}
node.left = removeMin(node.left);
return node;
}
删除任意元素
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 {//e==node.e 这就是要删除的元素
if (node.left == null) {
Node rightNode = node.right;//右节点
node.right = null;//把右节点从根节点摘下来,自己成为根节点
size--;//重点:理解此时的树 node为根 左子树为空 右子树摘下来等于删除根节点
return rightNode;
}
if (node.right == null) {
Node leftNode = node.left;
node.left = null;
size--;
return leftNode;
}
//剩下左右子树都不为空的情况
// 步骤 找到子树中大于自己并且是最小的元素
//用这个节点代替被删除节点
Node successor = minimum(node.right);//右子树都大于根节点,找右里的最小
successor.right = removeMin(node.right);//这里size--了
successor.left = node.left;
node.right = node.left = null;
return successor;
}
二叉搜索树需要对递归有更深的理解,同链表相同,node的存在是的bst天生具备递归性质,由于是非线性结构,递归算法代码会大大缩减代码量,更简洁。
但是还要注意递归存在缺点,就是对方法栈的调用,递归过多,性能较差,且容易栈溢出。