线索二叉树是利用空出来的指针域来存储 某个遍历序列 下的前驱和后继,左边的空节点指针指向前驱节点,右边的空节点指针指向后继节点。
由于线索二叉树是基于某种遍历序列的,所以就需要在对二分搜索树进行某种遍历的时候,构造线索,主要分为:
- 前序序列线索树
- 中序序列线索树
- 后续序列线索树
用土办法找树中某节点的中序遍历前驱节点
//前向指针
Node<E> pre = null;
boolean findFlag = false;
Node<E> target = null;
private Node<E> findNode(E e) {
return findNode(e, root);
}
private Node<E> findNode(E e, Node<E> node) {
if (node == null) {
return null;
}
if (e.compareTo(node.data) < 0) {
return findNode(e, node.left);
} else if (e.compareTo(node.data) > 0) {
return findNode(e, node.right);
}
return node;
}
//中序遍历
public E findInOrderPre(E e) {
//找到目标节点
target = findNode(e);
return pre.data;
}
//按照前序的顺序遍历整棵树
private void inOrderForFindPre(Node<E> node) {
if (node == null) {
return;
}
inOrderForFindPre(node.left);
//在这里调整pre指针
visit(node);
inOrderForFindPre(node.right);
}
private void visit(Node<E> node) {
//如果还没有找到
if (!findFlag) {
//如果当前节点就是目标节点
if (node.data.compareTo(q.data) == 0) {
findFlag = true;
//输出pre,即前序遍历中 目标节点的前驱节点
System.out.println("目标节点的前驱节点为:" + pre);
} else {
//否则,将pre指向当前节点,继续向后遍历
pre = node;
}
}
}
节点结构
private static class Node<E extends Comparable<E>> {
E data;
Node<E> left;
Node<E> right;
//用来记录当前节点的左右子节点指针是指向真实的子节点还是作为线索指针
//0-指向真实的子节点 1-线索指针
int ltag;
int rtag;
}
构造前序线索树
private Node<E> preOrderThreadPrev;
public void preThreadTree() {
preThreadTree(root);
if (preOrderThreadPrev != null) {
preOrderThreadPrev.rtag = 1;
}
}
private void preThreadTree(Node<E> node) {
if (node == null) {
return;
}
preThreadTreeVisit(node);
if (node.ltag == 0) {
preThreadTree(node.left);
}
if (node.rtag == 0) {
preThreadTree(node.right);
}
}
private void preThreadTreeVisit(Node<E> node) {
if (node.left == null) {
node.left = preOrderThreadPrev;
node.ltag = 1;
}
//如果前驱的right是空,就让他指向node节点
if (preOrderThreadPrev != null &&
preOrderThreadPrev.right == null) {
preOrderThreadPrev.right = node;
preOrderThreadPrev.rtag = 1;
}
preOrderThreadPrev = node;
}
遍历前序线索树
/**
* ③由于先序遍历线索树中的节点无法找到它的前驱节点(因为前驱是向前指,而节点的当前两个子节点都是向后只的)
* 所以只能从前往后遍历
* */
private Node<E> preOrderSequenceFirstNode(Node<E> node) {
//以node为根节点的子树的先序遍历的第一个节点就是当前节点
return node;
}
private Node<E> preOrderSequenceNextNode(Node<E> node) {
//如果right被线索化了,就直接返回right
if (node.rtag == 1) {
return node.right;
}
//如果没有,先序遍历的后继节点 只可能是它的左 / 右子节点
if (node.left != null) {
return node.left;
} else if (node.right != null) {
return node.right;
}
return null;
}
public void preSequenceOrder() {
for (Node<E> cur = preOrderSequenceFirstNode(root); cur != null; cur = preOrderSequenceNextNode(cur)) {
System.out.print(cur.data + " ");
}
System.out.println();
}
构造中序线索树
private Node<E> inOrderThreadPrev;
//②中序序列线索化
public void inThreadTree() {
inThreadTree(root);
if (inOrderThreadPrev != null) {
inOrderThreadPrev.rtag = 1;
}
}
private void inThreadTree(Node<E> node) {
if (node == null) {
return;
}
inThreadTree(node.left);
inThreadTreeVisit(node);
if (node.rtag == 0) {
inThreadTree(node.right);
}
}
private void inThreadTreeVisit(Node<E> node) {
if (node.left == null) {
node.left = inOrderThreadPrev;
node.ltag = 1;
}
if (inOrderThreadPrev != null &&
inOrderThreadPrev.right == null) {
inOrderThreadPrev.right = node;
inOrderThreadPrev.rtag = 1;
}
inOrderThreadPrev = node;
}
遍历中序线索树
private Node<E> inOrderSequenceFirstNode(Node<E> node) {
Node<E> cur = node;
while (cur.ltag == 0) {
cur = cur.left;
}
return cur;
}
//中序遍历序列中的下一个节点
private Node<E> inOrderSequenceNextNode(Node<E> node) {
if (node.rtag == 1) {
return node.right;
}
return inOrderSequenceFirstNode(node.right);
}
public void inSequenceOrder() {
for (Node<E> cur = inOrderSequenceFirstNode(root); cur != null; cur = inOrderSequenceNextNode(cur)) {
System.out.print(cur.data + " ");
}
System.out.println();
}
/**
* 中序遍历序列的逆序遍历
* */
private Node<E> inOrderSequenceLastNode(Node<E> node) {
Node<E> cur = node;
while (cur.rtag == 0) {
cur = cur.right;
}
return cur;
}
private Node<E> inOrderSequencePrevNode(Node<E> node) {
if (node.ltag == 1) {
return node.left;
}
return inOrderSequenceLastNode(node.left);
}
public void inSequenceOrderReverse(){
for (Node<E> cur = inOrderSequenceLastNode(root); cur != null; cur = inOrderSequencePrevNode(cur)) {
System.out.print(cur.data + " ");
}
System.out.println();
}
构造后续线索树
private Node<E> postOrderThreadPrev;
public void postThreadTree() {
postThreadTree(root);
//注意,由于后续遍历的最后一个节点不是叶子节点,所以不能同先序遍历和
//中序遍历那样,存在下面的逻辑
/*if (postOrderThreadPrev != null) {
postOrderThreadPrev.rtag = 1;
}*/
}
private void postThreadTree(Node<E> node) {
if (node == null) {
return;
}
//if (node.ltag == 0) {
postThreadTree(node.left);
//}
//if (node.rtag == 0) {
postThreadTree(node.right);
//}
postThreadTreeVisit(node);
}
private void postThreadTreeVisit(Node<E> node) {
if (node.left == null) {
node.left = postOrderThreadPrev;
node.ltag = 1;
}
if (postOrderThreadPrev != null
&& postOrderThreadPrev.right == null) {
postOrderThreadPrev.right = node;
postOrderThreadPrev.rtag = 1;
}
postOrderThreadPrev = node;
}
遍历后续线索树
/**
* ③遍历
* 由于后序线索二叉树找不到后继节点,所以只能从后往前遍历
* */
private Node<E> postOrderSequenceLastNode(Node<E> node) {
return node;
}
private Node<E> postOrderSequencePreNode(Node<E> node) {
if (node.ltag == 1) {
return node.left;
}
if (node.right != null && node.rtag != 1) {
return node.right;
}
return node.left;
}
public void postSequenceOrderReverse() {
for (Node<E> cur = postOrderSequenceLastNode(root); cur != null; cur = postOrderSequencePreNode(cur)) {
System.out.print(cur.data + " ");
}
System.out.println();
}