线索二叉树
线索化二叉树
基本概念
线索化二叉树:利用二叉树中的节点的空指针域,分别指向它的前继节点和后继节点
-
利用空指针域生成的指向称为线索
-
根据树结构的前序中序后序遍历的概念,有前序中序后序线索二叉树
-
一个节点的前一个节点称为前继节点
-
一个节点的后一个节点称为后继节点
说明:二叉树进行线索化后,其左指针域可能是左子树或者前继节点,右指针域可能是右子树或者后继节点
此章节举例二叉树为
前序结果:1,3,8,10,6,14
中序结果:8,3,10,1,14,6
后序结果:8,10,3,14,6,1
二叉树构造
//构造二叉树
TreeNode node1 = new TreeNode(1);
TreeNode node3 = new TreeNode(3);
TreeNode node6 = new TreeNode(6);
TreeNode node8 = new TreeNode(8);
TreeNode node10 = new TreeNode(10);
TreeNode node14 = new TreeNode(14);
node1.setLeft(node3);
node3.setLeft(node8);
node3.setRight(node10);
node1.setRight(node6);
node6.setLeft(node14);
// 设置父节点,用于后续遍历线索二叉树
node3.setParent(node1);
node8.setParent(node3);
node10.setParent(node3);
node14.setParent(node6);
node6.setParent(node1);
//节点属性
class TreeNode
{
private int value;// 权值
private TreeNode left;// 左子节点
private TreeNode right;// 右子节点
private int leftType;// 左边子节点类型 为0则是子树,为1则是前继或后继节点
private int rightType;// 右边子节点类型
private TreeNode parent;// 父节点,用于后序遍历线索二叉树
前序中序后序线索化二叉树
前序线索化二叉树:
-
思路:
-
根据前序遍历的顺序(根左右),先处理根节点,再递归处理左节点和右节点。从根节点开始
-
递归处理左节点,直到叶子节点(要交换pre节点)
-
递归处理右节点,直到结束
-
!!!注意:在进行左右递归是,要判断左右的节点类型(因为是先设置的左右节点会死循环)
-
-
代码
//线索化二叉树(前序)
public void changeTreePre(TreeNode node)
{
if (node == null)
{
return;
}
// 设置前驱节点(因为实在递归过程中的[递]过程进行前继节点赋值,所以时node指向pre)
if (node.getLeft() == null)
{
node.setLeft(pre);
node.setLeftType(1);
}
// 设置后驱节点(因为是在递归过程中的[归]过程进行后继节点赋值,所以是用pre指向node)
if (pre != null && pre.getRight() == null)
{
pre.setRight(node);
pre.setRightType(1);
}
// pre值改变
pre = node;
// 左子树递归
if (node.getLeftType() != 1)
{
changeTreePre(node.getLeft());
}
// 右子树递归
if (node.getRightType() != 1)
{
changeTreePre(node.getRight());
}
}
中序线索化二叉树:
-
思路:
-
根据中序遍历的顺序(左根右),先递归处理左节点,再处理根节点,再递归处理右节点。
-
左子树递归找到中序起点
-
处理根节点的前继后继节点
-
递归处理右节点,直到结束
-
-
代码
// 线索化二叉树(中序)
public void changeTreeMid(TreeNode node)
{
// 找到中序遍历的起始节点
if (node == null)
{
return;
}
// 左子树递归
changeTreeMid(node.getLeft());
// 设置前驱节点
if (node.getLeft() == null)
{
node.setLeft(pre);
node.setLeftType(1);
}
// 设置后驱节点
if (pre != null && pre.getRight() == null)
{
pre.setRight(node);
pre.setRightType(1);
}
// pre值切换
pre = node;
// 右子树递归
changeTreeMid(node.getRight());
}
后序线索化二叉树:
-
思路:
-
根据后序遍历的顺序(左右根),先递归处理左节点,再递归处理右节点,再处理根节点
-
左子树递归找到后序起点
-
右子树递归
-
设置根节点的前后继节点,直到结束
-
-
代码
//线索化二叉树(后序)
public void changeTreeNext(TreeNode node)
{
if (node == null)
{
return;
}
// 向左递归
changeTreeNext(node.getLeft());
// 向右递归
changeTreeNext(node.getRight());
// 设置前驱节点
if (node.getLeft() == null)
{
node.setLeft(pre);
node.setLeftType(1);
}
// 设置后驱节点
if (pre != null && pre.getRight() == null)
{
pre.setRight(node);
pre.setRightType(1);
}
// pre值修改
pre = node;
}
遍历前序中序后序线索化二叉树
前序线索二叉树遍历:
-
思路:
-
根据前序特点(根左右)
-
输出根节点(只要左节点类型是子树)
-
根据后继节点输出,则前序线索二叉树遍历完毕
-
-
代码
// 前序线索二叉树遍历
public void iteratorPre()
{
TreeNode node = root;
// while(node!=null &&node.getLeftType()==0) {
// System.out.println(node.getValue());
// while(node.getLeftType()==0) {
// node=node.getLeft();
// System.out.println(node.getValue());
// }
// while(node.getRightType()==1) {
// node=node.getRight();
// System.out.println(node.getValue());
// }
// }
//以上为自己写的前序
//一下是参考别人的前序,相对来说更简洁
while (node != null)
{
System.out.print(node.getValue() + " ");
// 如果存在左子节点就往左走,否则往右走,此时右指针一定是前序遍历的下一个节点
if (node.getLeftType() == 0)
{
node = node.getLeft();
} else
{
node = node.getRight();
}
}
}
中序线索二叉树遍历:
-
思路:
-
根据中序特点(左根右)
-
左子树递归(找到起点)
-
由起点开始向后遍历(通过后继节点)
-
当不再是后继节点时,将node置为node的右节点(因为左根右,到了根节点时,要遍历右节点)
-
-
代码
// 中序线索二叉树遍历
public void iteratorMid()
{
TreeNode node = root;
while (node != null)
{
// 找到中序起始节点
while (node.getLeftType() == 0)
{// 左指针为左子节点
node = node.getLeft();
}
System.out.println(node.getValue());
while (node.getRightType() == 1)
{// 为后继节点
node = node.getRight();
System.out.println(node.getValue());
}
node = node.getRight();
}
}
后序线索二叉树遍历:
-
说明:后序线索二叉树遍历时,需要用的父节点属性,在前面已经设置完毕
-
思路:
-
根据后序遍历顺序(左右根)
-
左子树递归找到后序起点
-
判断节点类型
-
节点的右节点是后继节点,输出节点,并替换pre,node=right
-
节点的不是后继节点的情况
-
节点的右节点是上一个节点,打印节点(此时左右根均已输出),则要找父节点的的右节点,则替换pre并且node=parent
-
节点的右节点不是上一个节点,node=right,递归处理右子树
-
-
-
-
代码
// 后序线索二叉树遍历
public void iteratorNext()
{
TreeNode node = root;
// 找后序起始节点
while (node.getLeftType() == 0)
{
node = node.getLeft();
}
while (node != null)
{
// 判断起始节点的右节点是否是后继节点
if (node.getRightType() == 1)
{
System.out.println(node.getValue());
pre = node;
node = node.getRight();
} else
{
// 判断是否是处理节点的父节点
if (node.getRight() == pre)
{
System.out.println(node.getValue());
// 结束条件(输出根节点)
if (node == root)
{
return;
}
pre = node;
node = node.getParent();
} else{
node = node.getRight();
while (node != null && node.getLeftType() == 0)
{
node = node.getLeft();
}
}
}
}
} -