二叉树的基本操作
继续看左神的视频,听他的思路之后自己写代码实现,过程中遇到很多问题,好在一一克服。写了很多注释和自己对其思路的理解。
一下代码包括了:
- 二叉树前中后序遍历的递归和非递归
- 层次遍历的非递归
- 二叉树深度
- 二叉树的序列化和反序列化非递归实现
- 判断二叉树是否是AVL树
- 判断二叉树是否是完全二叉树并计算其节点数(时间复杂度小于O(N))。
import java.util.*;
import static java.lang.Integer.max;
import static java.lang.Math.pow;
import static java.lang.StrictMath.abs;
/**
* 对二叉树的操作:
* 1.遍历(递归和非递归)
* 2.初始化
* 3.寻找深度
* 4. 特殊二叉树的相关操作
*/
public class BinaryTree {
public static int[] flag = new int[] {1,0};
//先序创建二叉树
public static TreeNode init()
{
int val = new Scanner(System.in).nextInt();
if(val == -1)
return null;
TreeNode root = new TreeNode(val);
root.left = init();
root.right = init();
return root;
}
// 先序遍历递归版本
public static void travelPriorRecursion(TreeNode root)
{
if(root == null)
return;
//功能语句
System.out.println(root.val);
travelPriorRecursion(root.left);
travelPriorRecursion(root.right);
}
//中序遍历递归版本
public static void travelMidRecursion(TreeNode root)
{
if(root == null)
return;
//功能语句
travelPriorRecursion(root.left);
System.out.println(root.val);
travelPriorRecursion(root.right);
}
//后序遍历递归版本
public static void travelLaterRecursion(TreeNode root)
{
if(root == null)
return;
//功能语句
travelPriorRecursion(root.left);
travelPriorRecursion(root.right);
System.out.println(root.val);
}
// 先序非递归
public static void travelPrior(TreeNode root)
{
//java提供的系统栈1
Stack<TreeNode> st = new Stack();
TreeNode current = root;
st.push(current);
while(!st.isEmpty())
{
current = st.pop();
//功能语句
if(current != null)
{
System.out.println(current.val);
st.push(current.right);
st.push(current.left);
}
}
}
//中序非递归 遇到根节点先不打印,遇到最后的节点再开始弹出
public static void travelMid(TreeNode root)
{
Stack<TreeNode> st = new Stack();
TreeNode current = root;
//由于右边是不放节点的,
// 所以在根节点的左边子树全部访问完成之后栈就空了,但是右边没有访问,
// 所以while的判断需要再加一条
while(!st.isEmpty()|| current != null)
{
//一直找到最左端的节点
if(current != null )
{
st.push(current);
current = current.left;
}
else
{
//功能语句
current = st.pop();
System.out.println(current.val);
current = current.right;
}
}
}
//后序非递归 :利用两个栈 先按照中右左的顺序走一遍,即调整一下先序遍历的入栈顺序即可
//遍历时打印的内容改为入另一个辅助栈,最后依次弹出辅助栈的全部内容即可
public static void travelLater(TreeNode root)
{
Stack<TreeNode> st1 = new Stack();
Stack<TreeNode> st2 = new Stack();
TreeNode current = root;
st1.push(current);
while(!st1.isEmpty())
{
current = st1.pop();
if(current != null)
{
st2.push(current);
//先放左结点,顺序就是 中--右---左
st1.push(current.left);
st1.push(current.right);
}
}
while (!st2.isEmpty())
{
System.out.println(st2.pop().val);
}
}
//层次遍历
public static void travelLevelOrder(TreeNode root)
{
Queue<TreeNode> queue = new LinkedList<>();
TreeNode current = root;
queue.add(current);
while(!queue.isEmpty())
{
current = queue.poll();
System.out.println(current.val);
if(current.left != null)
queue.add(current.left);
if(current.right != null)
queue.add(current.right);
}
}
//二叉树序列化与反序列:序列化用于持久化,将二叉树的结构转化成存储在内存中的文件,再利用反序列还原。
// 本质上就是来实现非递归遍历
// 不同节点间用“_“隔开,空节点用"#" 表示
public static String binaryTreeSerialization(TreeNode root)
{
String res = "";
Queue<TreeNode> queue = new LinkedList<>();
TreeNode current = root;
queue.add(current);
res += root.val + "_";
// 层次顺序序列化
while(!queue.isEmpty())
{
current = queue.poll();
if(current.left == null)
res += "#_";
else
{
res += current.left.val + "_";
queue.add(current.left);
}
if(current.right == null)
res += "#_";
else
{
res += current.right.val + "_";
queue.add(current.right);
}
}
return res;
}
//反序列化
public static TreeNode binaryTreeCounterSerialization(String str)
{
String[] arr = str.split("_");
if(arr.length == 0)
return null;
TreeNode root;
Queue<TreeNode> queue = new LinkedList<>();
root = new TreeNode(Integer.parseInt(arr[0]));
queue.add(root);
TreeNode current = root;
for(int i = 1; i < arr.length; i++)
{
current = queue.poll();
if(!arr[i].equals("#"))
{
current.left = new TreeNode(Integer.parseInt(arr[i]));
queue.add(current.left);
}
else
{
current.left = null;
}
i++;
if(!arr[i].equals("#"))
{
current.right = new TreeNode(Integer.parseInt(arr[i]));
queue.add(current.right);
}
else
{
current.right = null;
}
}
return root;
}
/**
* 判断是否是AVL树
*
* @param head
* @return
* avL树是所有子树的高度差小于2 0表示是否是AVL树, 1表示树的高度
* 思路就是:利用递归的结构,三次访问一个节点,我第一次访问这个节点的时候往左走,收集左子树的全部信息,
* 第二次访问到这个节点的时候往右走,收集右子树的高度信息
* 第三次访问节点时就开始计算高度,判断是否是AVL树
* 如果是则向上返回,如果不是就返回false。
*/
public static boolean isVALTree(TreeNode head)
{
if(isVALTreeProcess(head) != -1)
return true;
else
return false;
}
public static int isVALTreeProcess(TreeNode head)
{
//遍历到叶子节点就返回 此时高度为1,并且认为是AVL树
//从遍历的视角来,我们是从最下层向上看,叶子节点是第一个被记录的节点,所以高度是1
if(head == null)
{
return 1;
}
//从左子树收集高度
int left = isVALTreeProcess(head.left);
if(left == -1)
return -1;
//从右子树收集高度
int right = isVALTreeProcess(head.right);
if(right == -1)
return -1;
//不满足AVL定义就返回-1
if(abs(left - right) > 1)
return -1;
else
//否则就向该节点的父节点返回自己的收集的高度
return max(left,right)+1;
}
/**
* 判断是否是完全二叉树
*
* 思路:使用层次遍历,利用二叉树节点和高度的关系,在遍历的过程中记录二叉树的子节点的情况
* 当一个节点的左子树为空而右子树不为空,返回false
* 当一个右子树为空,那么之后遍历的节点一定是叶子节点,否则返回false
* @param head
* @return
*/
public static boolean isPerfectTree(TreeNode head)
{
Queue<TreeNode> queue = new LinkedList<>();
queue.add(head);
TreeNode current;
boolean flag = false;
while(!queue.isEmpty())
{
current = queue.poll();
if(flag)
{
if(current.left != null || current.right != null)
return false;
}
//这样的树一定不是完全二叉树
if(current.left == null && current.right != null)
return false;
//这样的树可能是,但是之后的所有节点必须是叶子节点
else if(current.left != null && current.right != null)
{
if(current.left != null)
queue.add(current.left);
if(current.right != null)
queue.add(current.right);
}
else
{
flag = true;//开启新的判断
}
}
return true;
}
/**
* 计算一颗完全二叉树的节点个数,要求时间复杂度小于N
* 思路:首先找到根节点的右子树的最左节点,如果这个节点达到了树的最大高度,则其左子树一定是满二叉树,利用公式计算即可。
* 如果没有达到最后一层,则整个树的节点数为H-1层的满二叉树加上叶子左子树上的叶子节点。
* @param head
* @return
*/
public static int conutNum(TreeNode head)
{
int num = 0;
Queue<TreeNode> queue = new LinkedList<>();
queue.add(head);
TreeNode current = head;
while (!queue.isEmpty())
{
int deep = 1;
int maxDeep = 0;
current = queue.poll();
TreeNode temp = current;
while (temp != null)
{
temp = temp.left;
maxDeep ++;
}
temp = current;
//寻找右子树的最左节点
while (temp.right != null)
{
temp = temp.right;
deep ++;
}
//第二种情况,我们此时需要遍历左子树,把左结点入队列 deep就是最大层数减一
if(deep != maxDeep)
{
if(current.left != null)
queue.add(current.left);
}
else
{
if(current.right != null)
queue.add(current.right);
}
num += (int)pow(2, deep - 1);
}
return num;
}
// for test
// 工具类中不要写main函数
public static void main(String[] args) {
TreeNode root = init();
//测试遍历
// travelLevelOrder(root);
// String[] data = str.split("_");
// for(int i = 0; i < data.length; i++)
// {
// System.out.println(data[i]);
// }
//测试序列化
// String str = binaryTreeSerialization(root);
// travelLevelOrder(binaryTreeCounterSerialization(str));
// travelPriorRecursion(binaryTreeCounterSerialization(str));
// travelLater(binaryTreeCounterSerialization(str));
//测试AVL树
// System.out.println(isVALTree(root));
//测试完全二叉树
travelPrior(root);
if(isPerfectTree(root))
System.out.println(conutNum(root));
else
System.out.println("不是完全二叉树");
//测试计算完全二叉树节点的个数
}
}