二叉搜索树是一种特殊类型的二叉树,它的特点是在子节点的值<根节点值<游子节点的值,利用二叉搜索树进行查询的效率为O(logn),与折半查找相当,在一些数据检索相关的程序中很多都用到了二叉树或二叉树的优化与变种树,对于一个二叉树的操作主要包括以下几个:
- 初始化
- 新增节点
- 判断是否包含某个值
- 查找最小值
- 查询最大值
- 删除最小值
- 删除最大值
- 删除给定的值
- 前序遍历
- 中序遍历
- 后续遍历
下面试一个二叉搜索树的具体实现:
package com.base.ds.csdn.tree;
import java.util.LinkedList;
import java.util.Queue;
/**
* 二分搜索树.
* 特点:左子节点 < 根节点 < 右子节点.
* 操作:
* 1. 添加
* 2. 删除
* 3. 遍历(前、中、后序遍历、层序遍历)
* */
public class BSTree<E extends Comparable<E>> {
private Node<E> root;
private int size;
//初始化
public BSTree() {
this.root = null;
size = 0;
}
/**
* 添加数据.
* */
public void add(E e) {
//添加操作完成后,root节点可能发生变化
root = add(e, root);
}
//利用递归,添加节点
private Node<E> add(E e, Node<E> node) {
if (node == null) {
size ++;
return new Node(e);
}
//如果当前数据比当前节点的值小,则向左添加
if(e.compareTo(node.data) < 0) {
node.left = add(e, node.left);
} else if (e.compareTo(node.data) > 0) {
node.right = add(e, node.right);
}
return node;
}
/**
* 判断释放包含某个数据
* */
public boolean contains(E e) {
if (isEmpty()) {
throw new IllegalArgumentException("bst is empty.");
}
return contains(e, root);
}
//利用递归来判断释放包含
private boolean contains(E e, Node<E> node) {
if (node == null) {
return false;
}
if (e.compareTo(node.data) == 0) {
return true;
} else if (e.compareTo(node.data) < 0) {
return contains(e, node.left);
} else {
return contains(e, node.right);
}
}
/**
* 找到最小的数据
* */
public E min() {
if (isEmpty()) {
throw new IllegalArgumentException("bst is empty.");
}
return minNode(root).data;
}
private Node<E> minNode(Node<E> node) {
if (node.left == null) {
return node;
}
return minNode(node.left);
}
/**
* 找到最大的数据
* */
public E max() {
if (isEmpty()) {
throw new IllegalArgumentException("bst is empty.");
}
return maxNode(root).data;
}
private Node<E> maxNode(Node<E> node) {
if (node.right == null) {
return node;
}
return maxNode(node.right);
}
/**
* 删除最小值
* */
public E removeMin() {
E ret = min();
root = removeMin(root);
return ret;
}
//用递归的方式实现
private Node<E> removeMin(Node<E> node) {
if (node.left == null) {
Node<E> right = node.right;
node.right = null;
size --;
return right;
}
node.left = removeMin(node.left);
return node;
}
/**
* 删除最大值
* */
public E removeMax() {
E ret = max();
root = removeMax(root);
return ret;
}
private Node<E> removeMax(Node<E> node) {
if (node.right == null) {
Node<E> left = node.left;
node.left = null;
size --;
return left;
}
node.right = removeMax(node.right);
return node;
}
/**
* 移除给定值的节点
* */
public void remove(E e){
root = remove(e, root);
}
private Node<E> remove(E e, Node<E> node) {
//如果没有找到目标节点就直接范围null
if (node == null) {
return null;
}
//给定值比当前节点的值小
if (e.compareTo(node.data) < 0) {
node.left = remove(e, node.left);
return node;
} else if (e.compareTo(node.data) > 0) {
//给定值比当前节点的值大
node.right = remove(e, node.right);
return node;
} else {
//找到了要删除的节点
/**
* 没有左子节点
* 没有右子节点
* 即有左子节点也有右子节点
* */
//没有左子树
if (node.left == null) {
Node<E> right = node.right;
node.right = null;
size --;
return right;
}
//没有右子树
if (node.right == null) {
Node<E> left = node.left;
node.left = null;
size --;
return left;
}
//即有左子树又有右子树
//思路是:用左子树中的最大节点 或 用右子树中的最小节点来替换删除的节点,这样可以
//保证二叉搜索树的性质
//找到又子树中的最小节点
Node<E> rightMinNode = minNode(node.right);
//将右子树中的最小节点删除,并把删除后的新的根节点挂在 上面得到的Node的右侧分支
rightMinNode.right = removeMin(node.right);
//将当前待删除节点的left挂到rightMinNode上
rightMinNode.left = node.left;
//help GC
node.right = null;
node.left = null;
return rightMinNode;
}
}
/**
* 前序遍历:父节点、左节点、右节点
* 中序遍历:左节点、父节点、右节点
* 后续遍历:左节点、右节点、父节点
* 层序遍历:一层一层遍历
* */
//前
public void preOrder() {
preOrder(root);
}
private void preOrder(Node<E> node) {
if (node == null) {
return;
}
System.out.println(node.data);
preOrder(node.left);
preOrder(node.right);
}
//中
public void inOrder() {
inOrder(root);
}
private void inOrder(Node<E> node) {
if (node == null) {
return;
}
inOrder(node.left);
System.out.println(node.data);
inOrder(node.right);
}
//后
public void postOrder() {
postOrder(root);
}
private void postOrder(Node<E> node) {
if (node == null) {
return;
}
postOrder(node.left);
postOrder(node.right);
System.out.println(node.data);
}
//层序遍历,借助队列
public void levelOrder() {
if (root == null) {
return;
}
Queue<Node<E>> queue = new LinkedList<>();
queue.add(root);
while (!queue.isEmpty() && root != null) {
Node<E> cur = queue.remove();
System.out.println(cur.data);
if (cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
}
public boolean isEmpty() {
return size == 0;
}
private static class Node<E extends Comparable<E>>{
private E data;
private Node<E> left;
private Node<E> right;
public Node(E elem) {
this(elem, null, null);
}
public Node(E elem, Node<E> left, Node<E> right) {
this.left = left;
this.right = right;
this.data = elem;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
}
public static void main(String[] args) {
BSTree<Integer> bsTree = new BSTree<>();
bsTree.add(5);
bsTree.add(3);
bsTree.add(8);
bsTree.add(2);
bsTree.add(4);
bsTree.add(1);
bsTree.add(7);
bsTree.add(10);
bsTree.add(9);
bsTree.add(15);
/**
5
3 8
2 4 7 10
1 9 15
1,2,3,4,5,7.,8,9,10,15
* */
bsTree.postOrder();
bsTree.levelOrder();
}
}
471

被折叠的 条评论
为什么被折叠?



