文章目录
前言
书接上文 , 本篇将介绍树结构中的查找 , 删除 , 顺序存储和线索化
提示:以下是本篇文章正文内容,下面案例可供参考
一、查找(前中后序)
1.前序遍历查找
思路 : 我们需要先判断当前结点是不是要查找的结点,如果是,则直接返回该结点,如果不是,需要分别递归左子树和右子树,知道遍历完成
//前序遍历搜索
public HeroNode preOrderSearch(int no) {
//定义一个结果结点
HeroNode resNode = null;
//因为是前序 , 我们上来就要判断当前结点是否为我们要找的结点
if(no == this.no) {
return this;
}
//如果不是,且左子结点不为空,我们继续递归左子树
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;
}
2.中序遍历查找
思路 : 根据中序遍历的特点,我们能很容易的得出遍历方法:
先遍历左子树 , 再判断根结点的值域是否为我们需要的,最后遍历右子树
//中序遍历
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;
}
3.后序遍历查找
思路类似,略
二、删除
1.思路
我们不是判断当前结点是否是要删除的结点,而是去看它的左/右子节点是否是需要删除的结点
(1).判断当前结点的左子节点是否为要删除的结点,如果是就删除,不是就递归
(2).判断当前结点的右子节点是否为要删除的结点,如果是就删除,不是就递归
(3).在树中调用删除方法时时 , 还需要判断根节点是否为空,如果不是才能去调用删除方法
2.代码
代码如下(示例):
结点类中的二叉树删除方法
public void delNode(int no) {
//1.判断当前结点的左子节点是否为要删除的结点,如果是就删除,不是就递归
if( this.left != null && this.left.no == no) {
this.left = null;
}else if(this.left != null){
//只满足右结点不为空
this.left.delNode(no);
}
//2.判断当前结点的右子节点是否为要删除的结点,如果是就删除,不是就递归
if( this.right != null && this.right.no == no) {
this.right = null;
}else if(this.right != null){
//只满足右结点不为空
this.right.delNode(no);
}
}
二叉树类中的调用代码
//删除结点
public void delNode(int no) {
if(root != null) {
//判空
if(root.getNo() == no) {
System.out.println(root.getName());
}else {
root.delNode(no); //递归
}
}else {
System.out.println("这是空树");
}
}
三、顺序存储二叉树
1.基本概念
顺序存储二叉树实质上就是将二叉树通过一个数组存储 , 但是遍历则是按照二叉树的方式进行遍历的 , 父子结点和前驱后继关系,是通过下面的公式来实现的
第n个元素的左子结点 : 2n+1
右子节点 : 2n+2
父节点 : (n-1/2) 向下取整
顺序存储结构通常只考虑完全二叉树,这里也讲讲啥是完全二叉树
1.叶子结点只出现在层次最大的两层
2.若右分支最大层数为L层 ,左分支最大层数为L 或 L+1层
2.代码实现
前序遍历实现顺序存储
class ArrBinaryTree{
private int[] arr; //存储数据结点
public ArrBinaryTree(int[] arr) {
this.arr = arr; //构造函数,向顺序存储二叉树中传入一个数组
}
public void preOrder() {
this.preOrder(0);
}
//前序遍历 [公式] : n2+1 , 2n+2 , (n-1)/2
public void preOrder(int index) {
//判空
if(arr == null || arr.length ==0) {
System.out.println("树为空");
return;
}
//输出当前元素
System.out.println(arr[index]);
//左递归
if( (index*2+1) < arr.length ) {
this.preOrder(2 * index + 1); //左递归
}
//右递归
if( (index*2+2) < arr.length ) {
this.preOrder(2 * index + 2);
}
}
}
3.如何优雅地调用前序遍历
public void preOrder() {
this.preOrder(0);
}
上面这段代码实际上是对下面void preOrder(int index)
方法的重载 , 原因是 ,在主函数调用void preOrder(int index)
方法时,我们需要传入一个参数0, 这样方法才能正常启动.
但是 ,传一个0参数非常的不优雅 ,因此我们对它进行一个重载, 重载的无参函数再去调用有参函数,这样就避免的参数外露
四、线索化二叉树
虚线为线索化后的指针,如果在左边引出表示指向前驱结点,右边表示后继结点
按中序遍历得到的结果为 : 5,2,6,1,8,3
注意 : 图文无关!
这里是中序线索化的核心代码
public void threadedNode(HeroNode node){
//判空
if(node == null){
return ;
}
//线索化左子树
threadedNode(node.getLeft());
//线索化当前结点
if(node.getLeft() == null){
node.setLeft(pre); //设置左子节点
node.setLeftType(1); //指向前驱
}
if(pre != null && node.getRight() == null){
node.setRight(pre); //
node.setRightType(1); //指向后继
}
pre = node; //相当于指针后退
//线索化右子树
threadedNode(node.getRight());
}