package BinaryTree;
import java.util.Scanner;
import java.util.Stack;
//二叉树的节点
public class Node {
String value;
Node left;
Node right;
public Node(String value){
this.value = value;
}
/**
* 创建一个二叉树
* 规定:不允许输入空数据,即输入的二叉树得至少有一个节点
* para:data是一个字符串数组,存储二叉树先序遍历得到的节点值,
* start是节点值在数组中开始下标,end是节点值在数组中的结束下标,如果数组中没有值,start = -1, end = -1
* 在进行判断时,不能使用data.length进行判断,因为递归函数使用的是同一个字符串数组作为输入变量,只是start和end的值不一样
* 输入的形式是作为一个满二叉树进行输入,空节点用$号代替
* 采用先序的方式创建一个二叉树
*/
public static Node createBinaryTree(String[] data,int start,int end){//由于使用递归算法,所有得多设置几个参数
if((end == start)){//只有一个节点的情况下,这就是递归结束条件,不可能出现end小于start的情况
if(data[start].equals("$")){
return null;
}else{
return new Node(data[start]);
}
}else{
if(data[start].equals("$")){//如果说某个子树的根节点为$,直接返回null,下面也不用计算了1,
return null;
}
Node root = new Node(data[start]);
int halfLength = (end - start)/2;
Node leftChild = createBinaryTree(data,start+1,start+halfLength);
Node rightChild = createBinaryTree(data,start+halfLength+1,start+2*halfLength);
root.left = leftChild;
root.right = rightChild;
return root;
}
}
/**
* 先序递归输出二叉树
*/
public static void preOrder(Node root){
if(root != null){
System.out.print(root.value+" ");
preOrder(root.left);
preOrder(root.right);
}
}
/**
* 中序递归输出二叉树
*/
public static void inOrder(Node root){
if(root != null){
inOrder(root.left);
System.out.print(root.value+" ");
inOrder(root.right);
}
}
/**
* 后序递归遍历二叉树
*/
public static void postOrder(Node root){
if(root != null){
postOrder(root.left);
postOrder(root.right);
System.out.print(root.value+" ");
}
}
/**
* 后序非递归遍历二叉树
* 后序非递归得使用到栈的结构
* 两层循环
* 我们要记得的是非递归遍历的栈中保存的是一个路径上的节点
* 出栈时访问,在栈中就没有访问
* 如果是回退到一个节点,那么只有可能该节点右子树和该节点没有被访问,该节点的左子树肯定被访问了
* 总结一下:外循环栈不为空,内循环一个左分支持续入栈,右分支持续出栈,出栈判断栈是否为空
*/
public static void postOrderNotRecursive(Node root){
Stack<Node> stack = new Stack<>();//<>是泛型机制
if(root == null){
return;
}
Node node = root;
stack.push(node);
node = node.left;
//两层循环、最外层循环就是栈不为空,因为根节点是第一个进栈,最后一个出栈的,但是得先进栈根节点
while(!stack.isEmpty()){
while(node != null){
stack.push(node);
node = node.left;
}
//到这儿是左遍历结束,现在的目的是访问栈顶节点的右子树,先把这个节点拿出来,不能pull出来
//当这个时候,弹出栈中的右分支,直到遇到左节点,这里稍微难写一点,注意,右分支不是右子树为空的节点,而是栈上节点是栈下节点的右子节点
//上一次弹出的节点如果是栈顶节点的右节点,则弹出栈顶节点
Node rightNode = null;
node = stack.peek();
while(node.right == rightNode){
rightNode = stack.pop();
System.out.print(rightNode.value+" ");
if(!stack.isEmpty()) {
node = stack.peek();
}else{
return;
}
}
node = node.right;
}
}
/**
* 先序遍历非递归形式
* 也要使用栈
*/
//仍然使用栈结构
//循环条件是什么呢?
//因为栈中元素是没访问过的,而根节点是第一个访问的。
//之前想过,只有后序遍历栈中的元素有规律,而先序和中序都没有什么规律。
//其实先序遍历的非递归形式很简单,我们要记住:一个节点访问过了就一定出栈了,如果在栈中,就一定是没有访问过的。
//那么就很简单了,根节点,压入,弹出,但是弹出之前得先把它的左右节点压入栈中,不然的话,左右子树的信息会全部丢失,那么又有问题来了
//先压入哪个节点呢?因为栈是先进后出,也就是先压入的后访问,那么我们自然得先把右节点压进去了,如果有子节点不为空,那就不用压入了,
//因为节点的访问是从栈中取出再访问,节点的压入,也是把一个节点弹出,然后拿它的左右子节点压入,那如果栈中没元素了,就没元素访问,也没元素压入了
//所有循环条件也是栈不为空
public static void preOrderNotRecursive(Node root){
if(root == null){
return;
}
Stack<Node> stack = new Stack<>();
Node node = root;
stack.push(node);
while(!stack.isEmpty()) {
node = stack.pop();
System.out.print(node.value+" ");
if (node.right != null) {//先压入右节点,再压入左节点
stack.push(node.right);
}
if (node.left != null) {
stack.push(node.left);
}
}
}
/**
* 中序遍历的非递归形式
* 中序的外层循环结束条件变了,不是栈不为空了,而是弹出节点的右节点不为空,
*/
public static void inOrderNotRecursive(Node root){
if(root == null){
return;
}
Stack<Node> stack = new Stack<>();
Node node = root;
while(node !=null) {
while (node != null) {
stack.push(node);
node = node.left;
}
do {
node = stack.pop();
System.out.print(node.value + " ");
node = node.right;
} while(!stack.isEmpty()&&node == null);//和非递归后续遍历一样,在进行持续出栈的时候一定要判断栈不为空。
}
}
/**
* 测试函数
*/
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
String[] data = s.split(",");
Node root = createBinaryTree(data,0,data.length-1);
inOrderNotRecursive(root);
}
}
The time complexity of the algorithm is O (N), the spatial complexity is O (h), h is the height of the tree.