我想尝试将自己的BST作为练习来实现,但我仍然坚持删除节点。我无法弄清楚为什么它在某些情况下不起作用。我从一本书中获取算法,但是当我用随机元素测试它时,有些情况下它不会删除元素,甚至会弄乱它们的顺序,因此它们不再被排序。我做错了什么以及什么是更好的方法来实现这个目标?
注意:方法中的所有println()语句仅用于调试目的
class TreeNode<T extends Comparable<T>> {
T data;
TreeNode<T> left;
TreeNode<T> right;
TreeNode<T> parent;
TreeNode(T data) {
this.data = data;
}
boolean hasBothChildren() {
return hasLeftChild() && hasRightChild();
}
boolean hasChildren() {
return hasLeftChild() || hasRightChild();
}
boolean hasLeftChild() {
return left != null;
}
boolean hasRightChild() {
return right != null;
}
boolean isLeftChild() {
return this.parent.left == this;
}
boolean isRightChild() {
return this.parent.right == this;
}
}
public class BinaryTreeSet<T extends Comparable<T>> {
private TreeNode<T> root;
private void makeRoot(T element) {
TreeNode<T> node = new TreeNode<T>(element);
root = node;
}
private TreeNode<T> find(T element) {
TreeNode<T> marker = root;
TreeNode<T> found = null;
while (found == null && marker != null) {
int comparator = (marker.data.compareTo(element));
if (comparator > 0)
marker = marker.left;
else if (comparator < 0)
marker = marker.right;
else
found = marker;
}
return found;
}
private TreeNode<T> max(TreeNode<T> root) {
TreeNode<T> currentMax = root;
while (currentMax.hasRightChild()) {
currentMax = currentMax.right;
}
return currentMax;
}
// returns the inorder predecessor of node
private TreeNode<T> predecessor(TreeNode<T> node) {
return max(node.left);
}
// removes a given node with 0 or 1 children
private void removeNode(TreeNode<T> node) {
if (!node.hasChildren()) {
System.out.println("node with no children");
if (node.isLeftChild())
node.parent.left = null;
else
node.parent.right = null;
}
else {
System.out.println("node with 1 child");
if (node.isRightChild()) {
if (node.hasLeftChild())
node.parent.right = node.left;
else if (node.hasRightChild())
node.parent.right = node.right;
}
else if (node.isLeftChild()) {
if (node.hasLeftChild())
node.parent.left = node.left;
else if (node.hasRightChild())
node.parent.left = node.right;
}
}
node = null;
}
public BinaryTreeSet() {
root = null;
}
public void addElement(T element) {
if (root == null)
makeRoot(element);
else {
TreeNode<T> marker = root;
TreeNode<T> node = new TreeNode<T>(element);
boolean done = false;
while(!done) {
int comparator = marker.data.compareTo(element);
if (comparator > 0) {
if (marker.hasLeftChild())
marker = marker.left;
else {
marker.left = node;
done = true;
}
}
else if (comparator < 0) {
if (marker.hasRightChild())
marker = marker.right;
else {
marker.right = node;
done = true;
}
}
else
return;
node.parent = marker;
}
}
}
public boolean contains(T element) {
boolean found = (find(element) == null)? false : true;
return found;
}
public boolean removeElement(T element) {
TreeNode<T> node = find(element);
if (node == null)
return false;
// removal of a node with no children
if (!node.hasChildren()) {
if (node.isLeftChild()) {
node.parent.left = null;
}
else if (node.isRightChild()) {
node.parent.right = null;
}
}
// removal of a node with both children
else if (node.hasBothChildren()) {
TreeNode<T> pred = predecessor(node);
T temp = pred.data;
pred.data = node.data;
node.data = temp;
removeNode(pred);
}
// removal of a node with only 1 child
else {
if (node.isRightChild()) {
if (node.hasLeftChild())
node.parent.right = node.left;
else if (node.hasRightChild())
node.parent.right = node.right;
}
else if (node.isLeftChild()) {
if (node.hasLeftChild())
node.parent.left = node.left;
else if (node.hasRightChild())
node.parent.left = node.right;
}
}
node = null;
System.out.println("item removed: " + !contains(element));
return true;
}
}
答案 0 :(得分:1)
发现导致我头痛的错误。问题是在我删除0或1个子节点的情况下。我没有更新他们的父节点,所以搞砸了代码。所以,而不是,例如
if (node.hasLeftChild())
node.parent.right = node.left;
我应该写
if (node.hasLeftChild()) {
node.parent.left = node.left;
node.left.parent = node.parent;
}
在我处理单个孩子的所有情况下。另外,当root是removeElement()
函数的目标时,我忘了更新根。
但是,就目前而言,我觉得我在代码中有很多重复。我还没有想出更优雅的解决方案。
编辑:还有其他小错误,主要在isRightChild()
和isLeftChild()
函数中,如果相关节点没有父级,则会生成NullPointerException
。
答案 1 :(得分:1)
请将以下方法添加到BinaryTreeSet类中并调用它, 这将显示带有左/右前缀的当前元素列表。
boolean rootOncePrint = true;
public void printAllTree(TreeNode<T> startNode){
if(startNode == null) return;
//System.out.println(startNode.data);
if(rootOncePrint){
System.out.println("Root : " + startNode.data);
rootOncePrint = false;
}
if(startNode.hasChildren()){
if(startNode.hasLeftChild()){
printAllTree(startNode.left);
}
if(startNode.hasRightChild()){
printAllTree(startNode.right);
}
}
if(startNode != root){
T parentValue = startNode.parent.data;
T dataValue = startNode.data;
System.out.println(startNode.data + ((parentValue.compareTo(dataValue) > 0)?"L":"R"));
}
}
添加此代码后,尝试在BinaryTreeSet中添加/删除元素 你会知道发生了什么。