java二叉查找树实现:
二叉查找树,上图:比根节点小者在其左边,比根节点大者在其右边。
抽象数据结构,上代码:
/*** 二叉查找树数据结构(非线程安全):
* 范型类型须实现Comparable接口,用于比较操作*/
public class BinarySearchTree>{private Node root; //tree root
private int count; //tree element counts
/*** 内部节点类*/
private static class Node{
E value;//元素对象
Node parent; //父节点
Node left; //左孩子节点
Node right; //右孩子节点
public Node(E value, Node parent, Node left, Noderight) {this.value =value;this.parent =parent;this.left =left;this.right =right;
}
}
}
一些基本操作实现:
插入(insert): 依次比较根元素,小者放左边,大者放右边:
/*** 插入元素
*@paramt 待插入元素
*@return插入成功返回true, 反之返回false*/
public booleaninsert(T t){if (root == null){ //若为空树
root = new Node(t, null, null, null);return true;
}
Node newNode = new Node(t, null, null, null);
Node pointer =root;while(true){if (newNode.value.compareTo(pointer.value) > 0){if (pointer.right == null){ //插入右边
newNode.parent =pointer;
pointer.right=newNode;
count++;return true;
}else{
pointer=pointer.right;
}
}else if (newNode.value.compareTo(pointer.value) < 0){if (pointer.left == null){ //插入左边
newNode.parent =pointer;
pointer.left=newNode;
count++;return true;
}else{
pointer=pointer.left;
}
}else { //相等了
return false;
}
}
}
查找(get):
/*** 查找元素
*@paramt 待查找元素
*@return对应元素或null*/
publicT get(T t) {
Node n =getN(t);return n == null? null: n.value;
}/*** 查找节点
*@paramt 待查找元素
*@return元素对应节点或null*/
private NodegetN(T t) {
Node cur =root;while (cur != null){if (cur.value.compareTo(t) < 0){ //右边子树找
cur =cur.right;
}else if(cur.value.compareTo(t) > 0){ //左边子树找
cur =cur.left;
}else{ //找到该节点
break;
}
}returncur;
}
查找最大,最小元素:
/*** 获取某节点为根的树的最小元素*/
public T min(Noden){
Node min =minN(n);return min == null ? null: min.value;
}/*** 获取某节点为根的树的最小节点
*@paramn 树根节点
*@return该子树最小节点*/
private Node minN(Noden){
Node min =n;while (min != null && min.left != null){
min=min.left;
}returnmin;
}/*** 获取某节点为根的树的最大元素
*@return最大元素, 没有返回null*/
public T max(Noden){
Node max =maxN(n);return max == null ? null: max.value;
}/*** 获取某节点为根的树的最大节点*/
private Node maxN(Noden){
Node max =n;while (max != null && max.right != null){
max=max.right;
}returnmax;
}
遍历树(中序遍历):
/*** 中序遍历*/
public voidleftRootRight(){
printLRR(root);
}/*** 中序遍历打印元素*/
private void printLRR(Nodenode) {if (node != null){
printLRR(node.left);
System.out.println(node.value);
printLRR(node.right);
}
}
获取前驱(prev)元素:
主要有两种情况:
1.该节点左子树不为空:其前驱节点为其左子树的最大元素:
2.该节点左子树为空: 其前驱节点为其祖先节点(递归),且该祖先节点的右孩子也为其祖先节点(就是一直往其parent找,出现左拐后的那个祖先节点):
代码实现:
/*** 获取元素前驱(中序遍历)
*@paramt 指定元素
*@return元素前驱,没有返回null*/
publicT prev(T t){//先找到该元素
Node cur =getN(t);if (cur != null){returnlocatePrev(cur);
}return null;
}/*** 定位到前驱节点
*@paramcur 当前节点
*@return前驱节点,没有返回null*/
private T locatePrev(Nodecur) {
Node prev =locatePrevN(cur);return prev == null ? null: prev.value;
}/*** 定位到前驱节点
*@paramcur 当前节点
*@return当前节点的前驱节点*/
private Node locatePrevN(Nodecur){if (cur != null){//1.如果该节点左子树不会空,则其前驱为其左子树的最大元素
if (cur.left != null) returnmaxN(cur.left);//2.该节点左子树为空, 则其前驱为:其祖先节点(递归), 且该祖先节点的右孩子也是其祖先节点//通俗的说,一直忘上找找到左拐后那个节点;
Node p =cur.parent;while(p != null && cur ==p.left){
cur=p;
p=p.parent;
}return p == null ? null: p;
}return null;
}
获取后继节点,也分两种情况:
1.该节点右子树不为空,其后继节点为其右子树的最小元素:
2.该节点右子树为空,其后继节点为其祖先节点(递归),且此祖先节点的左孩子也是该节点的祖先节点,就是说一直往上找其祖先节点,直到出现右拐后的那个祖先节点:
实现代码:
/*** 获取元素后继元素(中序遍历)
*@paramt 指定元素
*@return后继元素,没有返回null*/
publicT next(T t){//先找到该元素
Node cur =getN(t);if (cur != null){returnlocateNext(cur);
}return null;
}/*** 定位当前节点的后继元素
*@paramcur 当前节点
*@return其后继元素*/
private T locateNext(Nodecur) {
Node next =locateNextN(cur);return next == null ? null: next.value;
}/*** 定位到当前节点的后继节点
*@paramcur 当前节点
*@return当前节点的后继节点*/
private Node locateNextN(Nodecur) {if (cur == null) return null;//1.若其右子树不为空,那么其后继节点就是其右子树的最小元素
if (cur.right != null) returnminN(cur.right);//2.若为空,应该为其祖先节点(递归),且该祖先节点的左孩子也是其祖先节点//通俗的说,一直忘上找,找到右拐后那个节点;
Node p =cur.parent;while (p != null && cur ==p.right){
cur=p;
p=p.parent;
}returnp;
}
删除(remove), 可分为三种情况:
1.该节点为叶子节点,直接删除:
2.该节点有一个孩子,将其孩子接上其父节点:
3.该节点有2个孩子,先删除其右子树的最小元素(该元素最多只会有一个孩子),将这个最小元素去替换要删除的节点:
实现代码:
/*** 移除某元素
*@paramt 待删除元素
*@return删除成功返回true, 反之false*/
public booleanremove(T t){//找到该节点
Node cur =getN(t);if (cur != null){if(doRemove(cur)){
cur=null; count--;return true;
}
}return false;
}/*** 执行删除操作*/
private boolean doRemove(Nodecur) {//该节点是否为根
boolean isRoot = cur ==root;//1.该节点为叶子节点, 直接将其父节点对应(左或右)孩子置空
if (cur.left == null && cur.right == null){if (isRoot) return true; //若树只有一个根节点
if (cur == cur.parent.right) //该节点为父节点的右孩子
cur.parent.right = null;else //该节点为父节点的左孩子
cur.parent.left = null;return true;
}else if(cur.left != null && cur.right != null){//2.该节点有2个孩子, 我们先找出一个替换节点(该节点的后继节点,后继节点没有则前驱节点)//找到其后继节点
Node replaceNode =locateNextN(cur);if (replaceNode == null) //若没有后继节点则用前驱节点
replaceNode =locatePrevN(cur);
doRemove(replaceNode);
cur.value=replaceNode.value;return true;
}else{ //3.该节点有1个孩子, 直接将其父节点对应(左或右)孩子接到其非空孩子
Node needLinkedNode = null;if (cur.left == null && cur.right != null){ //该节点有右孩子
needLinkedNode =cur.right;
}else if(cur.left != null && cur.right == null){ //该节点有左孩子
needLinkedNode =cur.left;
}if(isRoot){ //若该节点为根
root =needLinkedNode;return true;
}if (cur == cur.parent.right) //该节点为父节点右孩子
cur.parent.right =needLinkedNode;elsecur.parent.left=needLinkedNode;return true;
}
}