上一篇主要整理了遍历。删除的时候是复杂的。因为考虑的情况比较多。
网上这篇例子写的很好,我就转过了。
https://blog.csdn.net/fengrunche/article/details/52305748
1)删除节点为叶子节点
2)删除节点只有一个子节点:只有一个左子节点和只有一个右子节点
3)删除节点有两个子节点:这种情况比较复杂,需要寻找后继节点,即比要删除的节点的关键值次高的节点是它的后继节点。说得简单一些,后继节点就是比要删除的节点的关键值要大的节点集合中的最小值。(右子节点的左后代)
得到后继节点的代码如下:
/**
*
* 得到后继节点,即删除节点的左后代
*/
private Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = null;
Node current = delNode.rightChild;
while (current != null) {
successorParent = successor;
successor = current;
current = current.leftChild;
}
//如果后继节点不是删除节点的右子节点时,
if (successor != delNode.rightChild) {
//要将后继节点的右子节点指向后继结点父节点的左子节点,
successorParent.leftChild = successor.rightChild;
//并将删除节点的右子节点指向后继结点的右子节点
successor.rightChild = delNode.rightChild;
}
//任何情况下,都需要将删除节点的左子节点指向后继节点的左子节点
successor.leftChild = delNode.leftChild;
return successor;
}
a)如果后继节点是刚好是要删除节点的右子节点
//删除的节点为父节点的左子节点时:
parent.leftChild = successor;
successor.leftChild = delNode.leftChild;
//删除的节点为父节点的右子节点时:
parent.rightChild = successor;
successor.leftChild = delNode.leftChild
b)如果后继节点为要删除节点的右子节点的左后代:
上面图中50, 888 分别为successorparent,
//删除的节点为父节点的左子节点时:
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
parent.leftChild = successor;
successor.leftChild = delNode.leftChild;
//删除的节点为父节点的右子节点时:
successorParent.leftChild = successor.rightChild;
successor.rightChild = delNode.rightChild;
parent.rightChild = successor;
successor.leftChild = delNode.leftChild;
删除的代码如下,作者做了抽取公共流程。终点是复杂子节点的处理。结合着图比较好理解。
package com.daojia.collect;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
public class Btree {
private Node root = null;
Btree(int value) {
root = new Node(value);
root.leftChild = null;
root.rightChild = null;
}
//查找
public Node findKey(int value) {
Node current = root ;
while(true){
if(current.value== value)
{
return current;
}
else if(current.value>value){
current = current.leftChild;
}else if(current.value < value){
current = current.rightChild;
}
if(current == null){
return null;
}
}
}
//插入
public Node insert(int value) {
String error = null;
Node node = new Node(value);
if (root == null) {
root = node;
root.leftChild = null;
root.rightChild = null;
} else {
Node current = root;
Node parent = null;
while (true) {
if (value < current.value) {
parent = current;
current = current.leftChild;
if (current == null) {
parent.leftChild = node;
break;
}
} else if (value > current.value) {
parent = current;
current = current.rightChild;
if (current == null) {
parent.rightChild = node;
break;
}
} else {
System.out.println("exists");
break;
}
} // end of while
}
return node;
}
//中序遍历递归操作
public void inOrderTraverse() {
System.out.print("中序递归:");
inOrderTraverse(root);
System.out.println();
}
/**
* (递归)先遍历左子树,在跟节点,再遍历右子树
* @param node
*/
private void inOrderTraverse(Node node) {
if (node == null)
return ;
inOrderTraverse(node.leftChild);
node.display();
inOrderTraverse(node.rightChild);
}
/**
* 非递归遍历
* 1)对于任意节点current,若该节点不为空则将该节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,栈顶节点出栈,访问节点后将该节点的右子树置为current
* 3) 重复1、2步操作,直到current为空且栈内节点为空。
*/
public ArrayList<Integer> inOrderByStack() {
System.out.print("中序非递归:");
ArrayList<Integer> list = new ArrayList<Integer>();
Stack<Node> stack = new Stack<Node>();
Node current = root;
while(current != null ||!stack.isEmpty())
{
while(current != null){
stack.push(current);
current = current.leftChild;
}
if(!stack.isEmpty())
{
current =stack.pop();
list.add(current.value);
current = current.rightChild;
}
}
return list;
}
//前序遍历
public void preOrderTraverse() {
System.out.print("前序遍历:");
preOrderTraverse(root);
System.out.println("");
}
private void preOrderTraverse(Node node) {
if (node == null)
return ;
node.display();
preOrderTraverse(node.leftChild);
preOrderTraverse(node.rightChild);
}
//前序遍历非递归操作
public ArrayList<Integer> preOrderByStack() {
System.out.print("前序遍历 非递归:");
ArrayList<Integer> list = new ArrayList<Integer>();
Stack<Node> stack = new Stack<Node>();
Node current = root;
while(current != null || !stack.isEmpty())
{
while(current != null){
stack.push(current);
list.add(current.value);
current = current.leftChild;
}
if( !stack.isEmpty()){
current = stack.pop();
current = current.rightChild;
}
}
return list;
}
//后序遍历
public void postOrderTraverse() {
System.out.print("后序遍历:");
postOrderTraverse(root);
System.out.println("");
}
private void postOrderTraverse(Node node){
if(node == null){
return;
}
postOrderTraverse(node.leftChild);
postOrderTraverse(node.rightChild);
node.display();
}
/**
* 1)对于任意节点current,若该节点不为空则访问该节点后再将节点压栈,并将左子树节点置为current,重复此操作,直到current为空。
* 2)若左子树为空,取栈顶节点的右子树,如果右子树为空或右子树刚访问过,则访问该节点,并将preNode置为该节点
*/
public ArrayList<Integer> postOrderByStack() {
System.out.print("后序遍历 非递归:");
ArrayList<Integer> list = new ArrayList<Integer>();
Stack<Node> stack = new Stack<Node>();
Node current = root;
Node preNode = null;
while(current != null || !stack.isEmpty())
{
while(current != null){
stack.push(current);
current = current.leftChild;
}
if( !stack.isEmpty()){
current = stack.peek().rightChild;
if (current == null || current == preNode) {
current = stack.pop();
list.add(current.value);
preNode = current;
current = null;
}
}
}
return list;
}
//后序遍历非递归操作
public ArrayList<Integer> postOrderByStack2() {
System.out.print("后序遍历 非递归:");
ArrayList<Integer> list = new ArrayList<Integer>();
Stack<Node> stack = new Stack<Node>();
Node current = null;
Node preNode = null;
stack.push(root);
while(!stack.isEmpty())
{
current = stack.peek();
//当前节点没有孩子节点或者左右孩子节点都被访问过
if( (current.leftChild == null&¤t.rightChild== null) ||
(current!= null&&(current.leftChild == preNode|| current.rightChild== preNode)) ) {
stack.pop();
list.add(current.value);
preNode = current;
}else{
if(current.rightChild != null ){
stack.push(current.rightChild);
}
if(current.leftChild != null ){
stack.push(current.leftChild);
}
}
}
return list;
}
/**
* 广度优先遍历
* @return
*/
public ArrayList<Integer> levelTraverse(){
LinkedList<Node> queue = new LinkedList<Node>();
ArrayList<Integer> list = new ArrayList<Integer>();
System.out.print("广度优先遍历 :");
queue.add(root);
while(!queue.isEmpty()){
Node current = queue.poll();
list.add(current.value);
if(current.leftChild != null){
queue.add(current.leftChild);
}
if(current.rightChild != null){
queue.add(current.rightChild);
}
}
return list;
}
/**
* 得到最小值
* @return
*/
public int getMinValue() {
System.out.print("最小值:");
Node current = root;
while(true){
if(current.leftChild == null)
{
return current.value;
}
current = current.leftChild;
}
}
//得到最大值
public int getMaxValue(){
System.out.print("最大值:");
Node current = root;
while(true){
if(current.rightChild == null)
{
return current.value;
}
current = current.rightChild;
}
}
/**
*
* 得到后继节点,即删除节点的左后代
*/
private Node getSuccessor(Node delNode) {
Node successor = delNode;
Node successorParent = null;
Node current = delNode.rightChild;
while (current != null) {
successorParent = successor;
successor = current;
current = current.leftChild;
}
//如果后继节点不是删除节点的右子节点时,
if (successor != delNode.rightChild) {
//要将后继节点的右子节点指向后继结点父节点的左子节点,
successorParent.leftChild = successor.rightChild;
//并将删除节点的右子节点指向后继结点的右子节点
successor.rightChild = delNode.rightChild;
}
//任何情况下,都需要将删除节点的左子节点指向后继节点的左子节点
successor.leftChild = delNode.leftChild;
return successor;
}
//删除节点
public boolean delete(int value) {
Node current = root; //需要删除的节点
Node parent = null; //需要删除的节点的父节点
boolean isLeftChild = true; //需要删除的节点是否父节点的左子树
while (true) {
if (value == current.value) {
break;
} else if (value < current.value) {
isLeftChild = true;
parent = current;
current = current.leftChild;
} else {
isLeftChild = false;
parent = current;
current = current.rightChild;
}
//找不到需要删除的节点,直接返回
if (current == null)
return false;
}
//分情况考虑
//1、需要删除的节点为叶子节点
if (current.leftChild == null && current.rightChild == null) {
//如果该叶节点为根节点,将根节点置为null
if (current == root) {
root = null;
} else {
//如果该叶节点是父节点的左子节点,将父节点的左子节点置为null
if (isLeftChild) {
parent.leftChild = null;
} else { //如果该叶节点是父节点的右子节点,将父节点的右子节点置为null
parent.rightChild = null;
}
}
}
//2、需要删除的节点有一个子节点,且该子节点为左子节点
else if (current.rightChild == null) {
//如果该节点为根节点,将根节点的左子节点变为根节点
if (current == root) {
root = current.leftChild;
} else {
//如果该节点是父节点的左子节点,将该节点的左子节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = current.leftChild;
} else { //如果该节点是父节点的右子节点,将该节点的左子节点变为父节点的右子节点
parent.rightChild = current.leftChild;
}
}
}
//2、需要删除的节点有一个子节点,且该子节点为右子节点
else if (current.leftChild == null) {
//如果该节点为根节点,将根节点的右子节点变为根节点
if (current == root) {
root = current.rightChild;
} else {
//如果该节点是父节点的左子节点,将该节点的右子节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = current.rightChild;
} else { //如果该节点是父节点的右子节点,将该节点的右子节点变为父节点的右子节点
parent.rightChild = current.rightChild;
}
}
}
//3、需要删除的节点有两个子节点,需要寻找该节点的后续节点替代删除节点
else {
Node successor = getSuccessor(current);
//如果该节点为根节点,将后继节点变为根节点,并将根节点的左子节点变为后继节点的左子节点
if (current == root) {
root = successor;
} else {
//如果该节点是父节点的左子节点,将该节点的后继节点变为父节点的左子节点
if (isLeftChild) {
parent.leftChild = successor;
} else { //如果该节点是父节点的右子节点,将该节点的后继节点变为父节点的右子节点
parent.rightChild = successor;
}
}
}
current = null;
return true;
}
public static void main(String[] args){
Btree bt = new Btree(52);
bt.insert(580);
bt.insert(12);
bt.insert(50);
bt.insert(58);
bt.insert(9);
bt.insert(888);
bt.insert(248);
bt.insert(32);
bt.insert(666);
bt.insert(455);
bt.insert(777);
bt.insert(999);
bt.postOrderTraverse();
bt.inOrderTraverse();
// bt.delete(32); //删除叶子节点
// bt.delete(50); //删除只有一个左子节点的节点
// bt.delete(248); //删除只有一个右子节点的节点
bt.delete(580); //删除有两个子节点的节点,且后继节点为删除节点的右子节点的左后代
// bt.delete(888); //删除有两个子节点的节点,且后继节点为删除节点的右子节点
// bt.delete(52); //删除有两个子节点的节点,且删除节点为根节点
bt.inOrderTraverse();
}
}
中序递归:9,12,32,50,52,58,248,455,580,666,777,888,999,
中序递归:9,12,32,50,52,58,248,455,666,777,888,999,
先对比图理解。