在编写二叉树懒惰删除相关例程时,发现findMin和findMax两个方法很是复杂,于是上网百度懒惰删除相关例程的解法,却没有找到。今天硬是用比较粗糙的方法憋了出来,既然网上无法百度的到,那么我就把我的解法贴出来交流交流吧。
首先是节点类
public class TreeNode <T> {
private T value;//节点值
private TreeNode<T> left;//左节点
private TreeNode<T> right;//右节点
private TreeNode<T> father;//父节点
private boolean delete;//删除标记,若被删除则为true,初始化为false;
public TreeNode(T value)
{
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public TreeNode<T> getLeft() {
return left;
}
public void setLeft(TreeNode<T> left) {
this.left = left;
}
public TreeNode<T> getRight() {
return right;
}
public void setRight(TreeNode<T> right) {
this.right = right;
}
public TreeNode<T> getFather() {
return father;
}
public void setFather(TreeNode<T> father) {
this.father = father;
}
public boolean isDelete() {
return delete;
}
public void setDelete(boolean delete) {
this.delete = delete;
}
}
接着是二叉树类,采用懒惰删除思想
public class BinaryTree <T extends Comparable<? super T>> {
private TreeNode<T> root;
/**
* 从根部打印树
*/
public void print()
{
this.print(root);
}
/**
* 递归打印树
* @param node 树根结点
*/
public void print(TreeNode<T> node)
{
//若根节点不为空可进行递归打印,采用中序遍历
if(node != null)
{
//1.打印左节点
this.print(node.getLeft());
//2.若未被删除打印节点
if(!node.isDelete())
{
System.out.print(node.getValue() + "\t");
}
//3.打印右节点
this.print(node.getRight());
}
}
/**
* 从根部寻找节点
* @param value 节点值
* @return
*/
public boolean contain(T value)
{
return contain(value, root);
}
/**
* 递归寻找节点
* @param value 寻找节点值
* @param node 树根结点
* @return
*/
public boolean contain(T value, TreeNode<T> node)
{
if(node == null)
{
return false;
}
//1.比较大小
int result = value.compareTo(node.getValue());
//2.若小则从左节点寻找
if(result < 0)
{
return contain(value, node.getLeft());
}
//3.若大则从右节点寻找
else if(result > 0)
{
return contain(value, node.getRight());
}
//4.若相等并且被删除返回false
else if(result == 0 && node.isDelete())
{
return false;
}
//5.若相等并且未被删除返回true
else
{
return true;
}
}
/**
* 从根部开始添加节点
* @param value 被添加节点值
*/
public void add(T value)
{
root = add(value, root);
}
/**
* 递归添加节点
* @param value 被添加节点值
* @param node 树根结点
* @return 树根结点
*/
public TreeNode<T> add(T value, TreeNode<T> node)
{
if(node == null)
{
return new TreeNode<T>(value);
}
//1.比较大小
int result = value.compareTo(node.getValue());
//2.若小则在左子树进行添加操作
if(result < 0)
{
TreeNode<T> temp = add(value, node.getLeft());
//连接操作,防止当子节点为空时丢失子树
node.setLeft(temp);
temp.setFather(node);
}
//3.若大则在右子树进行添加操作
else if(result > 0)
{
TreeNode<T> temp = add(value, node.getRight());
//连接操作,防止当子节点为空时丢失子树
node.setRight(temp);
temp.setFather(node);
}
//4.相等并且被删除则修改删除标记为false;
else if(result == 0 && node.isDelete())
{
node.setDelete(false);
}
return node;
}
/**
* 从根部开始删除节点
* @param value 目标节点值
*/
public void remove(T value)
{
remove(value, root);
}
/**
* 递归懒惰删除节点
* @param value 目标节点值
* @param node 树根结点
* @return 树根结点
*/
public void remove(T value, TreeNode<T> node)
{
if(node == null)
{
return;
}
//1.比较大小
int result = value.compareTo(node.getValue());
//2.若小在左子树进行删除操作
if(result < 0)
{
remove(value, node.getLeft());
}
//3.若大在右子树进行删除操作
else if(result > 0)
{
remove(value, node.getRight());
}
//4.若相等并且未被删除则修改标记为true
else if(!node.isDelete())
{
node.setDelete(true);
}
}
/**
* 从根节点寻找最小节点
* @return 最小节点
*/
public TreeNode<T> findMin()
{
return this.findMin(root);
}
/**
* 递归寻找最小节点
* 先找到最小节点,再根据是否被删除进行后续处理
* @param node 树根节点
* @return 最小节点
*/
public TreeNode<T> findMin(TreeNode<T> node)
{
if(node == null)
{
return null;
}
else if(node.getLeft() == null)//找到最小节点
{
//1.未被删除则为最小节点
if(!node.isDelete())
{
return node;
}
//2.已被删除进行后续判断
else
{
//2.1若右子节点不为空,则在右子树中寻找最小节点
if(node.getRight() != null)
{
return this.findMin(node.getRight());
}
//2.2若右节点为空并且父节点为空
else if(node.getFather() == null)
{
return null;
}
//2.3若右节点为空并且父节点不为空
else
{
TreeNode<T> father = node.getFather();
//2.3.1取得下一个较大的父节点
while(father.getLeft() != node && father != null)
{
node = father;
father = father.getFather();
}
//2.3.2若父节点不为空
if(father != null)
{
father.setLeft(null);
TreeNode<T> min = this.findMin(father);
node.setLeft(node);
return min;
}
//2.3.3若父节点为空
else
{
return null;
}
}
}
}
//递归寻找最小节点
else
{
return this.findMin(node.getLeft());
}
}
}
总结:懒惰删除方便了删除方法,省去了删除的额外操作,也方便了添加节点,当想要添加的节点已经处于被删除状态时,只需要修改删除标志即可。
懒惰删除比较麻烦的处理是在findMin和findMax两个方法中,由于两者相似,例子中只给出了findMin的实现,需要进行一系列周全的考虑,很是繁琐。其实可以通过给每个节点设置上下节点这种方式,来简化findMin和findMax这两个方法,相当于构建有序的链表。