文章目录
- 一、二叉树的基本算法
- 1.二叉树的遍历
- 二、序列化与反序列化
- 三、怎样直观打印一棵树
- 四、给你二叉树中的某个节点,返回后继节点
- 五、打印折痕问题
- 六、二叉树的递归套路
- 1. 给定一颗二叉树的头结点head,返回这颗二叉树是不是平衡二叉树
- 2. 给定一颗二叉树的头节点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离
- 3.给定一颗二叉树的头节点head。返回这颗二叉树中最大的二叉搜索树的头节点
- 4.派对的最大快乐值
- 总结
一、二叉树的基本算法
1.二叉树的遍历
- 先序:任何子树的处理顺序都是先中结点,在左子树在右子树
- 中序:任何子树的处理顺序都是先左子树,在中间结点在右子树
- 后序:任何子树的处理顺序都是先左子树,在右子树在后中间结点
递归方法:略
非递归方法:
先序:弹出就打印,如有右孩子压入右孩子,如有左孩子压入左孩子
后序:方法一:弹出就入辅助栈,如有左孩子压入左孩子,如有右孩子压入右孩子。辅助栈依次弹出就是后序。方法二·:一个栈,三个逻辑分支,分别代表左数未处理,右树未处理,两数均处理过。
中序:整条左边界依次入栈,当左边界处理完之后弹出节点就打印之后来到弹出节点的右孩子,之后继续重复。
package Tree;
import java.util.Stack;
public class TreePrint {
public class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
public void printPreTree(Node root) {
if(root == null) return;
System.out.print(root.value+" ");
printPreTree(root.left);
printPreTree(root.right);
}
public void printInTree(Node root) {
if(root == null) return;
printInTree(root.left);
System.out.print(root.value+" ");
printInTree(root.right);
}
public void printBeTree(Node root) {
if(root == null) return;
printBeTree(root.left);
printBeTree(root.right);
System.out.print(root.value+" ");
}
public void printPre(Node root) {
System.out.println("先序非递归");
if(root != null) {
Stack<Node> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
Node cur = stack.pop(); //弹出即打印
System.out.print(cur.value+" ");
if(cur.right != null) {
stack.push(cur.right);
}
if(cur.left != null) {
stack.push(cur.left);
}
}
}
System.out.println();
}
public void printBe(Node root) { //双栈
System.out.println("后序非递归(双栈)");
if(root != null) {
Stack<Node> stack = new Stack<>();
Stack<Node> val = new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
Node cur = stack.pop(); //弹出即入栈
val.push(cur);
if(cur.left != null) {
stack.push(cur.left);
}
if(cur.right != null) {
stack.push(cur.right);
}
}
while(!val.isEmpty()) {
Node cur = val.pop();
System.out.print(cur.value + " ");
}
System.out.println();
}
}
public void printIn(Node root) {
System.out.println("中序非递归");
if(root != null) {
Stack<Node> stack = new Stack<>();
Node cur = root;
while(cur != null || !stack.isEmpty()) {
if(cur != null) {
stack.push(cur);
cur = cur.left;
}else {
cur = stack.pop();
System.out.print(cur.value + " ");
cur = cur.right;
}
}
System.out.println();
}
}
public void printBe2(Node root) {//单栈
System.out.println("后序非递归(单栈)");
if(root != null) {
Stack<Node> stack = new Stack<>();
stack.push(root);
Node last = root;
Node cur = null;
while(!stack.isEmpty()) {
cur = stack.peek();
if(cur.left != null && last != cur.left && last != cur.right) {
stack.push(cur.left);
}else if(cur.right != null && last != cur.right) {
stack.push(cur.right);
}else {
cur = stack.pop();
System.out.print(cur.value + " ");
last = cur;
}
}
}
System.out.println();
}
public static void main(String[] args) {
Node root = new TreePrint().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
TreePrint f = new TreePrint();
System.out.println("-----------------递归----------------");
System.out.println("先序");
f.printPreTree(root);
System.out.println();
System.out.println("中序");
f.printInTree(root);
System.out.println();
System.out.println("后序");
f.printBeTree(root);
System.out.println();
System.out.println("-----------------非递归----------------");
f.printPre(root);
f.printIn(root);
f.printBe(root);
f.printBe2(root);
}
}
运行结果
层次遍历方法:利用队列
public void print(Node root) {
if(root == null) return;
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
int i = 1;
while(!queue.isEmpty()) {
System.out.print("第"+i+"层:");
int size = queue.size();
for(int j=0; j<size; j++) {
Node cur = queue.poll();
System.out.print(cur.value+" ");
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
System.out.println();
++i;
}
}
运行结果
二、序列化与反序列化
- 可以用先序或者中序或者后序或者层次遍历,来实现二叉树的序列化
- 用了什么方式序列化,就用什么样的方式反序列化
package Tree;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class Serialized {
public class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
public void print(Node root) {
if(root == null) return;
Queue<Node> queue = new LinkedList<>();
queue.offer(root);
int i = 1;
while(!queue.isEmpty()) {
System.out.print("第"+i+"层:");
int size = queue.size();
for(int j=0; j<size; j++) {
Node cur = queue.poll();
System.out.print(cur.value+" ");
if(cur.left != null) {
queue.offer(cur.left);
}
if(cur.right != null) {
queue.offer(cur.right);
}
}
System.out.println();
++i;
}
}
public String serial(Node root) { // 前序
StringBuilder ans = new StringBuilder();
serialHelp(root, ans);
return ans.toString();
}
public void serialHelp(Node root, StringBuilder sb) {
if(root == null) {
sb.append("Null,");
}else {
sb.append(root.value+",");
serialHelp(root.left, sb);
serialHelp(root.right, sb);
}
}
public Node deSerial(String s) {
String[] serial = s.split(",");
List<String> serials = new LinkedList<String>(Arrays.asList(serial));
return deserialHelp(serials);
}
public Node deserialHelp(List<String> s) {
String cur = s.get(0);
s.remove(0);
if(cur.equals("Null")) {
return null;
}else {
Node root = new Node(Integer.parseInt(cur));
root.left = deserialHelp(s);
root.right = deserialHelp(s);
return root;
}
}
public static void main(String[] args) {
Node root = new Serialized().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
Serialized f = new Serialized();
System.out.println("------------序列化前(层次遍历)-------------------");
f.print(root);
System.out.println("----------------序列化后结果-----------------------");
String s = f.serial(root);
System.out.println(s);
System.out.println("----------------反序列化后结果---------------------");
f.print(f.deSerial(s));
}
}
运行结果
三、怎样直观打印一棵树
横着打
package Tree;
public class PrintTree {
public class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
public void printTree(Node root, char ch, int length, int level) {
if(root == null) {
return ;
}
printTree(root.left, '↓', length, level + 1); //打印左树 下箭头指出下层是父节点
String ans = ch + Integer.toString(root.value) + ch;
int space = (length - ans.length()) / 2; //为了对其,层占length长度
ans = getSpace(space) + ans;
ans = ans + getSpace(length - ans.length());
System.out.println(getSpace(length * level)+ans); //打印右树 下箭头指出下层是父节点
printTree(root.right, '↑', length, level + 1);
}
public String getSpace(int n) {
String ret = "";
for(int i=0; i<n; ++i) {
ret+=" ";
}
return ret;
}
public void print(Node root) {
printTree(root, 'H', 10, 0);
}
public static void main(String[] args) {
Node root = new PrintTree().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
PrintTree f = new PrintTree();
f.print(root);
}
}
运行结果
四、给你二叉树中的某个节点,返回后继节点
后继节点:二叉树中,中序遍历的结果中一个节点后面的节点为该节点的后继节点。
package Tree;
public class FindSeccess {
public class Node{
int value;
Node left;
Node right;
Node parent;
public Node(int value) {
this.value = value;
left = null;
right = null;
parent = null;
}
public Node insert(Node root, int val, Node parent) {
if(root == null) {
Node ret = new Node(val);
ret.parent = parent;
return ret;
}
if(root.value > val) {
root.left = insert(root.left, val, root);
}else {
root.right = insert(root.right, val, root);
}
return root;
}
public Node insert(Node root, int val) {
return insert(root, val, root);
}
}
public Node findSeccess(Node root) {
if(root == null) return null;
Node ret = null;
if(root.right != null) { // 若右子树不为空则后继节点为右子树上的最左节点
Node cur = root.right;
while(cur.left != null) {
cur = cur.left;
}
ret = cur;
}else { //若无右子树则在其祖宗节点中找到在其左子树上的节点
Node cur = root.parent;
Node child = root;
while(cur != null && cur.left != child) {
child = cur;
cur = cur.parent;
}
ret = cur;
}
return ret;
}
public static void main(String[] args) {
Node root = new FindSeccess().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
FindSeccess f = new FindSeccess();
Node ans = f.findSeccess(root);
System.out.println("10的后继为"+(ans == null? "null": ans.value));
ans = f.findSeccess(root.left);
System.out.println("4的后继为"+(ans == null? "null": ans.value));
ans = f.findSeccess(root.right);
System.out.println("20的后继为"+(ans == null? "null": ans.value));
ans = f.findSeccess(root.left.left);
System.out.println("0的后继为"+(ans == null? "null": ans.value));
}
}
运行结果:
五、打印折痕问题
解法:
package Tree;
public class PrintCrease {
public void printCrease(String s, int level, int n) {
if(level > n) return;
printCrease("Down", level+1, n);
System.out.print(" "+s+" ");
printCrease("Up", level+1, n);
}
public void printCrease(int n) {
printCrease("Down", 1, n);
}
public static void main(String[] args) {
PrintCrease f = new PrintCrease();
f.printCrease(3);
System.out.println();
f.printCrease(4);
}
}
运行结果
六、二叉树的递归套路
- 可以解决面试中绝大多数的二叉树问题,尤其是树形dp问题
- 本质是利用递归遍历二叉树的便利性
二叉树的递归套路
- 假设以X节点为头,假设可以向X左树和X右树要任何信息
- 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
- 列出所有可能性后,确定到底需要向左树和右树要什么样的信息。(和X无关,和X有关)
- 把左树信息和右树信息求全集,就是任何一颗子树都需要返回信息S
- 递归函数都返回S,每一颗树都这么要求
- 写代码,在代码中考虑如何把左树的信息和右树的信息整合出整颗树的信息。
1. 给定一颗二叉树的头结点head,返回这颗二叉树是不是平衡二叉树
package Tree;
import Tree.PrintTree.Node;
public class IsBalance {
public class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
//可视化
public void printTree(Node root, char ch, int length, int level) {
if(root == null) {
return ;
}
printTree(root.left, '↓', length, level + 1); //打印左树 下箭头指出下层是父节点
String ans = ch + Integer.toString(root.value) + ch;
int space = (length - ans.length()) / 2; //为了对其,层占length长度
ans = getSpace(space) + ans;
ans = ans + getSpace(length - ans.length());
System.out.println(getSpace(length * level)+ans); //打印右树 下箭头指出下层是父节点
printTree(root.right, '↑', length, level + 1);
}
public String getSpace(int n) {
String ret = "";
for(int i=0; i<n; ++i) {
ret+=" ";
}
return ret;
}
public void print(Node root) {
printTree(root, 'H', 10, 0);
}
public info isBalance(Node root) {
if(root == null) { //空树为平衡树,高度为0
return new info(true, 0);
}
info ret;
info info_left = isBalance(root.left); //左树状况
info info_right = isBalance(root.right); //右树状况
if(info_left.isBalance && info_right.isBalance && Math.abs(info_left.height - info_right.height) <= 1) { //子树是平衡的且子树高度差小于等于1
int maxheight = info_left.height > info_right.height? info_left.height: info_right.height;
ret = new info(true, maxheight + 1);
}else {
int maxheight = info_left.height > info_right.height? info_left.height: info_right.height;
ret = new info(false, maxheight + 1);
}
return ret;
}
public static void main(String[] args) {
Node root = new IsBalance().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
IsBalance f = new IsBalance();
System.out.println("------------------树结构-------------------");
f.print(root);
System.out.println("\n" + f.isBalance(root));
root = new IsBalance().new Node(10);
root.insert(root, 0).insert(root, 11);
System.out.println("\n------------------树结构-------------------");
f.print(root);
System.out.println("\n" + f.isBalance(root));
}
}
class info{ //子树返回的节点
boolean isBalance; //是否为平衡树
int height; //树高
public info(boolean isBalance, int height) {
this.isBalance = isBalance;
this.height = height;
}
public String toString() {
return "结果:isBalance:" + this.isBalance + " height:" + this.height;
}
}
运行结果
2. 给定一颗二叉树的头节点head,任何两个节点之间都存在距离,返回整颗二叉树的最大距离
package Tree;
public class GetMaxDistance {
public class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
public infom getMaxDistance(Node root) {
if(root == null) {
return new infom(0 ,0);
}
infom info_left = getMaxDistance(root.left);
infom info_right = getMaxDistance(root.right);
int max_Distance = info_left.maxDistance > info_right.maxDistance? info_left.maxDistance: info_right.maxDistance; //子树中最长距离
max_Distance = max_Distance > info_left.height + info_right.height + 1? max_Distance: info_left.height + info_right.height + 1; //包括头节点的距离和子树中最长距离进行比较
int max_height = info_left.height > info_right.height? info_left.height: info_right.height;
return new infom(max_Distance, max_height + 1);
}
public static void main(String[] args) {
Node root = new GetMaxDistance().new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
GetMaxDistance f = new GetMaxDistance();
System.out.println(f.getMaxDistance(root));
}
}
class infom{ //获得子树上节点的最大距离,以及高度
int maxDistance; //子树上的最大距离
int height; //子树高度
public infom(int maxDistance, int height) {
this.maxDistance = maxDistance;
this.height = height;
}
public String toString() {
return "最大距离:" + this.maxDistance + "树高:" + this.height;
}
}
运行结果:解释: 17 -> 15 -> 20 -> 10 -> 4 -> 8 -> 5 -> 7一共8个节点
3.给定一颗二叉树的头节点head。返回这颗二叉树中最大的二叉搜索树的头节点
package Tree;
public class GetMaxBinarySearchTree {
public infoma getTree(Node root) {
if(root == null) {
return null;
}
infoma left = getTree(root.left);
infoma right = getTree(root.right);
boolean isAllBST = false;
int maxSubBSTSize = 0;
Node head = null;
int max = root.value;
int min = root.value;
if(left != null) {
max = Math.max(max, left.max);
min = Math.min(min, left.min);
maxSubBSTSize = left.cnt;
head = left.head;
}
if(right != null) {
max = Math.max(max, right.max);
min = Math.min(min, right.min);
if(maxSubBSTSize < right.cnt) {
maxSubBSTSize = right.cnt;
head = right.head;
}
}
if( (left == null ? true: left.isSearchTree)
&&
(right == null ? true: right.isSearchTree)
&&
(left == null ? true: left.max < root.value)
&&
(right == null ? true: right.min > root.value)
) {
int leftSize = left == null? 0: left.cnt;
int rightSize = right == null? 0: right.cnt;
maxSubBSTSize = leftSize + rightSize + 1;
isAllBST = true;
head = root;
}
return new infoma(isAllBST, min, max, maxSubBSTSize, head);
}
public static void main(String[] args) {
Node root = new Node(10);
root.insert(root, 4).insert(root, 8).insert(root, 20).insert(root, 15).insert(root, 0).insert(root, 5).insert(root, 7).insert(root, 17);
GetMaxBinarySearchTree f = new GetMaxBinarySearchTree();
System.out.println(f.getTree(root));
}
}
class infoma{
boolean isSearchTree; //是否为二叉树
int min; //树上最小值
int max; //树上最大值
int cnt; //最大搜索树的大小
Node head; //最大搜索树的头节点
public infoma(boolean isSearchTree, int min, int max, int cnt, Node head) {
this.isSearchTree = isSearchTree;
this.min = min;
this.max = max;
this.cnt = cnt;
this.head = head;
}
public void printTree(Node root, char ch, int length, int level) {
if(root == null) {
return ;
}
printTree(root.left, '↓', length, level + 1); //打印左树 下箭头指出下层是父节点
String ans = ch + Integer.toString(root.value) + ch;
int space = (length - ans.length()) / 2; //为了对其,层占length长度
ans = getSpace(space) + ans;
ans = ans + getSpace(length - ans.length());
System.out.println(getSpace(length * level)+ans); //打印右树 下箭头指出下层是父节点
printTree(root.right, '↑', length, level + 1);
}
public String getSpace(int n) {
String ret = "";
for(int i=0; i<n; ++i) {
ret+=" ";
}
return ret;
}
public void print(Node root) {
printTree(root, 'H', 10, 0);
}
public String toString() {
//最大搜索树结构
System.out.println("-----最大搜索树结构-------");
this.print(head);
return "最大搜索树的大小" + this.cnt;
}
}
class Node{
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
left = null;
right = null;
}
public Node insert(Node root, int val) {
if(root == null) {
return new Node(val);
}
if(root.value > val) {
root.left = insert(root.left, val);
}else {
root.right = insert(root.right, val);
}
return root;
}
}
运行结果
4.派对的最大快乐值
员工信息如下:问题描述:
思路:两种情况,分别为头结点来与头结点不来
package Tree;
import java.util.ArrayList;
import java.util.List;
public class GetMaxHappy {
public Infomation getMaxHappy(Employee boss) {
int no = 0; //不来的最大值
int yes = boss.happy; //来的最大值
List<Employee> nolist = new ArrayList<>();
List<Employee> yeslist = new ArrayList<>();
yeslist.add(boss);
for(Employee e: boss.subordinates) {
Infomation info = getMaxHappy(e);
no += (info.no > info.yes? info.no: info.yes);
nolist.addAll((info.no > info.yes? info.nolist: info.yeslist));
yes += info.no;
yeslist.addAll(info.nolist);
}
return new Infomation(no, yes, nolist, yeslist);
}
public String getHappy(Employee boss) {
Infomation info = getMaxHappy(boss);
String ans = "最大快乐值为:" + (info.no > info.yes? info.no: info.yes) + " 来的员工为:";
for(Employee e: (info.no > info.yes? info.nolist: info.yeslist)) {
ans += " " + e.happy;
}
return ans;
}
public static void main(String[] args) {
Employee boss = new Employee(1);
boss.subordinates.add(new Employee(2));
boss.subordinates.add(new Employee(3));
boss.subordinates.add(new Employee(4));
boss.subordinates.add(new Employee(5));
boss.subordinates.add(new Employee(6));
boss.subordinates.add(new Employee(7));
Employee e = new Employee(8);
boss.subordinates.add(e);
e.subordinates.add(new Employee(9));
e.subordinates.add(new Employee(10));
e.subordinates.add(new Employee(11));
e.subordinates.add(new Employee(12));
e.subordinates.add(new Employee(13));
GetMaxHappy f = new GetMaxHappy();
System.out.println(f.getHappy(boss));
}
}
class Infomation{
int no; //来的快乐值
int yes;//不来的快乐值
List<Employee> nolist;
List<Employee> yeslist;
public Infomation(int no, int yes, List<Employee> nolist, List<Employee> yeslist) {
this.no = no;
this.yes = yes;
this.nolist = nolist;
this.yeslist = yeslist;
}
}
class Employee{
public int happy; // 这名员工可以带来的快乐值
List<Employee> subordinates; //这名员工有哪些直接下属
public Employee(int happy){
this.happy = happy;
this.subordinates = new ArrayList<>();
}
}
运行结果:
总结
二叉树的递归套路
- 假设以X节点为头,假设可以向X左树和X右树要任何信息
- 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
- 列出所有可能性后,确定到底需要向左树和右树要什么样的信息。(和X无关,和X有关)
- 把左树信息和右树信息求全集,就是任何一颗子树都需要返回信息S
- 递归函数都返回S,每一颗树都这么要求
- 写代码,在代码中考虑如何把左树的信息和右树的信息整合出整颗树的信息。