java实现二叉搜索树的删除(非递归方式)
网上有很多的二叉搜索树的java实现都是基于递归的,非递归看了半天好多都是一模一样的,并且好多都有问题,下面就是我在网上看到的最多的错误版本:
public void deleteMin(Node node){ //删除该节点下的最小节点
Node currentNode=node;
Node parentNode=node;
if(currentNode==null)
return;
//先定位到key,不用已经写好的find方法而自己写是因为find方法是从根开始的,有些地方的用到这个函数不用从根开始
//一直往左深入,直到它的左子节点为null,然后将其右子节点替换掉该节点(如无右节点即为null)。
while(currentNode.left!=null){
parentNode=currentNode;
currentNode=currentNode.left;
}
parentNode.left=currentNode.right;
currentNode.right=null;
}
此处错误就是并没有考虑删除节点是根节点的情况,如待删除的树没有左节点和右节点,此时,按照上面代码的逻辑,并不会进入while循环,currentNode = parentNode,currentNode并没有被删除,所以用这种方式删除是不能完全删除的。下面时改进的代码
public E removeMin() {
E res = getMin(root).e;
root = removeMin(root);
return res;
}
private Node<E> removeMin(Node<E> cur) {
Node<E> resCur = cur; //临时保存cur的值,将修改后的值返回回去
Node<E> p = cur; //cur的父节点
if (cur == null) {
throw new NoSuchElementException();
} else if (cur.left == null) { //当没有左节点时,最小值就是自己(cur)
Node<E> res = cur.right; //创建节点返回cur的右节点,将自己删除
size--;
return res;
}
while (cur.left != null) {
p = cur;
cur = cur.left;
}
p.left = cur.right;
cur.right = null;
size--;
return resCur;
}
解决办法是在while循环前加if判断当前节点是否为最小节点,起初,我是想让cur = cur.right将根节点删除的,可是发现这样删除cur的根节点引用并不会消失,也怪自己对对象的引用没有太深刻的理解。受递归代码的实现的启发,将删除节点后的树赋值给新创建的节点res,返回给删除之前的树,这样就可以成功删除掉了。删除最大值也是一样的。由于本人第一次写博客,可能表达不是太清晰,望理解。下面时二叉搜索树的全部实现,本人能力有限,如果有错误的地方欢迎指正。(总感觉自己写的代码有很大的优化空间,欢迎指出,谢谢)
package tree;
import basic.Stack;
import java.util.Comparator;
import java.util.NoSuchElementException;
/**
* @author yqs
* @description 二叉搜索树
* define 左子树比根节点小,右子树比根节点大
* @time 19-7-23 下午7:26
*/
public class BinarySearchTree<E extends Comparable<E>> {
private int size;
private Node<E> root;
public BinarySearchTree(){
root = new Node<>();
size = 0;
}
private class Node<E> {
private Node<E> left;
private Node<E> right;
private E e;
public Node() {}
public Node(Node<E> left, E e, Node<E> right) {
this.left = left;
this.e = e;
this.right = right;
}
}
public boolean add(E e) {
if (size == 0) {
root = new Node<>(null,e,null);
size++;
return true;
}
Node<E> cur = root;
Node<E> p = cur;
while (cur != null) {
p = cur;
if (e.compareTo(cur.e) < 0)
cur = cur.left;
else if (e.compareTo(cur.e) > 0)
cur = cur.right;
else
return false;
}
if (p.e.compareTo(e) > 0)
p.left = new Node<>(null,e,null);
else
p.right = new Node<>(null,e,null);
size++;
return true;
}
//删除节点
public void remove(E e){
root = remove(root, e);
}
private Node<E> remove(Node root,E e) {
Node<E> resCur = root;
Node<E> cur = root;
Node<E> p = cur;
if (root == null)
throw new NoSuchElementException();
while (cur != null) {
if (e.compareTo(cur.e) < 0) {
p = cur;
cur = cur.left;
} else if (e.compareTo(cur.e) > 0) {
p = cur;
cur = cur.right;
} else { //找到节点,cur为待删除节点,p为cur父节点
if (p == cur) { //此时要删除的为根节点
if (cur.right != null) { //判断根节点有没有右节点
Node<E> newRoot = new Node<E>(null, getMin(cur.right).e,null);
newRoot.left = cur.left;
newRoot.right = removeMin(cur.right);
return newRoot;
}else {
Node<E> newRoot = cur.left;
size--;
return newRoot;
}
}
//待删除节点左右节点都不为空,4种情况
if (cur.right != null && cur.left != null) {
Node<E> newRoot = new Node<E>(null, getMin(cur.right).e,null);
newRoot.left = cur.left;
newRoot.right = removeMin(cur.right);
if (p.e.compareTo(newRoot.e) > 0) {
p.left = newRoot;
}else {
p.right = newRoot;
}
return resCur;
} else if (cur.left != null && cur.right == null) {
if (p.e.compareTo(cur.e) > 0) {
p.left = cur.left;
}else {
p.right = cur.left;
}
} else if (cur.left == null && cur.right != null) {
if (p.e.compareTo(cur.e) > 0) {
p.left = cur.right;
}else {
p.right = cur.right;
}
} else {
if (p.e.compareTo(cur.e) > 0) {
p.left = null;
}else {
p.right = null;
}
}
size--;
return resCur;
}
}
return resCur;
}
public E removeMin() {
E res = getMin(root).e;
root = removeMin(root);
return res;
}
public E removeMax() {
E res = getMax(root).e;
root = removeMax(root);
return res;
}
private Node<E> removeMin(Node<E> cur) {
Node<E> resCur = cur; //临时保存cur的值,将修改后的值返回回去
Node<E> p = cur; //cur的父节点
if (cur == null) {
throw new NoSuchElementException();
} else if (cur.left == null) { //当没有左节点时,最小值就是自己(cur)
Node<E> res = cur.right; //创建节点返回cur的右节点,将自己删除
size--;
return res;
}
while (cur.left != null) {
p = cur;
cur = cur.left;
}
p.left = cur.right;
cur.right = null;
size--;
return resCur;
}
private Node<E> removeMax(Node<E> cur) {
Node<E> resCur = cur;
Node<E> p = cur;
if (cur == null) {
throw new NoSuchElementException();
} else if (cur.right == null) { //当最小值就是自己的时候
Node<E> res = cur.left;
size--;
return res;
}
while (cur.right != null) {
p = cur;
cur = cur.right;
}
p.right = cur.left;
cur.left = null;
size--;
return resCur;
}
private Node<E> getMin(Node cur) {
Node<E> p = cur;
while (cur != null) {
p = cur;
cur = cur.left;
}
return p;
}
private Node<E> getMax(Node cur) {
Node<E> p = cur;
while (cur != null) {
p = cur;
cur = cur.right;
}
return p;
}
public int getSize() {
return size;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
traverse(sb, root);
return sb.toString();
}
public void traverse(StringBuilder sb,Node<E> root) {
if (root == null)
return;
Stack<Node> stack = new Stack<>();
while (root != null || !stack.isEmpty()) {
while (root != null) {
sb.append(root.e+","); //前序遍历
stack.push(root);
root = root.left;
}
root = stack.pop();
//sb.append(root.e+","); //中序遍历
root = root.right;
}
}
}
代码写的有点乱,还有很大的优化空间。