1.二叉树的按层遍历
这里有一个二叉树,我们按层遍历,应该输出 a b c d e f
那么,我们可以怎么实现呢?
我们可以通过队列来实现。
当然实现代码也很简单,在此奉上
public void levelBinary(Node head){
if (head == null){
return;
}
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while (!queue.isEmpty()){
Node poll = queue.poll();
System.out.println(poll.value);
if (poll.left != null){
queue.add(poll.left);
}
if (poll.right != null){
queue.add(poll.right);
}
}
}
该代码只能按照层序将二叉树输出,并不能知道该节点在哪一层,当然我们可以对该代码进行改进,使得我们可以知道该节点在哪一层。
2.二叉树的序列化和反序列化
2.1什么是序列化和反序列化
2.1.1序列化
把内存中的二叉树变成文件的形式,最好把二叉树转成一个字符串,该字符串可以代表一个唯一的结构,并且可以还原出唯一的树来,将二叉树变为字符串的结构,叫做序列化
2.1.2反序列化
我们把转换成的字符串,还原成唯一的树结构,叫做反序列化
2.2为什么需要序列化和反序列化
我们知道,二叉树是内存中的结构,没有不挂的服务,假如机器要停机,我们想人为控制把树变成文件的形式,就需要序列化,当我们重启的时候,或者移植到其他服务的时候,我们需要把文件还原成唯一的二叉树,就需要反序列化
2.3 怎么做?
我们可以通过二叉树的先序遍历或者按层遍历来实现。
比如说这么一个二叉树
首先我们认为null不可忽略,null相当于一个占位符,用来保存结构的,我们可以将二叉树想象成这个样子
然后我们进行先序遍历,遍历到谁,就直接序列化了,null不可忽略,用于保存结构,该例子中用#代替,其实可以用任意字符,只要我们在还原时用相同的字符还原即可,该二叉树序列化后为
"1,1,#,1,#,#,#,"
我们如果使用先序遍历进行序列化,那么就应该用先序遍历进行反序列化
先序遍历序列化代码
private static Queue<String> preSerial(Node head){
Queue<String> queue = new LinkedList<>();
pres(queue,head);
return queue;
}
public static void pres(Queue<String> queue,Node head){
if (head == null){
queue.add(null);
}else {
queue.add(String.valueOf(head.value));
pres(queue,head.left);
pres(queue,head.right);
}
}
先序遍历反序列化代码
public static Node buildByQueue(Queue<String> queue){
if (queue == null || queue.size() == 0){
return null;
}
return preb(queue);
}
public static Node preb(Queue<String> queue){
String headStr = queue.poll();
if (headStr == null){
return null;
}
Node headNode = new Node(Integer.valueOf(headStr));
headNode.left = preb(queue);
headNode.right = preb(queue);
return headNode;
}
那既然有先序序列化,是由也会有中序序列化和后续序列化呢
后序序列化是有的,我们可以根据先序的改,代码大差不差,但是中序序列化是没有的,因为即使null不可忽略,我们根据中序排序,也没办法还原出唯一的二叉树。譬如
/**
* 2
* /
* 1
* 和
* 1
* \
* 2
* 补足空位的中序遍历结果都是 { null,1,null,2,null }
*/
所以没办法根据中序遍历进行序列化和反序列化
按层进行序列化
可以根据上文提到的二叉树按层遍历进行改造,改成按层进行序列化,其实就是在按层遍历的基础上添加了一些东西。
public static Queue<String> levelSerial(Node head){
Queue<String> queue = new LinkedList<>();
if (head == null){
queue.add(null);
}else {
queue.add(String.valueOf(head.value));
Queue<Node> levelQueue = new LinkedList<>();
levelQueue.add(head);
while (!levelQueue.isEmpty()){
head = levelQueue.poll();
if (head.left != null){
levelQueue.add(head.left);
queue.add(String.valueOf(head.left.value));
}else {
queue.add(null);
}
if (head.right != null){
levelQueue.add(head.right);
queue.add(String.valueOf(head.right.value));
}else {
queue.add(null);
}
}
}
return queue;
}
按层反序列化
public static Node buildByLevelQueue(Queue<String> queue){
if (queue == null || queue.size() == 0){
return null;
}
Node head = generateNode(queue.poll());
Queue<Node> nodeQueue = new LinkedList<>();
if (head != null){
nodeQueue.add(head);
}
Node node = null;
while (!nodeQueue.isEmpty()){
node = nodeQueue.poll();
node.left = generateNode(queue.poll());
node.right = generateNode(queue.poll());
if (node.left != null){
nodeQueue.add(node.left);
}
if (node.right != null){
nodeQueue.add(node.right);
}
}
return head;
}
private static Node generateNode(String poll) {
if (poll == null){
return null;
}
return new Node(Integer.valueOf(poll));
}
3.在1中的按层遍历中,我们只有一个树按层遍历的一个结果,并不能知道哪一层有多少个值,假如说我如果想要获取到树的最大宽度,我们应该怎么做呢?
我们可以在按层遍历的过程中,新增加几个变量来实现。
获取二叉树的最大宽度代码:
public int getTreeMaxWidth(Node head){
if (head == null){
return 0;
}
Queue<Node> queue = new LinkedList<>();
Node curEnd = head;
Node nextEnd = null;
queue.add(head);
int max = 0;
int count = 0; //当前节点数
while (!queue.isEmpty()){
Node poll = queue.poll();
count ++;
if (poll.left != null){
queue.add(poll.left);
nextEnd = poll.left;
}
if (poll.right != null){
queue.add(poll.right);
nextEnd = poll.right;
}
if (curEnd == poll){
if (count > max){
max = count;
}
count = 0;
curEnd = nextEnd;
}
}
return max;
}
curEnd用来保存当前最后一个节点,nextEnd用来保存下一层的最后一个节点,max表示最大的宽度,count表示当前的节点数,在队列中,我们弹一个数字,count++,当到当前节点是该层最后一个节点时,count和max比较,count 清零,把下一层最后一个节点nextEnd的值给当层的最后一个节点curEnd,开始下一层的遍历。