目前学习到现在所出现的问题如下
1.先序序列的线索二叉树构建这一节
基于先序序列进行构建线索二叉树时,向左先序化和向右先序化之前都需要进行是否是孩子标志条件判断,否则会出现栈溢出的异常,对于这一点,王道视频的数据结构中的示例代码中,只对向左先序化时进行了标志位的处理,并没有对向右先序化进行处理处理.这一点是我认为它的代码有漏洞的原因.
java版本的详细代码如下:
package com.wqc.tree.threadbinarytree;
/**
* @author 高瞻远瞩
* @version 1.0
* @motto 算法并不可怕, 可怕的是你不敢面对它, 加油!别浮躁~冲击大厂!!!
* 线索二叉树 叶子结点左指针指向前驱结点 右指针指向后继结点
* n个结点的线索二叉树 空指针域有2n-(n-1) = n+1个
* 明天实现基于先序实现线索二叉树
*/
public class ThreadBinaryTreeDemo {
public static void main(String[] args) {
ThreadBinaryTree threadBinaryTree = new ThreadBinaryTree();
Node root = new Node(1, "tom");
threadBinaryTree.setRoot(root);
Node node1 = new Node(3, "lisa");
Node node2 = new Node(6, "tony");
Node node3 = new Node(8, "john");
Node node4 = new Node(10, "marry");
Node node5 = new Node(14, "jack");
//生成二叉树
root.setLeft(node1);
root.setRight(node2);
node1.setLeft(node3);
node1.setRight(node4);
node2.setLeft(node5);
node1.setParent(root);//设置父结点
node2.setParent(root);
node3.setParent(node1);
node4.setParent(node1);
node5.setParent(node2);
threadBinaryTree.preThreadBinaryTree();//先序遍历的结果 1 3 8 10 6 14
//验证一下先序线索化是否成功
System.out.println(node3.getLeft());//3
System.out.println(node3.getRight());//10
System.out.println(node4.getLeft());//8
System.out.println(node4.getRight());//6
System.out.println(node2.getRight());//14
System.out.println(node5.getLeft());
//输出6 node5节点本来是node2节点的左孩子 经过线索化后 确成为了node2节点前驱 相同的是 他们两个节点成为了前驱和后继的关系
System.out.println(node5.getRight());//null
System.out.println("递归方式进行先序遍历");
threadBinaryTree.preOrder();
System.out.println("非递归方式进行先序遍历");
threadBinaryTree.preOrder2();
}
}
class ThreadBinaryTree {
private Node root;
private Node pre; //在递归线索化时 pre总是保留前一个结点
public void setRoot(Node root) {
this.root = root;
}
/**
* @param node 基于中序遍历构建线索二叉树
*/
public void ThreadedNodes(Node node) {
if (node == null) {//如果当前结点为null 不能线索化
return;
}
if (node.getLeftType() != 1) {//在中序遍历中 此线索化的条件可以省略
ThreadedNodes(node.getLeft());//线索化左子树
}
if (node.getLeft() == null) {
node.setLeft(pre);//让当前结点左指针指向前驱结点
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null) {
pre.setRight(node);//让pre的右指针指向后继结点
pre.setRightType(1);
}
pre = node;
if (node.getRightType() != 1) {
ThreadedNodes(node.getRight());//线索化右子树
}
}
//中序线索化二叉树的重载
public void ThreadedNodes() {
this.ThreadedNodes(root);
pre.setRightType(1);
}
//递归方式中序遍历线索二叉树
public void infixOrder() {
if (root == null) {
System.out.println("二叉树为空");
return;
}
root.infixOrder();
}
//非递归方式中序遍历线索化二叉树
public void infixOrder2() {
Node node = root;
while (node != null) {
//先向左遍历到最左结点
while (node.getLeftType() == 0) {
node = node.getLeft();
}
//输出当前结点
System.out.println(node);
while (node.getRightType() == 1) {
node = node.getRight();//让node指向它的后继结点 按照线索二叉树的次序
if(node == null) return;
System.out.println(node);//输出
}
node = node.getRight();//然后让node结点指向这个后继结点
}
}
/**
* 基于先序遍历构建线索二叉树
* @param node
*/
public void preThreadBinaryTree(Node node) {
if (node == null) {//如果该结点已经线索化的话就返回
return;
}
if (node.getLeft() == null) {
node.setLeft(pre);//设置前驱结点
node.setLeftType(1);
}
if (pre != null && pre.getRight() == null) {
pre.setRight(node);
pre.setRightType(1);
}
pre = node;
//向左线索化
if (node.getLeftType() != 1) {
preThreadBinaryTree(node.getLeft());
}
//向右线索化
if (node.getRightType() != 1) {
preThreadBinaryTree(node.getRight());
}
}
public void preThreadBinaryTree() {//先序线索二叉树的方法重载
this.preThreadBinaryTree(root);
}
//递归方式的先序遍历线索二叉树
public void preOrder() {
if (root != null) {
root.preOrder();
} else {
System.out.println("二叉树为空");
}
}
//非递归方式的先序遍历线索二叉树
public void preOrder2() {
if (root == null) {
System.out.println("二叉树为空");
return;
}
Node node = root;
while (node != null) {
while (node.getLeftType() == 0) {
System.out.println(node);//打印没有线索化的结点
node = node.getLeft();
}
System.out.println(node);//退出时打印当前线索化的结点
node = node.getRight();
}
}
/**
* 基于后序遍历构建线索二叉树
* @param node
*/
public void postThreadBinaryTree(Node node) {
if (node == null) {
return;
}
//向左线索化(前提是没有被线索化)
if (node.getLeftType() == 0) {//在后续遍历中 此判断条件也可以省略
postThreadBinaryTree(node.getLeft());
}
//向右线索化
if (node.getRightType() == 0) {
postThreadBinaryTree(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 = node;
}
public void postThreadBinaryTree() {
this.postThreadBinaryTree(root);
}
//递归方式后序遍历线索二叉树
public void postOrder() {
if (root != null) {
root.postOrder();
} else {
System.out.println("二叉树为空");
}
}
//非递归方式的后序遍历
public void postOrder2() {
Node node = root;
if(node == null){
System.out.println("二叉树为空");
return;
}
Node pre = null;
while(node.getLeftType() == 0){//先找到最左的线索化结点
node = node.getLeft();
}
while(node != null){
if(node.getRightType() == 1){//如果右结点是线索化结点
System.out.println(node);//输出
pre = node;
node = node.getRight();
}else{//如果不是线索化结点的话
if(node.getRight() == pre){//如果当前结点的右节点是上一个处理的结点的话
// 说明当前结点的左右子树都已经遍历结束
System.out.println(node);//输出当前结点
if(node == root){
return; //如果是根结点的话 直接跳出循环
}
pre = node;
node = node.getParent();
}else{//继续向右子树的左边遍历
node = node.getRight();
while(node != null && node.getLeftType() == 0){
node = node.getLeft();
}
}
}
}
}
}
class Node {
private int id;
private String name;
private Node left;
private Node right;
private int leftType;//默认为0 代表是指向左子树 1代表是前驱结点
private int rightType;//默认为0 代表是指向右子树 1代表是后继结点
private Node parent;//定义一个父结点 方便进行非递归的后序遍历
public Node(int id, String name) {
this.id = id;
this.name = name;
}
public Node getParent() {
return parent;
}
public void setParent(Node parent) {
this.parent = parent;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
public int getLeftType() {
return leftType;
}
public void setLeftType(int leftType) {
this.leftType = leftType;
}
public int getRightType() {
return rightType;
}
public void setRightType(int rightType) {
this.rightType = rightType;
}
//中序遍历
public void infixOrder() {
if (this.left != null && this.leftType == 0) {
//左结点不为null 且 当前结点的左结点类型是左子树的话(即没有线索化的话) 才继续进行遍历
this.left.infixOrder();
}
System.out.println(this);
if (this.right != null && this.rightType == 0) {
this.right.infixOrder();
}
}
//先序遍历
public void preOrder() {
System.out.println(this);
if (this.left != null && this.leftType == 0) {
this.left.preOrder();
}
if (this.right != null && this.rightType == 0) {
this.right.preOrder();
}
}
//后续遍历
public void postOrder() {
if (this.left != null && this.leftType == 0) {
this.left.postOrder();
}
if (this.right != null && this.rightType == 0) {
this.right.postOrder();
}
System.out.println(this);
}
@Override
public String toString() {
return "Node{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
由于这个版本的数据结构是我最早学习的,所以示例代码基本上写的都有,当我今天再次学习c版本的时候,发现两者并无本质的差别,核心代码基本上都一模一样,我也懒得再写c版本的代码了.关键是c语言的语法太麻烦了.写起来太费时间了.我希望你们能理解我。
如下图:如果把167行和171行的条件判断任何一个去掉的话都会出现转圈问题的,最后也都会出现栈溢出的异常
这就是忽略向右线索化条件判断所出现的后果。。。。具体的实现过程可以自己debug一遍