查找指定节点
使用我们的前中后序遍历找到一个指定节点,查找HeroNode = 4 分别查找了几次?
例子还是我们上面讲到的那个二叉树,我们先分析
前序查找:先判断当前的no是不是要查找的,是就返回当前节点,不是的话,判断当前节点的左子节点是否为空,若不为空则递归前序查找,找到返回,为空(或者没有找到)的话就去判断右子树,跟我们的左子节点一样
emmmm,其他两种都已一样的套路,这里就不多说了
前序遍历:小王——>小胡——>小黄——>老肥找到
中序遍历:小胡——>小王——>老肥找到
后序遍历:小胡——>老肥找到
上代码,我用的还是上次的那个二叉树,我直接贴方法的代码,其他的就不补充了
节点中的遍历查找方法
/**
* 前序遍历查找
* @param no 待查找编号
* @return
*/
public HeroNode preOrderSearch(int no){
//判断当前节点是不是
if(this.no == no){
return this;
}
//判断左子节点是否为空
HeroNode resNode = null;//辅助节点
if(this.left != null){
resNode = this.left.preOrderSearch(no);
}
if(resNode != null){
//说明左子树上找到了
return resNode;
}
if(this.right != null){
resNode = this.right.preOrderSearch(no);
}
return resNode;
}
//中序遍历查找
public HeroNode infixOrderSearch(int no){
HeroNode resNode = null;
//先判断当前节点的左子节点
if(this.left != null){
resNode = this.left.infixOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.no == no){
return this;
}
if(this.right != null){
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
//后序遍历查找
public HeroNode postOrderSearch(int no){
HeroNode resNode = null;
if(this.left != null){
resNode = this.left.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.right != null){
resNode = this.right.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.no == no)
return this;
return resNode;
}
树中的遍历查找方法
//前序遍历查找
public HeroNode preOrderSearch(int no){
if(root != null){
return root.preOrderSearch(no);
}else{
return null;
}
}
//中序遍历查找
public HeroNode infixOrderSearch(int no){
if(root != null){
return root.infixOrderSearch(no);
}else{
return null;
}
}
//后续序遍历查找
public HeroNode postOrderSearch(int no){
if(root != null){
return root.postOrderSearch(no);
}else{
return null;
}
}
测试
//测试查找
System.out.println("-----------------------------");
System.out.println("测试前序遍历查找4号节点");
System.out.println(binnaryTreeDemo.preOrderSearch(4));
System.out.println("-----------------------------");
System.out.println("测试中序遍历查找4号节点");
System.out.println(binnaryTreeDemo.infixOrderSearch(4));
System.out.println("-----------------------------");
System.out.println("测试后序遍历查找4号节点");
System.out.println(binnaryTreeDemo.postOrderSearch(4));
-----------------------------
测试前序遍历查找4号节点
HeroNode [no=4, name=老肥]
-----------------------------
测试中序遍历查找4号节点
HeroNode [no=4, name=老肥]
-----------------------------
测试后序遍历查找4号节点
HeroNode [no=4, name=老肥]
删除节点
我们需要分情况
1.删除叶子结点的话,我们直接删除就OK了
2.如果删除的节点是非叶子节点,则要删除这个子树
那我们还用刚才的那个树
我现在添加一个节点,让我们的树变成这个样子
我们删除油哥这个节点和小黄这个节点
我们按照不同的情况进行不同的操作
1.删除油哥,他是不是叶子结点呢?是的话就单独删除这一个节点
因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点
如果当前节点的左子节点不为空,并且左子节点就是需要删除的节点,也就是说要删除油哥,我们要找到小黄,判断小黄的左子节点是不是需要删除的节点,是的话就将this.left = null;没有找到就去当前节点的右子节点继续判断
都不是就递归左子树,再去递归右子树
如果树是空树root为空,或者只有一个root节点,则等价将二叉树置空
首选是节点中的代码
//递归删除节点
//1.删除叶子结点的话,我们直接删除就OK了
//2.如果删除的节点是非叶子节点,则要删除这个子树
public void deleteHeroNode(int no){
//思路如果当前节点的左子节点不为空,
//并且左子节点就是需要删除的节点,也就是说要删除油哥,
//我们要找到小黄,判断小黄的左子节点是不是需要删除的节点,
//是的话就将this.left = null;没有找到就去当前节点的右子节点继续判断
//都不是就递归左子树,再去递归右子树
if(this.left != null && this.left.no == no){
this.left = null;
return;
}
if(this.right != null && this.right.no == no){
this.right = null;
return;
}
//向左子树递归
if(this.left != null){
this.left.deleteHeroNode(no);
}
//向右子树递归删除
if(this.right != null){
this.right.deleteHeroNode(no);
}
}
然后是我们树中的操作
/**
* 删除节点操作
* @param no 需要删除的节点
*/
public void deleteHeroNode(int no){
if(root == null){
System.out.println("该二叉树为空树,不能删除");
}else{
//如果root只有一个节点,这里立即判断root是不是要删除的节点
//否则往下一找就没机会回来了
if(root.getNo() == no){
root = null;
}else{
//递归删除
root.deleteHeroNode(no);
}
}
}
测试结果
BinnaryTreeDemo binnaryTreeDemo = new BinnaryTreeDemo();
//创建节点
HeroNode root = new HeroNode(1, "小王");
HeroNode node2 = new HeroNode(2, "小胡");
HeroNode node3 = new HeroNode(3, "小黄");
HeroNode node4 = new HeroNode(4, "老肥");
HeroNode node5 = new HeroNode(5, "油哥");
//手动创建二叉树,后面用递归方式创建二叉树
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binnaryTreeDemo.setRoot(root);
System.out.println("删除前的树,前序遍历为");
binnaryTreeDemo.preOrder();
System.out.println("开始删除油哥");
binnaryTreeDemo.deleteHeroNode(5);
System.out.println("删除后的树,前序遍历为");
binnaryTreeDemo.preOrder();
删除前的树,前序遍历为
HeroNode [no=1, name=小王]
HeroNode [no=2, name=小胡]
HeroNode [no=3, name=小黄]
HeroNode [no=5, name=油哥]
HeroNode [no=4, name=老肥]
开始删除油哥
删除后的树,前序遍历为
HeroNode [no=1, name=小王]
HeroNode [no=2, name=小胡]
HeroNode [no=3, name=小黄]
HeroNode [no=4, name=老肥]
显示正确,那要是我们现在删除的是小黄呢?小黄下面还有一个右子节点,能否一起删除呢?
删除小黄后,树只剩下了小王和小胡,我们试试
System.out.println("开始删除小黄");
binnaryTreeDemo.deleteHeroNode(3);
System.out.println("删除后的树,前序遍历为");
binnaryTreeDemo.preOrder();
开始删除小黄
删除后的树,前序遍历为
HeroNode [no=1, name=小王]
HeroNode [no=2, name=小胡]
正确
那么这个到底是怎么走的呢?删除叶子结点很容易理解,但时删除非叶子节点是怎么删除的呢?
我们来分析一波
第一步:判断我们的root节点是不是空,我们的root节点是小王不为空,继续往下走
第二步:直接判断root节点是不是要删除的节点,小王的编号是1不是3,所以继续往下
第三步:进入递归删除—>小王的左子节点为空么?是小胡不为空,那是不是就是我们要删除的节点呢?小胡是2显然不是,那么继续往下走
第四步:小王的右子树为空么?是小黄不为空,那小黄是要删除的节点么,是的,那么进入删除,直接把小黄置空
第五步:递归进入左子树,假如我们删除的是油哥,那么上述的四步都没有找到,第五步,小王的左子树为空么?是小胡不为空,递归,那此时的this就指向了小胡,发现没有子树了,出来
第六步:递归进入右子树,发现右子树是小黄,this变成了小黄,然后以小黄为基准再次开始第一步判断,直到找到删除的节点
那么二叉树的常见操作都写完了,我把最终代码贴一遍
package 树;
//二叉树的前序、中序、后序遍历
//2021年1月25日22:57:42
//@author 王
public class BinnaryTree {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建一颗二叉树
BinnaryTreeDemo binnaryTreeDemo = new BinnaryTreeDemo();
//创建节点
HeroNode root = new HeroNode(1, "小王");
HeroNode node2 = new HeroNode(2, "小胡");
HeroNode node3 = new HeroNode(3, "小黄");
HeroNode node4 = new HeroNode(4, "老肥");
HeroNode node5 = new HeroNode(5, "油哥");
//手动创建二叉树,后面用递归方式创建二叉树
root.setLeft(node2);
root.setRight(node3);
node3.setRight(node4);
node3.setLeft(node5);
binnaryTreeDemo.setRoot(root);
//测试
// System.out.println("前序遍历");
// binnaryTreeDemo.preOrder();
// System.out.println("中序遍历");
// binnaryTreeDemo.infixOrder();
// System.out.println("后序遍历");
// binnaryTreeDemo.postOrder();
//
// //测试查找
// System.out.println("-----------------------------");
// System.out.println("测试前序遍历查找4号节点");
// System.out.println(binnaryTreeDemo.preOrderSearch(4));
// System.out.println("-----------------------------");
// System.out.println("测试中序遍历查找4号节点");
// System.out.println(binnaryTreeDemo.infixOrderSearch(4));
// System.out.println("-----------------------------");
// System.out.println("测试后序遍历查找4号节点");
// System.out.println(binnaryTreeDemo.postOrderSearch(4));
System.out.println("删除前的树,前序遍历为");
binnaryTreeDemo.preOrder();
System.out.println("开始删除油哥");
binnaryTreeDemo.deleteHeroNode(5);
System.out.println("删除后的树,前序遍历为");
binnaryTreeDemo.preOrder();
System.out.println("开始删除小黄");
binnaryTreeDemo.deleteHeroNode(3);
System.out.println("删除后的树,前序遍历为");
binnaryTreeDemo.preOrder();
}
}
//定义一个BinnaryTree二叉树
class BinnaryTreeDemo{
private HeroNode root;
public void setRoot(HeroNode root) {
this.root = root;
}
//前序遍历
public void preOrder(){
if(this.root != null){
this.root.preOrder();
}else{
System.out.println("二叉树为空,无法遍历");
}
}
//中序遍历
public void infixOrder(){
if(this.root != null){
this.root.infixOrder();
}else{
System.out.println("二叉树为空,无法遍历");
}
}
//后序遍历
public void postOrder(){
if(this.root != null){
this.root.postOrder();
}else{
System.out.println("二叉树为空,无法遍历");
}
}
//前序遍历查找
public HeroNode preOrderSearch(int no){
if(root != null){
return root.preOrderSearch(no);
}else{
return null;
}
}
//中序遍历查找
public HeroNode infixOrderSearch(int no){
if(root != null){
return root.infixOrderSearch(no);
}else{
return null;
}
}
//后续序遍历查找
public HeroNode postOrderSearch(int no){
if(root != null){
return root.postOrderSearch(no);
}else{
return null;
}
}
/**
* 删除节点操作
* @param no 需要删除的节点
*/
public void deleteHeroNode(int no){
if(root == null){
System.out.println("该二叉树为空树,不能删除");
}else{
//如果root只有一个节点,这里立即判断root是不是要删除的节点
//否则往下一找就没机会回来了
if(root.getNo() == no){
root = null;
}else{
//递归删除
root.deleteHeroNode(no);
}
}
}
}
//先创建HeroNode节点
class HeroNode{
private int no;
private String name;
private HeroNode left;
private HeroNode right;
public HeroNode(int no, String name) {
super();
this.no = no;
this.name = name;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public HeroNode getLeft() {
return left;
}
public void setLeft(HeroNode left) {
this.left = left;
}
public HeroNode getRight() {
return right;
}
public void setRight(HeroNode right) {
this.right = right;
}
@Override
public String toString() {
return "HeroNode [no=" + no + ", name=" + name + "]";
}
//编写前序遍历方法
public void preOrder(){
System.out.println(this);//先输出父节点
//递归向左子树前序遍历
if(this.left != null){
this.left.preOrder();
}
//递归向右子树前序遍历
if(this.right != null){
this.right.preOrder();
}
}
//中序遍历
public void infixOrder(){
//递归向左子树中序遍历
if(this.left != null){
this.left.infixOrder();
}
System.out.println(this);//输出父节点
//向右递归中序遍历子树
if(this.right != null){
this.right.infixOrder();
}
}
//后序遍历
public void postOrder(){
if(this.left != null){
this.left.postOrder();
}
if(this.right != null){
this.right.postOrder();
}
System.out.println(this);//输出父节点
}
/**
* 前序遍历查找
* @param no 待查找编号
* @return
*/
public HeroNode preOrderSearch(int no){
//判断当前节点是不是
if(this.no == no){
return this;
}
//判断左子节点是否为空
HeroNode resNode = null;
if(this.left != null){
resNode = this.left.preOrderSearch(no);
}
if(resNode != null){
//说明左子树上找到了
return resNode;
}
if(this.right != null){
resNode = this.right.preOrderSearch(no);
}
return resNode;
}
//中序遍历查找
public HeroNode infixOrderSearch(int no){
HeroNode resNode = null;
//先判断当前节点的左子节点
if(this.left != null){
resNode = this.left.infixOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.no == no){
return this;
}
if(this.right != null){
resNode = this.right.infixOrderSearch(no);
}
return resNode;
}
//后序遍历查找
public HeroNode postOrderSearch(int no){
HeroNode resNode = null;
if(this.left != null){
resNode = this.left.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.right != null){
resNode = this.right.postOrderSearch(no);
}
if(resNode != null){
return resNode;
}
if(this.no == no)
return this;
return resNode;
}
//递归删除节点
//1.删除叶子结点的话,我们直接删除就OK了
//2.如果删除的节点是非叶子节点,则要删除这个子树
public void deleteHeroNode(int no){
//思路如果当前节点的左子节点不为空,
//并且左子节点就是需要删除的节点,也就是说要删除油哥,
//我们要找到小黄,判断小黄的左子节点是不是需要删除的节点,
//是的话就将this.left = null;没有找到就去当前节点的右子节点继续判断
//都不是就递归左子树,再去递归右子树
if(this.left != null && this.left.no == no){
this.left = null;
return;
}
if(this.right != null && this.right.no == no){
this.right = null;
return;
}
//向左子树递归
if(this.left != null){
this.left.deleteHeroNode(no);
}
//向右子树递归删除
if(this.right != null){
this.right.deleteHeroNode(no);
}
}
}