二叉搜索树(Java) – 左小右大
树的概念应该比较直观,一个根延伸出很多的分支,每个分支又延伸出多个分支,以此类推,直到没有分支的为叶,以此枝繁叶茂;二叉树则是一棵有性格的树,规定了一个节点的分支不能超过2个;二叉搜索树则是在这个基础上再规定了,一个节点的左子节点的值必须小于该节点的值,右子节点的值必须大于该节点的值
这里二叉搜索树是基于递归实现的,递归实现起来要简便一些,思路是从上至下的,只需确定好递归条件和递归结束条件即可;BST类中主要实现了二叉搜索树的增删改查;
需要注意的是,二叉搜索树中的每一个节点都是一个对象(这里是内部类class Node),其中存放了键值和值,左节点指针和右节点指针,二叉搜索树中的排序比较是以各节点的键值进行比较的;定义为public中的方法供外部调用,方法功能的实现则定义为private,命名则为前面加go,如插入方法public供外部调用的是insert方法,实际实现则是private的goInsert方法~~
二叉搜索树 – 递归玩法
**
* 二叉搜索树,类内部的最后面设置了节点的类;
* 这里的二叉搜索树为大数据做了准备,键值的存在就是为了排位用的,从而不用实际的数据进行排位
*/
public class BST<Key extends Comparable<Key>, Data> {
private Node root;
private int size;
public BST() {
root = null;
size = 0;
}
public int size() {return size;}
public boolean isEmpty() {return size == 0;}
/**
* 插入元素实现
*/
// 插入方法接口
public void insert(Key key, Data data) { root = goInsert(root,key,data); }
// 插入方法的实现方法;向以node为根或父节点的树或子树插入节点,最终返回根节点
private Node goInsert(Node node, Key key, Data data) {
// 如果当前的根或父节点为null,则需要新增一个节点存储新数据
if(node == null) {
node = new Node(key, data);
size++;
return node;
}
// 键值一样时更新数据,插入的键值较小时往左子树插入,插入的键值较大时往右子树插入
if(key.compareTo(node.key) == 0) node.data = data;
else if(key.compareTo(node.key) < 0) { node.left = goInsert(node.left,key,data); }
else { node.right = goInsert(node.right,key,data); }
return node;
}
/**
* 查找元素实现
*/
// 查找元素方法
public Boolean find(Key key) {
assert size != 0;
return goFind(root,key);
}
// 查找元素的实现方法
private Boolean goFind(Node node, Key key) {
if(node == null) return false;
if(key.compareTo(node.key) == 0) return true;
else if(key.compareTo(node.key) < 0) return goFind(node.left,key);
else return goFind(node.right,key);
}
/**
* 遍历元素实现
*/
// 先序遍历
public void preOrderTraversal() {
goPreOrderTraversal(root);
}
// 先序遍历实现
private void goPreOrderTraversal(Node node) {
if(node != null) {
System.out.print(node.key + " ");
goPreOrderTraversal(node.left);
goPreOrderTraversal(node.right);
}
}
// 中序遍历
public void inOrderTraversal() {
goInOrderTraversal(root);
}
// 中序遍历实现
private void goInOrderTraversal(Node node) {
if(node != null) {
goInOrderTraversal(node.left);
System.out.print(node.key + " ");
goInOrderTraversal(node.right);
}
}
// 后序遍历
public void postOrderTraversal() {
goInOrderTraversal(root);
}
// 后序遍历实现
private void goPostOrderTraversal(Node node) {
if(node != null) {
goPostOrderTraversal(node.left);
goPostOrderTraversal(node.right);
System.out.print(node.key + " ");
}
}
// 层序遍历
public void TierOrderTraversal() {
if(root == null) return;
goTierOrderTraversal(root);
}
// 层序遍历实现
private void goTierOrderTraversal(Node node) {
// 用LinkedList作为队列
Queue<Node> q = new LinkedList<Node>();
q.add(node);
// 只要队列中还有节点
while(!q.isEmpty()) {
// 弹出队列第一个元素
Node hen = q.remove();
System.out.print(hen.key + " ");
if(hen.left != null) q.add(hen.left);
if(hen.right != null) q.add(hen.right);
}
}
/**
* 查找
*/
// 获取最大值
public Key getMax() {
// 确保有元素
assert size != 0;
return goGetMax(root).key;
}
// 获取最大值实现
private Node goGetMax(Node node) {
// 最右边的子节点为大哥,如果右边没大哥了,说明找到最大值了
if(node.right == null) return node;
// 右边还有子节点,继续遍历
return goGetMax(node.right);
}
// 获取最小值
public Key getMin() {
// 确保有元素
assert size != 0;
return goGetMin(root).key;
}
// 获取最小值实现,实现逻辑和获取最大值相似
private Node goGetMin(Node node) {
if(node.left != null) return goGetMin(node.left);
else return node;
}
// 是否存在某个元素
public boolean isExisted(Key key) {
return goIsExisted(root,key);
}
// 是否存在某个元素实现
private boolean goIsExisted(Node node, Key key) {
if(node == null) return false;
if(node.key.compareTo(key) == 0) return true;
else if(node.key.compareTo(key) > 0) return goIsExisted(node.left,key);
else return goIsExisted(node.right,key);
}
// 获取某个元素的数据
public Data getData(Key key) {
return goGetData(root, key).data;
}
// 获取某个元素的数据实现
private Node goGetData(Node node, Key key) {
if(node == null) return null;
if(node.key.compareTo(key) == 0) return node;
else if(node.key.compareTo(key) > 0) return goGetData(node.left,key);
else return goGetData(node.right,key);
}
/**
* 节点类
*/
// 节点中存储键值和数据,还要记录左右子节点
private class Node {
private Key key;
private Data data;
private Node left, right;
public Node(Key key, Data data) {
this.key = key;
this.data = data;
left = right = null;
}
}
}
二叉搜索树 – 性能
二叉搜索树虽然具有左小右大的特性,但不一定是完全二叉树,甚至在最尴尬的情况下会变成一棵斜二叉树,也就是一个所有子节点都是左节点或都是右节点,说穿了就成了一个链表了;当键值从小到大插入时就会变成一棵右斜二叉树了,从大倒下插入则会变成一棵左斜二叉树,所以顺序插入会使二叉搜索树的性能退化成链表;所以一般情况下,也就是随机插入键值时,BST通常是优于二分查找法的,但是如果退化成链表,那就是一个O(n)级别的选手了,尴尬程度可想而知,所以在二叉搜索树后又催生了基于平衡的平衡二叉搜索树,如AVL和红黑树,下回分解~~