数据结构之巅:揭秘树的奥秘与力量
关于作者
- 作者介绍
🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名初入职场小白🎓、曾在校期间参加各种省赛、国赛,斩获一系列荣誉🏆
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨💻
二叉树
结构描述:
Class Node {
V value;
Node left;
Node right;
}
先序、中序、后序遍历
- 先序:任何子树的处理顺序都是,先头节点、再左子树、然后右子树
- 中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树
- 后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点
递归方式
- 理解递归序
- 先序、中序、后序都可以在递归序的基础上加工出来
- 第一次到达一个节点就打印就是先序、第二次打印即中序、第三次即后序
package com.cz.Tree;
/**
* @ProjectName: 递归版本遍历树
* @Package: com.cz.Tree
* @ClassName: RecursiveTraversalTree
* @Author: 张晟睿
* @Date: 2022/3/20 14:53
* @Version: 1.0
*/
public class RecursiveTraversalTree {
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
// 1
// 23
// 4567
pre(head);
System.out.println("先序========");
middle(head);
System.out.println("中序========");
post(head);
System.out.println("后序========");
}
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
public static void pre(Node head){
if(head == null) return;
System.out.print(head.value + " ");
pre(head.left);
pre(head.right);
}
public static void middle(Node head){
if(head == null) return;
middle(head.left);
System.out.print(head.value + " ");
middle(head.right);
}
public static void post(Node head){
if(head == null) return;
post(head.left);
post(head.right);
System.out.print(head.value + " ");
}
}
非递归方式
- 任何递归函数都可以改成非递归
- 自己设计压栈的来实现
package com.cz.Tree;
import java.util.Stack;
/**
* @ProjectName: Data_structure
* @Package: com.cz.Tree
* @ClassName: UnRecursiveTraversalTree
* @Author: 张晟睿
* @Date: 2022/3/20 16:06
* @Version: 1.0
*/
public class UnRecursiveTraversalTree {
public static void main(String[] args) {
Node1 head = new Node1(1);
head.left = new Node1(2);
head.right = new Node1(3);
head.left.left = new Node1(4);
head.left.right = new Node1(5);
head.right.left = new Node1(6);
head.right.right = new Node1(7);
pre(head);
System.out.println("========");
middle(head);
System.out.println("========");
post(head);
System.out.println("========");
}
public static class Node1 {
public int value;
public Node1 left;
public Node1 right;
public Node1(int val) {
value = val;
}
}
public static void pre(Node1 head) {
System.out.println("先序遍历:");
Stack<Node1> s = new Stack<>();
if(head != null) {
s.push(head);
while(!s.isEmpty()) {
Node1 node = s.pop();
System.out.print(node.value + " ");
if(node.right != null) s.push(node.right);
if(node.left != null) s.push(node.left);
}
}
System.out.println();
}
public static void middle(Node1 head){
System.out.println("中序遍历:");
if (head != null) {
Stack<Node1> s = new Stack<>();
while(!s.isEmpty() || head != null) {
//步骤1:如果头结点不为空的话,一直向左边执行
if (head != null) {
s.push(head);
head = head.left;
}else {//根节点打印后,来到右树,继续执行步骤1
head = s.pop();
System.out.print(head.value + " ");
head = head.right;
}
}
System.out.println();
}
}
public static void post(Node1 head){
System.out.println("后序遍历:");
if(head != null) {
Stack<Node1> s = new Stack<>();
s.push(head);
Node1 c = null; //指向栈顶的某个元素的位置
while(!s.isEmpty()) {
c = s.peek();
//判断c左孩子 是否已经处理过
if(c.left != null && head != c.left && head != c.right) {
s.push(c.left);
//判断c右孩子 是否已经处理过
}else if(c.right != null && head != c.right) {
s.push(c.right);
}else {
System.out.print(s.pop().value+" ");
head = c; //head用来记录上次打印的内容
}
}
System.out.println();
}
}
}
层序遍历
- 其实就是宽度优先遍历,用队列
- 可以通过设置flag变量的方式,来发现某一层的结束(看题目)
package com.cz.Tree;
import java.util.LinkedList;
import java.util.Queue;
/**
* @ProjectName: 层序遍历
* @Package: com.cz.Tree
* @ClassName: LevelTraversalTree
* @Author: 张晟睿
* @Date: 2022/4/7 21:04
* @Version: 1.0
*/
public class LevelTraversalTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
//层序遍历
public static void level(Node head) {
if(head == null) return ;
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while(!queue.isEmpty()) {
Node cur = queue.poll();
System.out.print(cur.value + ",");
if(cur.left != null) {
queue.add(cur.left);
}
if (cur.right != null) {
queue.add(cur.right);
}
}
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
level(head);
}
}
序列化和反序列化
- 可以用先序或者中序或者后序或者按层遍历,来实现二叉树的序列化
- 用了什么方式序列化,就用什么样的方式反序列化
package com.cz.Tree;
import java.util.LinkedList;
import java.util.Queue;
/**
* @ProjectName: 二叉树的序列化和反序列化
* @Package: com.cz.Tree
* @ClassName: SerializableTree
* @Author: 张晟睿
* @Date: 2022/4/8 10:58
* @Version: 1.0
*/
public class SerializableTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
//先序 序列化
public static Queue<String> preSerializable(Node head) {
Queue<String> ans = new LinkedList<>();
pre(head,ans);
return ans;
}
public static void pre(Node head, Queue<String> ans) {
if(head == null) ans.add(null);
else {
ans.add(String.valueOf(head.value));
pre(head.left,ans);
pre(head.right,ans);
}
}
//先序反序列化
public static Node buildByPreQueue(Queue<String> prelist){
if (prelist == null || prelist.size() <= 0) return null;
return preBuild(prelist);
}
public static Node preBuild(Queue<String> prelist) {
String val = prelist.poll();
if( val == null) return null;
Node head = new Node(Integer.valueOf(val));
head.left = preBuild(prelist);
head.right = preBuild(prelist);
return head;
}
//层序 序列化
public static Queue<String> levelSerializable(Node head){
Queue<String> ans = new LinkedList<>();
if(head == null) {
ans.add(null);
}else {
ans.add(String.valueOf(head.value));
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while(!queue.isEmpty()) {
head = queue.poll();
if (head.left != null) {
ans.add(String.valueOf(head.left.value));
queue.add(head.left);
}else {
ans.add(null);
}
if (head.right != null) {
ans.add(String.valueOf(head.right.value));
queue.add(head.right);
}else {
ans.add(null);
}
}
}
return ans;
}
public static Node buildByLevelQueue(Queue<String> levellist){
if(levellist == null || levellist.size() <= 0) return null;
Node head = generateNode(levellist.poll());
Queue<Node> queue = new LinkedList<>();
queue.add(head);
Node node = null;
while(!queue.isEmpty()) {
node = queue.poll();
node.left = generateNode(levellist.poll());
node.right = generateNode(levellist.poll());
if (node.left != null) {
queue.add(node.left);
}
if (node.right != null) {
queue.add(node.right);
}
}
return head;
}
public static Node generateNode(String val) {
if(val == null) return null;
return new Node(Integer.valueOf(val));
}
public static void prePrintf(Node head) {
Queue<Node> queue = new LinkedList<>();
queue.add(head);
while(!queue.isEmpty()) {
Node node = queue.poll();
System.out.print(node.value + ",");
if (node.left != null) queue.add(node.left);
if (node.right != null) queue.add(node.right);
}
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.right = new Node(7);
System.out.println("先序序列化:"+preSerializable(head));
System.out.print("先序反序列化:");
System.out.println(buildByPreQueue(preSerializable(head)));
System.out.println();
System.out.println("层序序列化:"+levelSerializable(head));
System.out.print("层序反序列化:");
System.out.println(buildByLevelQueue(levelSerializable(head)));
}
}
练习
1、设计一个打印整棵树的打印函数
package com.cz.Tree;
/**
* @ProjectName: Data_structure
* @Package: com.cz.Tree
* @ClassName: printBinaryTree
* @Author: 张晟睿
* @Date: 2022/4/9 16:07
* @Version: 1.0
*/
public class printBinaryTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
public static void printBinaryT(Node head) {
System.out.println("BinartTree:");
printInOrder(head, 0, "H", 17);
System.out.println();
}
/**
*
* @param head 树的节点
* @param height 当前节点的高度
* @param to 用一个字符来表示是左孩子(\)还是右孩子(/)还是头节点(H)
* @param len 规定每个节点值的宽度
*/
public static void printInOrder(Node head, int height, String to, int len){
if(head == null) return ;
printInOrder(head.right, height + 1, "/", len);
String val = to + head.value + to;
int lenM = val.length();
int lenL = (len - lenM) >> 1;
int lenR = len - lenM - lenL;
val = getspace(lenL) + val + getspace(lenR);
System.out.println(getspace(height * len) + val);
printInOrder(head.left, height + 1, "\\", len);
}
public static String getspace(int num){
String space = " ";
StringBuffer sb = new StringBuffer("");
for (int i = 0; i < num; i++) {
sb.append(space);
}
return sb.toString();
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
printBinaryT(head);
Node head1 = new Node(1);
head1.left = new Node(Integer.MIN_VALUE);
head1.right = new Node(7777);
head1.left.left = new Node(798);
head1.left.right = new Node(56);
head1.right.left = new Node(666666);
head1.right.right = new Node(520);
printBinaryT(head1);
}
}

