使用java代码实现二叉树,查找节点,插入一个节点,遍历树,查找最大值和最小值,删除节点
用Java代码表示树
下面来看看如何用Java语言实现二叉树。像其他数据结构一样,有很多方法可以在计算机内存中表示树。最常用的方法是把节点存在无关联的存储器中,而通过每个节点中指向自己子节点的引用来连接。
1.我们要定义Node类
首先,需要有一个节点对象的类。这些对象包含数据,数据代表要存储的内容(例如,在员工数据库中的员工记录),而且还有指向节点的两个子节点的引用。下面就是这个类的描述语句:
public class BinaryTree {
Node root;//定义一个Node对象来存放树的根的变量
/**
* 定义节点的内部类
*/
class Node {
int keyData;//用作键值的数据
Object data;//定义存储数据的变量
Node left;//左子树
Node right; //右子树
/**
* 构造器用来初始化keydata 和data
*
* @param keyData
* @param data
*/
public Node(int keyData, Object data) {
this.keyData = keyData;
this.data = data;
}
}
}
2.查找节点
这个过程我们设置一个变量current 来保存正在查看的节点。参数key是要找的值。查找从根开始。(它只能这样做,因为只有根节点可以直接访问。)因此,开始把current设为根。
之后,在while循环中,将要查找的值,key与当前节点的keyData字段的值(关键字字段)做比较。如果key小于这个数据域的值,current 设为此节点的左子节点。如果key大于(或等于)节点keyData字段的值,current设为此节点的右子节点。
找不到节点
如果current等于null,在查找序列中找不到下一个子节点;到达序列的末端而没有找到要找的节点,表明了它不存在。返回null来指出这个情况。
找到节点
如果while循环的条件不满足,从循环的末端退出来,current 的keyData字段和key相等:这就是说找到该节点了。返回这个节点,调用find()方法的程序可以访问节点的任何字段。
/**
* 查询树中的某个节点
*
* @param keyData
* @return
*/
public Node findNode(int keyData) {
//定义当前节点,并且当前节点从根节点开始
Node current = root;
while (keyData != current.keyData) {//循环遍历查找,直到keyData==current.keyData
if (keyData < current.keyData) {//如果keyData < current.keyData,往左子树遍历
//让循环进入左子树
current = current.left;
} else {//往右子树遍历
current = current.right;//让循环进入右子树
}
if (current == null) {//如果遍历到最后节点了,传入的keyData没有和树的所有节点匹配就返回null
return null;
}
}
//如果跳出了循环,表示已经找到了相匹配的节点了;
return current;//把当前节点返回去
}
3. 插入一个节点的Java代码
insert()方法从创建新节点开始,用提供的数据作为参数。
下一 步,insert()必 须先确定新节点插入的位置。这段代码与查找节点的代码大致相同, “查找一个节点的Java代码”这节中已经介绍过。区别是原来简单的查找节点时,遇到null (不存在)值后,表明要找的节点不存在,于是就立即返回了。现在要插入一个节点,就在返回前插入(要是必要的话,要先创建)节点。
要找的值是从参数keyData传入的。while 循环用true作为它的条件,
插入节点的位置总会找到的(除非存储器溢出);找到后,新节点被接到树上,while循环从retum处跳出。
注意:我这里没有实现key一样的情况,如果需要,可以限制其一样,或者把一样的当作大的数据处理,我这里的实现只有不一样的情况
/**
* 定义往树插入节点的方法
*
* @param keyData 用于判断插入的节点是左子树还是右子树,或者查询的作用
* @param data 存放的数据
*/
public void insert(int keyData, Object data) {
//创建新的节点
Node newNode = new Node(keyData, data);
if (root == null) {//如果插入的节点是第一个节点
//直接把该节点作为树的根节点
root = newNode;
} else {
Node current = root;//定义当前节点,从根节点开始
Node parent; //定义一个节点用户后面的插入
while (true) {//循环插入
parent = current; //赋值为当前节点,如果当前节点往下移动,就是parent节点的子节点了
if (keyData < current.keyData) {//如果是true就插入左子树
current = current.left;//插入进左子树
if (current == null) {//如果本来当前节点也没有左子树(是null)的时候
parent.left = newNode;//新节点插入当前节点的父节点的左子树
return;
}
} else {//插入右子树
current = current.right;//插入进右子树
if (current == null) {
parent.right = newNode;//新节点插入当前节点的父节点的右子树
return;
}
}
}
}
}
中序遍历
中序遍历二叉搜索树会使所有的节点按关键字值升序被访问到。如果希望在二叉树中创建有序的数据序列,这是一种方法。
遍历树的最简单方法是用递归的方法(第6章中介绍过)。用递归的方法遍历整棵树要用一个节点作为参数。初始化时这个节点是根。这个方法只需要做三件事:
1.调用自身来遍历节点的左子树。
2.访问这个节点。
3.调用自身来遍历节点的右子树。
记住访问一个节点意味着对这个节点做某种操作:显示节点,把节点写入文件,或其他别的操作。
遍历可以应用于任何二叉树,而不只是二叉搜索树。这个遍历的原理不关心节点的关键字值;它只是看这个节点是否有子节点。
前序遍历,后续遍历和中序遍历一样,只不过改了两句代码的顺序位置
前序遍历:
1.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
2.调用自身来遍历节点的左子树节点
3.调用自身遍历节点的右子树节点
后序遍历:
1.调用自身来遍历节点的左子树节点
2.调用自身遍历节点的右子树节点
3.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
java代码
/**
* 中序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.调用自身来遍历节点的左子树节点
* 2.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
* 3.调用自身遍历节点的右子树节点
*
* @param localNode (开始传入的是根节点)
*/
public void InOrder(Node localNode) {
if (localNode != null) {
this.InOrder(localNode.left);//递归遍历树的左子树节点
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
this.InOrder(localNode.right);//递归遍历树的右子树节点
}
}
/**
* 前序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
* 2.调用自身来遍历节点的左子树节点
* 3.调用自身遍历节点的右子树节点
*
* @param localNode (开始传入的是根节点)
*/
public void preorderTraversal(Node localNode) {
if (localNode != null) {
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
this.preorderTraversal(localNode.left);//递归遍历树的左子树节点
this.preorderTraversal(localNode.right);//递归遍历树的右子树节点
}
}
/**
* 后序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.调用自身来遍历节点的左子树节点
* 2.调用自身遍历节点的右子树节点
* 3.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
*
* @param localNode (开始传入的是根节点)
*/
public void postorderTraversal(Node localNode) {
if (localNode != null) {
this.postorderTraversal(localNode.left);//递归遍历树的左子树节点
this.postorderTraversal(localNode.right);//递归遍历树的右子树节点
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
}
}
查找最大值和最小值
要找最小值时,先走到根的左子节点处,然后接着走到那个子节点的左子节点,如此类推,直到找到一个没有左子节点的节点。这个节点就是最小值的节点。
按照相同的步骤来查找树中的最大值,不过要找到右子节点,一直向右找到没有右子节点的节点。这个节点就是最大值的节点。
/**
* 其实获得最小值就是获得树的树的最左侧的的叶子节点
*
* @return 返回最小值的节点
*/
public Node getMinNode() {
Node current = root;//定义当前节点,从根节点开始
Node last = null;//用于临时存储节点的
while (current != null) {//如果当前节点不为null就一致循环下去
last = current;//临时存储当前的节点
current = current.left;//当前节点往下走
}
return last;
}
/**
* 其实获得最小值就是获得树的树的最左侧的的叶子节点
*
* @return 返回最大值的节点
*/
public Node getMaxNode() {
Node current = root;//定义当前节点,从根节点开始
Node last = null;//用于临时存储节点的
while (current != null) {//如果当前节点不为null就一致循环下去
last = current;//临时存储当前的节点
current = current.right;//当前节点往下走
}
return last;
}
删除节点
删除节点是二叉搜索树常用的一般操作中最复杂的。但是,删除节点在很多树的应用中又非常重要,所以要详细研究并总结特点。
删除节点要从查找要删的节点开始入手,方法与前面介绍的find()和insert()相同。找到节点后,这个要删除的节点可能会有三种情况需要考虑:
1.该节点是叶节点(没有子节点)。2. 该节点有一个子节点。3.该节点有两个子节点。
情况1:删除没有子节点的节点
要删除叶节点,只需要改变该节点的父节点的对应子字段的值,由指向该节点改为null就可以了。 要删除的节 点仍然存在,但它已经不是树的一部分 了。如图上图中的 0012等节点
情况2:删除有一个子节点的节点
这第二种情况也不是很难。这个节点只有两个连接:连向父节点的和连向它惟一的子节点的。需要从这个序列中“剪断”这个节点,把它的子节点直接连到它的父节点上。这个过程要求改变父节点适当的引用(左子节点还是右子节点),指向要删除节点的子节点,如 0030节点。
情况3:删除有两个子节点的节点
首先,程序找到初始节点的右子节点,它的关键字值一定比初始节点大。然后转到初始节点的右子节点的左子节点那里(如果有的话),然后到这个左子节点的左子节点,以此类推,顺着左子节点的路径一直向下找。这个路径上的最后一个左子节点就是初始节点的后继。
为什么可以用这个算法呢?这里实际上是要找比初始节点关键值大的节点集合中最小的一个节点。当找到初始节点的右子节点时,这个以右子节点为根的子树的所有节点都比初始节点的关键字值大,因为这是二叉搜索树所定义的。现在要找到这棵子树中值最小的节点。本书已经讲过,找子树的最小值应该顺着所有左子节点的路径找下去。因此,这个算法可以找到比初始节点大的最小的节点:它就是要找的后继。如果初始节点的右子节点没有左子节点,那么这个右子节点本身就是后继。
删除的Java代码和寻找后继节点的代码
/**
*
* 当找到了要删除的的节点时,有三种情况
* 1.要删除的节点没有子节点或者叶子节点
* 2.要删除的节点有一个节点
* 3.要删除的节点有两个节点
*
* @param keyData
* @return
*/
public boolean deleteNote(int keyData) {
Node current = root;//定义当前节点,从根节点开始
//定义parent节点,也是从根节点开始,当current节点往下移动,parent就是current的父节点了
Node parent = root;
//定义一个变量存放是否有左子树,如果有就是true,有右子树就是false
boolean isLeftNode = true;
while (keyData != current.keyData) {//直到找到keyData节点,或者是遍历到最后的节点为止
parent = current;//同步parent节点位当前节点
if (keyData < current.keyData) {//如果要删除的节点小于当前节点,就往左子树移动
isLeftNode = true;
current = current.left;//节点移动到左子树
} else {
isLeftNode = false;
current = current.right;
}
if (current == null) {//遍历到最后的节点还没有找到就返回false
return false;
}
}
//第一情况,要删除的节点没有子节点或者叶子节点
if (current.left == null && current.right == null) {
//首先判断该删除的节点是不是根节点,如果是根节点就把root=null
if (current == root) {
root = null;
} else {//当不是根节点的时候,只要把当前节点的父节点对当前节点的引用删除就可以了
if (isLeftNode) {//如果当前节点是它父节点的左节点的时候
//把对于当前节点的引用置为null就可以,jvm的垃圾回收机制会把没有引用的内存对象自动回收的
parent.left = null;
} else {//如果当前节点是它父节点的右节点的时候
//把对于当前节点的引用置为null就可以,jvm的垃圾回收机制会把没有引用的内存对象自动回收的
parent.right = null;
}
}
} else if (current.right == null) {//第二种情况,要删除的节点有一个节点(左节点)
//判断删除的节点是否是根节点
if (current == root) {
root = current.left;//让删除节点(当前节点)作为新的根节点就可以了
} else {//如果不是根节点
if (isLeftNode) {//判断当前节点是其父节点的左子树还是右子树
parent.left = current.left;//把要删除的节点的左子树的引用赋给父节点的左子树的引用
} else {
parent.right = current.left;//把要删除的节点的左子树的引用赋给父节点的右子树的引用
}
}
} else if (current.left == null) {//第二种情况,要删除的节点有一个节点(右节点)
//判断删除的节点是否是根节点
if (current == root) {
root = current.right;//让删除节点(当前节点)作为新的根节点就可以了
} else {//如果不是根节点
if (isLeftNode) {//判断当前节点是其父节点的左子树还是右子树
parent.left = current.right;//把要删除的节点的左子树的引用赋给父节点的左子树的引用
} else {
parent.right = current.right;//把要删除的节点的左子树的引用赋给父节点的右子树的引用
}
}
}else {//删除节点有两个子节点的情况
Node successorNode = this.getSuccessorNode(current);//后的后继节点
if (current == root){//判断删除的节点是否是根节点
root = successorNode;//把后继节点作为根节点
}else {
if (isLeftNode){//判断删除的节点是删除节点父节点的左节点
parent.left = successorNode;//把删除节点的父节点的左子节点的引用指向后继节点
}else {
parent.right = successorNode;//把删除节点的父节点的右子节点的引用指向后继节点
}
}
successorNode.left = current.left;//把要删除节点的左子节点的引用给后继节点的左子节点
}
return true;
}
/**
* 获得删除节点时候有两个节点时候,后继节点去替代被删除的节点
* 说明一下:为什么从删除节点下的右子节点下的左子点...(孙左节点)呢,这是我们定义的
* 也可以从删除节点的左子节点下的右子节点....(孙右节点),只不过是引用的指向不同而已
* @param deleteNode 要删除的节点
* @return 返回后继节点
*/
public Node getSuccessorNode(Node deleteNode){
Node successorNode = deleteNode;//定义后继节点
Node successorNodeParent = deleteNode;//定义后继节点的父节点,后面要使用
Node current = deleteNode.right;//定义当前节点,从要删除节点的右节点开始(如果该节点没有左子节点,该节点就是后继节点)
while (current != null){//当前节点不为null的时候一直循环
successorNodeParent = successorNode;//因为一直循环下去的,所以要同步后继节点父亲节点
successorNode = current;//因为一直循环下去的,所以要同步后继节点
current = current.left;//当前节点往左子点循环下去,直到节点是叶子节点或者是只有右子节点
}
if (successorNode != deleteNode.right){//说明要删除的节点的右子节点还有左节点
successorNodeParent.left = successorNode.right;//把后继节点右子树的引用指向后继节点的父节点
successorNode.right = deleteNode.right;//把要删除的节点的右子节点引用给后继节点,因为后继节点将要替代删除的节点
}
return successorNode;
}
测试代码:
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
binaryTree.insert(50, "君不见,黄河之水天上来,奔流到海不复回。");
binaryTree.insert(25, "君不见,高堂明镜悲白发,朝如青丝暮成雪。");
binaryTree.insert(75, "人生得意须尽欢,莫使金樽空对月。");
binaryTree.insert(12, "天生我材必有用,千金散尽还复来。");
binaryTree.insert(37, "烹羊宰牛且为乐,会须一饮三百杯。");
binaryTree.insert(43, "岑夫子,丹丘生,将进酒,杯莫停。");
binaryTree.insert(30, "与君歌一曲,请君为我倾耳听。");
binaryTree.insert(33, "钟鼓馔玉不足贵,但愿长醉不复醒。");
binaryTree.insert(87, "古来圣贤皆寂寞,惟有饮者留其名。");
binaryTree.insert(93, "陈王昔时宴平乐,斗酒十千恣欢谑。");
binaryTree.insert(97, "主人何为言少钱,径须沽取对君酌。");
binaryTree.insert(100, "五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。");
// Node findNode = binaryTree.findNode(75);
// if (findNode != null) {
// System.out.println(findNode.data);
// } else {
// System.out.println("该节点不存在");
// }
// System.out.println("中序遍历");
// //中序遍历
// binaryTree.InOrder(binaryTree.root);
System.out.println("前序遍历");
// //前序遍历
// binaryTree.preorderTraversal(binaryTree.root);
// System.out.println("后序遍历");
// //后序遍历
// binaryTree.postorderTraversal(binaryTree.root);
// //获得最小值
// Node minNode = binaryTree.getMinNode();
// System.out.println("最小值的keyData:" + minNode.keyData + " 存放的数据是:" + minNode.data);
// Node maxNode = binaryTree.getMaxNode();
// System.out.println("最大值的keyData:" + maxNode.keyData + " 存放的数据是:" + maxNode.data);
boolean result = binaryTree.deleteNote(25);
System.out.println("删除节点的情况:"+result);
binaryTree.InOrder(binaryTree.root);
}
完整代码
package com.cym.datastructure.tree;
/**
* 用java代码实现二叉树
* 查找节点,插入一个节点,遍历树,查找最大值和最小值,删除节点
*/
public class BinaryTree {
Node root;//定义一个Node对象来存放树的根的变量
/**
* 定义节点的内部类
*/
class Node {
int keyData;//用作键值的数据
Object data;//定义存储数据的变量
Node left;//左子树
Node right; //右子树
/**
* 构造器用来初始化keydata 和data
*
* @param keyData
* @param data
*/
public Node(int keyData, Object data) {
this.keyData = keyData;
this.data = data;
}
}
/**
* 定义往树插入节点的方法
*
* @param keyData 用于判断插入的节点是左子树还是右子树,或者查询的作用
* @param data 存放的数据
*/
public void insert(int keyData, Object data) {
//创建新的节点
Node newNode = new Node(keyData, data);
if (root == null) {//如果插入的节点是第一个节点
//直接把该节点作为树的根节点
root = newNode;
} else {
Node current = root;//定义当前节点,从根节点开始
Node parent; //定义一个节点用户后面的插入
while (true) {//循环插入
parent = current; //赋值为当前节点,如果当前节点往下移动,就是parent节点的子节点了
if (keyData < current.keyData) {//如果是true就插入左子树
current = current.left;//插入进左子树
if (current == null) {//如果本来当前节点也没有左子树(是null)的时候
parent.left = newNode;//新节点插入当前节点的父节点的左子树
return;
}
} else {//插入右子树
current = current.right;//插入进右子树
if (current == null) {
parent.right = newNode;//新节点插入当前节点的父节点的右子树
return;
}
}
}
}
}
/**
* 查询树中的某个节点
*
* @param keyData
* @return
*/
public Node findNode(int keyData) {
//定义当前节点,并且当前节点从根节点开始
Node current = root;
while (keyData != current.keyData) {//循环遍历查找,直到keyData==current.keyData
if (keyData < current.keyData) {//如果keyData < current.keyData,往左子树遍历
//让循环进入左子树
current = current.left;
} else {//往右子树遍历
current = current.right;//让循环进入右子树
}
if (current == null) {//如果遍历到最后节点了,传入的keyData没有和树的所有节点匹配就返回null
return null;
}
}
//如果跳出了循环,表示已经找到了相匹配的节点了;
return current;//把当前节点返回去
}
/**
* 中序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.调用自身来遍历节点的左子树节点
* 2.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
* 3.调用自身遍历节点的右子树节点
*
* @param localNode (开始传入的是根节点)
*/
public void InOrder(Node localNode) {
if (localNode != null) {
this.InOrder(localNode.left);//递归遍历树的左子树节点
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
this.InOrder(localNode.right);//递归遍历树的右子树节点
}
}
/**
* 前序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
* 2.调用自身来遍历节点的左子树节点
* 3.调用自身遍历节点的右子树节点
*
* @param localNode (开始传入的是根节点)
*/
public void preorderTraversal(Node localNode) {
if (localNode != null) {
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
this.preorderTraversal(localNode.left);//递归遍历树的左子树节点
this.preorderTraversal(localNode.right);//递归遍历树的右子树节点
}
}
/**
* 后序遍历
* 用递归遍历整棵树,要用一个节点作为参数,初始化的时候这个节点就是根节点
* 1.调用自身来遍历节点的左子树节点
* 2.调用自身遍历节点的右子树节点
* 3.访问这个节点(因为树的每一个节点都可以看成树或者子树的根节点)
*
* @param localNode (开始传入的是根节点)
*/
public void postorderTraversal(Node localNode) {
if (localNode != null) {
this.postorderTraversal(localNode.left);//递归遍历树的左子树节点
this.postorderTraversal(localNode.right);//递归遍历树的右子树节点
System.out.print("节点: " + localNode.keyData + " 存放的数据: " + localNode.data);
System.out.println();
}
}
/**
* 其实获得最小值就是获得树的树的最左侧的的叶子节点
*
* @return 返回最小值的节点
*/
public Node getMinNode() {
Node current = root;//定义当前节点,从根节点开始
Node last = null;//用于临时存储节点的
while (current != null) {//如果当前节点不为null就一致循环下去
last = current;//临时存储当前的节点
current = current.left;//当前节点往下走
}
return last;
}
/**
* 其实获得最小值就是获得树的树的最左侧的的叶子节点
*
* @return 返回最大值的节点
*/
public Node getMaxNode() {
Node current = root;//定义当前节点,从根节点开始
Node last = null;//用于临时存储节点的
while (current != null) {//如果当前节点不为null就一致循环下去
last = current;//临时存储当前的节点
current = current.right;//当前节点往下走
}
return last;
}
/**
*
* 当找到了要删除的的节点时,有三种情况
* 1.要删除的节点没有子节点或者叶子节点
* 2.要删除的节点有一个节点
* 3.要删除的节点有两个节点
*
* @param keyData
* @return
*/
public boolean deleteNote(int keyData) {
Node current = root;//定义当前节点,从根节点开始
//定义parent节点,也是从根节点开始,当current节点往下移动,parent就是current的父节点了
Node parent = root;
//定义一个变量存放是否有左子树,如果有就是true,有右子树就是false
boolean isLeftNode = true;
while (keyData != current.keyData) {//直到找到keyData节点,或者是遍历到最后的节点为止
parent = current;//同步parent节点位当前节点
if (keyData < current.keyData) {//如果要删除的节点小于当前节点,就往左子树移动
isLeftNode = true;
current = current.left;//节点移动到左子树
} else {
isLeftNode = false;
current = current.right;
}
if (current == null) {//遍历到最后的节点还没有找到就返回false
return false;
}
}
//第一情况,要删除的节点没有子节点或者叶子节点
if (current.left == null && current.right == null) {
//首先判断该删除的节点是不是根节点,如果是根节点就把root=null
if (current == root) {
root = null;
} else {//当不是根节点的时候,只要把当前节点的父节点对当前节点的引用删除就可以了
if (isLeftNode) {//如果当前节点是它父节点的左节点的时候
//把对于当前节点的引用置为null就可以,jvm的垃圾回收机制会把没有引用的内存对象自动回收的
parent.left = null;
} else {//如果当前节点是它父节点的右节点的时候
//把对于当前节点的引用置为null就可以,jvm的垃圾回收机制会把没有引用的内存对象自动回收的
parent.right = null;
}
}
} else if (current.right == null) {//第二种情况,要删除的节点有一个节点(左节点)
//判断删除的节点是否是根节点
if (current == root) {
root = current.left;//让删除节点(当前节点)作为新的根节点就可以了
} else {//如果不是根节点
if (isLeftNode) {//判断当前节点是其父节点的左子树还是右子树
parent.left = current.left;//把要删除的节点的左子树的引用赋给父节点的左子树的引用
} else {
parent.right = current.left;//把要删除的节点的左子树的引用赋给父节点的右子树的引用
}
}
} else if (current.left == null) {//第二种情况,要删除的节点有一个节点(右节点)
//判断删除的节点是否是根节点
if (current == root) {
root = current.right;//让删除节点(当前节点)作为新的根节点就可以了
} else {//如果不是根节点
if (isLeftNode) {//判断当前节点是其父节点的左子树还是右子树
parent.left = current.right;//把要删除的节点的左子树的引用赋给父节点的左子树的引用
} else {
parent.right = current.right;//把要删除的节点的左子树的引用赋给父节点的右子树的引用
}
}
}else {//删除节点有两个子节点的情况
Node successorNode = this.getSuccessorNode(current);//后的后继节点
if (current == root){//判断删除的节点是否是根节点
root = successorNode;//把后继节点作为根节点
}else {
if (isLeftNode){//判断删除的节点是删除节点父节点的左节点
parent.left = successorNode;//把删除节点的父节点的左子节点的引用指向后继节点
}else {
parent.right = successorNode;//把删除节点的父节点的右子节点的引用指向后继节点
}
}
successorNode.left = current.left;//把要删除节点的左子节点的引用给后继节点的左子节点
}
return true;
}
/**
* 获得删除节点时候有两个节点时候,后继节点去替代被删除的节点
* 说明一下:为什么从删除节点下的右子节点下的左子点...(孙左节点)呢,这是我们定义的
* 也可以从删除节点的左子节点下的右子节点....(孙右节点),只不过是引用的指向不同而已
* @param deleteNode 要删除的节点
* @return 返回后继节点
*/
public Node getSuccessorNode(Node deleteNode){
Node successorNode = deleteNode;//定义后继节点
Node successorNodeParent = deleteNode;//定义后继节点的父节点,后面要使用
Node current = deleteNode.right;//定义当前节点,从要删除节点的右节点开始(如果该节点没有左子节点,该节点就是后继节点)
while (current != null){//当前节点不为null的时候一直循环
successorNodeParent = successorNode;//因为一直循环下去的,所以要同步后继节点父亲节点
successorNode = current;//因为一直循环下去的,所以要同步后继节点
current = current.left;//当前节点往左子点循环下去,直到节点是叶子节点或者是只有右子节点
}
if (successorNode != deleteNode.right){//说明要删除的节点的右子节点还有左节点
successorNodeParent.left = successorNode.right;//把后继节点右子树的引用指向后继节点的父节点
successorNode.right = deleteNode.right;//把要删除的节点的右子节点引用给后继节点,因为后继节点将要替代删除的节点
}
return successorNode;
}
public static void main(String[] args) {
BinaryTree binaryTree = new BinaryTree();
binaryTree.insert(50, "君不见,黄河之水天上来,奔流到海不复回。");
binaryTree.insert(25, "君不见,高堂明镜悲白发,朝如青丝暮成雪。");
binaryTree.insert(75, "人生得意须尽欢,莫使金樽空对月。");
binaryTree.insert(12, "天生我材必有用,千金散尽还复来。");
binaryTree.insert(37, "烹羊宰牛且为乐,会须一饮三百杯。");
binaryTree.insert(43, "岑夫子,丹丘生,将进酒,杯莫停。");
binaryTree.insert(30, "与君歌一曲,请君为我倾耳听。");
binaryTree.insert(33, "钟鼓馔玉不足贵,但愿长醉不复醒。");
binaryTree.insert(87, "古来圣贤皆寂寞,惟有饮者留其名。");
binaryTree.insert(93, "陈王昔时宴平乐,斗酒十千恣欢谑。");
binaryTree.insert(97, "主人何为言少钱,径须沽取对君酌。");
binaryTree.insert(100, "五花马,千金裘,呼儿将出换美酒,与尔同销万古愁。");
// Node findNode = binaryTree.findNode(75);
// if (findNode != null) {
// System.out.println(findNode.data);
// } else {
// System.out.println("该节点不存在");
// }
// System.out.println("中序遍历");
// //中序遍历
// binaryTree.InOrder(binaryTree.root);
System.out.println("前序遍历");
// //前序遍历
// binaryTree.preorderTraversal(binaryTree.root);
// System.out.println("后序遍历");
// //后序遍历
// binaryTree.postorderTraversal(binaryTree.root);
// //获得最小值
// Node minNode = binaryTree.getMinNode();
// System.out.println("最小值的keyData:" + minNode.keyData + " 存放的数据是:" + minNode.data);
// Node maxNode = binaryTree.getMaxNode();
// System.out.println("最大值的keyData:" + maxNode.keyData + " 存放的数据是:" + maxNode.data);
boolean result = binaryTree.deleteNote(25);
System.out.println("删除节点的情况:"+result);
binaryTree.InOrder(binaryTree.root);
}
}