JAVA搜索二叉树的重构

~~接着实现搜索二叉树,BST继承了BinaryTree(写在另一篇中了),由于是继承二叉树,搜索二叉树中只写了其特有的方法。本文章仅供学习参考使用,转载附明出处。
__方法包括:添加方法,删除方法,判断结点是否存在的方法。代码中BST的一个构造方法中需要加入比较器。为了防止是对引用数据类型进行比较多次比较。
意思大概是这样的,如果你在树的初始化中添加比较器,那么我们就按照比较器的比较逻辑来对比。如果你没加入比较器,我们就用引用类型自带(继承Comparable后)的比较逻辑来对比。这样的好处是可以灵活的改变比较的逻辑。比如一棵树你想比较年龄,另一棵树你想比较价格。此代码中你只要在传入的比较器中改变比较逻辑即可。想一想如果别人没有这个需求(需要不同的比较逻辑),那我们就不用传比较器。直接使用引用类型自带的比较逻辑即可。
除此之外还有一些给子类使用的的接口,比如afterAdd,afterRemove 这些都是为了下面写AVL树做铺垫。

代码如下
import java.util.Comparator;

import tree.BinaryTree.Node;




public class BST<E> extends BinaryTree<E> {
	//引用类型的比较器
	private Comparator<E> comparator;
	
	//无参构造方法
	public BST() {
		
	}
	//有比较器的构造方法
	public BST(Comparator<E> comparator) {
		this.comparator = comparator;
		
	}
	
	/*
	添加方法 
	 */
	public void add(E element) {
		elementNotNullCheck(element);
		
		//添加第一个节点
		if (root == null) {
			root = createNode(element, null);
			size ++;
			afterAdd(root);
			return;
		}
		//添加的不是第一个节点
		
		//用来记录 父节点位置
		Node<E> parent = root;
		//比较是从父节点开始的
		Node<E> node = root;
		//我们还要记下方向(左节点 or 右节点)
		int cmp = 0;
		
		while(node != null) {
			//记录父节点和方向
			cmp = compare(element, node.element);
			parent = node;  
			if (cmp > 0) {
				node = node.right;
			}else if (cmp < 0) {
				node = node.left;
			}else {
				//相等覆盖
				//防止是引用类型 除了年龄还有别的属性
				node.element = element;
				return;
			}
		}
		//创建新节点
		Node<E> newNode = createNode(element, parent);
		//插入父节点左右
		if (cmp > 0) {
			parent.right = newNode;
		}else {
			parent.left = newNode;
		}
		
		
		size ++;
		afterAdd(newNode);

	}
	
	/*
	 创建节点的接口
	 */
	protected Node<E> createNode(E element, Node<E> parent){
		return new Node<>(element, parent);
	}
	
	
	
	/*
	 添加node之后调整的接口
	 */
	protected void afterAdd(Node<E> node) {}
	
	
	/*
	 删除后恢复平衡的方法
	 */
	protected void afterRemove(Node<E> node) {}
	
	
	/*
	 删除元素方法
	 */
	
	public void remove(E element) {
		//删除元素对应的结点
		remove(node(element));
		
	}
	
	/*
	 分三大情况讨论
	 我们线讨论度为2的结点 有原因的 可以简化代码
	 */	
	private void remove(Node<E> node) {
		//结点为空
		if(node == null) return;
		
		size --;
		
		//删除结点是度为2的结点
		if (node.hasTwoChildren()) {
			//找后继结点
			Node<E> s = successor(node);
			//后继节点的值覆盖原来结点
			node.element = s.element;
			//下面还要删除后继结点
			node = s;
		}
		
		//删除node 结点(现在node的度必然为0 或者1)
		Node<E> replacement = node.left != null ? node.left : node.right;
		
		if (replacement != null) {//node 是度为1的结点
			if (node == node.parent.left) {//node是左子节点
				replacement.parent = node.parent;
				node.parent.left = replacement;
			}else {//node是右子节点
				replacement.parent = node.parent;
				node.parent.right = replacement;
			}
			afterRemove(node);
		}else if (node.parent == null) {//node是叶子结点并且是根结点
			
			root = null;
			afterRemove(node);
		}else {//是叶子结点 且不是根结点 直接删除
			
			if (node == node.parent.left) {
				node.parent.left = null;
			}else {
				node.parent.right = null;
			}
			afterRemove(node);
		}
		
	}
	/*
	 查找一个节点是否存在
	 */
	private Node<E> node(E element){
		//查找从根节点开始
		Node<E> node = root;
		
		while(node != null) {
			int cmp = compare(element, node.element);
			if (cmp == 0) {
				//找到了
				return node;
			}else if (cmp > 0) {
				node = node.right;
			}else {
				node = node.left;
			}
		}
		
		//循环出来就是没找到啊
		return null;
		
	}
	
	
	/*
	 是否包含元素方法
	 */
	
	public boolean contains(E element) {
		return node(element) != null;
	}
	
	
	/*
	 搜索树 节点不能为空的判断方法
	 */
	
	public void elementNotNullCheck(E element) {
		if (element == null) {
			throw new IllegalArgumentException("element must not be null");
			
		}
	}
	
	/*
	 比较函数 
	 */
	private int compare(E e1,E e2) {
		if (comparator != null) {
			return compare(e1, e2);
		}
		return ((Comparable<E>)e1).compareTo(e2);
	}
		
	
	
}


猜你喜欢

转载自blog.csdn.net/qq_43507104/article/details/105800547