2、求二叉树最宽的层有多少个节点
package com.cz.Tree;
import com.cz.Queuearray.LinkedListDemo;
import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
/**
* @ProjectName: Data_structure
* @Package: com.cz.Tree
* @ClassName: TreeMaxWidth
* @Author: 张晟睿
* @Date: 2022/4/7 21:14
* @Version: 1.0
*/
public class TreeMaxWidth {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int val) {
value = val;
}
}
public static int MaxWidthuseMap(Node head) {
if(head == null) return 0;
Queue<Node> queue = new LinkedList<>();
Map<Node, Integer> levelMap = new HashMap<>();
queue.add(head);
levelMap.put(head, 1);
int curLevel = 1;//记录当前统计的在那一层
int curLevelNode = 0;//记录curLevel层的宽度
int max = 0;
int curNodelevel = 1;
while (!queue.isEmpty()) {
Node cur = queue.poll();
curNodelevel = levelMap.get(cur);//当前节点在那一层
if (cur.left != null) {
levelMap.put(cur.left , curNodelevel + 1);
queue.add(cur.left);
}
if (cur.right != null) {
levelMap.put(cur.right, curNodelevel + 1);
queue.add(cur.right);
}
if (curNodelevel == curLevel) {//判断当前的层与统计的层在同一层,节点的个数+1
curLevelNode++;
}else {//当前的层与统计的层不在同一层
max = Math.max(max, curLevelNode);
curLevel++;//将统计的那一层下移
curLevelNode = 1;//当前下一层的第一个节点已经出了队列所以宽度为1
}
}
max = Math.max(max, curLevelNode);
System.out.println("curNodelevel = " + curNodelevel);
return max;
}
public static int MaxWidthnoMap(Node head) {
if(head == null) {
return 0;
}
Queue<Node> queue = new LinkedList<>();
queue.add(head);
Node curEnd = head; //记录当前统计层的最后节点
Node nextEnd = null;//记录当前统计层的下一层最后节点
int curLevelNodes = 0;//记录当前统计层的节点个数,也就是宽度
int max = 0;//用来存放最大宽度
while(!queue.isEmpty()) {
Node cur = queue.poll();
if (cur.left != null) {
queue.add(cur.left);
nextEnd = cur.left;
}
if (cur.right != null) {
queue.add(cur.right);
nextEnd = cur.right;
}
curLevelNodes++;
if(cur == curEnd) {
max = Math.max(max, curLevelNodes);
curLevelNodes = 0;
curEnd = nextEnd;
}
}
return max;
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
//暴力
System.out.println(MaxWidthuseMap(head));
System.out.println(MaxWidthnoMap(head));
}
}
3、给你二叉树中的某个节点,返回该节点的后继节点
二叉树结构如下定义(带父亲节点):
Class Node {
V value;
Node left;
Node right;
Node parent; //指向父节点
}
给你二叉树中的某个节点,返回该节点的后继节点
package com.cz.Tree;
/**
* @ProjectName: 中序遍历的后继节点
* @Package: com.cz.Tree
* @ClassName: SuccessOrTree
* @Author: 张晟睿
* @Date: 2022/4/10 16:50
* @Version: 1.0
*/
public class SuccessOrTree {
public static class Node {
public int value;
public Node left;
public Node right;
public Node parent;
public Node(int val) {
this.value = val;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}
//中序遍历 左 根 右
public static Node getSuccessOrTree(Node node){
if(node == null) return node;
if(node.right != null) { //该节点如果有右子树,即根 右(左根右) 找到右数上最左侧的节点,则后继节点就是右数上最左侧的节点
return goingLeft(node.right);
}
else { //该节点无右树,则来到左树
Node parent = node.parent;
while(parent != null && parent.right == node) { //如果当前节点是其父亲节点的,必须找到这个节点是他父亲节点的左孩子停止
node = parent;
parent = node.parent;
}
return parent;
}
}
public static Node goingLeft(Node node){
if (node == null) return node;
while(node.left != null) {
node = node.left;
}
return node;
}
public static void middle(Node head) {
if(head == null) return ;
middle(head.left);
System.out.print(head.value + " ");
middle(head.right);
}
public static void main(String[] args){
Node head = new Node(1);
head.left = new Node(2);
head.left.parent = head;
head.right = new Node(3);
head.right.parent = head;
head.left.left = new Node(4);
head.left.left.parent = head.left;
head.left.right = new Node(5);
head.left.right.parent = head.left;
head.right.right = new Node(7);
head.right.right.parent = head.right;
//中序遍历 4,2,5,1,3,7
System.out.println("中序遍历");
middle(head);
System.out.println();
System.out.println(getSuccessOrTree(head.left.right));
System.out.println(getSuccessOrTree(head.left.left));
System.out.println(getSuccessOrTree(head));
}
}
4、给定一个输入参数N,代表纸条都从下边向上方连续对折N次。 请从上到下打印所有折痕的方向
请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。 如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。
例如:N=1时,打印: down N=2时,打印: down down up
package com.cz.Tree;
/**
* @ProjectName: Data_structure
* @Package: com.cz.Tree
* @ClassName: PaperFolding
* @Author: 张晟睿
* @Date: 2022/4/10 17:23
* @Version: 1.0
*/
public class PaperFolding {
public static void printAllFolds(int N){
printProcess(1, N, true);
}
/**
* 递归过程
* @param i 节点的层数
* @param N 一共N层
* @param down down==true 凹 down==false 凸
*/
public static void printProcess(int i, int N, boolean down){
if (i > N) return ;
printProcess(i + 1, N , true);
System.out.println(down ? "凹" : "凸");
printProcess(i + 1, N , false);
}
public static void main(String[] args) {
int N = 3;
printAllFolds(N);
}
}