目录
一.树形dp套路
二.派对的最大快乐值
三.Morris遍历
morris先序遍历
morris中序遍历
moris后序遍历
判断是不是搜索二叉树
四.额外习题
一.树形dp套路
情况1:最大距离,节点X不参与。 => 左树最大距离 or 右树最大距离
情况2:最大距离,节点X参与。 => 最大距离 = 左树高度+1+右树高度
常见的分类,是头节点参不参与来罗列可能性。
可以设计:
节点的信息 => 最大距离maxDistance 、树高height节点cur时的最大距离 = max{left的maxDistance, right的maxDistance,left的height+right的height+1}
public static class Node { // 树节点
public int value; // 节点值
public Node left; // 左子树
public Node right; // 右子树
public Node(int data) { // 构造
this.value = data;
}
}
// 返回最大距离
public static int maxDistance(Node head) {
return process(head).maxDistance;
}
// 一个节点的返回信息
public static class ReturnType{
public int maxDistance; // 最大距离
public int h; // 高度
public ReturnType(int m, int h) { // 构造
this.maxDistance = m;;
this.h = h;
}
}
// 给出当前节点的返回信息(最大距离,高度)
public static ReturnType process(Node head) {
if(head == null) {
return new ReturnType(0,0);
}
ReturnType leftReturnType = process(head.left); // 左树返回信息
ReturnType rightReturnType = process(head.right); // 右数返回信息
int includeHeadDistance = leftReturnType.h + 1 + rightReturnType.h; // 计算包含根节点的最大距离
int p1 = leftReturnType.maxDistance; // 左子树最大距离
int p2 = rightReturnType.maxDistance; // 右子树最大举例
int resultDistance = Math.max(Math.max(p1, p2), includeHeadDistance); // 到当前位置可以获得的最大距离
int hitself = Math.max(leftReturnType.h, leftReturnType.h) + 1; // 高度
return new ReturnType(resultDistance, hitself); // 将信息放回给上一级
}
二.派对的最大快乐值
情况1:节点X参与
X参与的最大快乐 = X乐 + a不来的最大快乐 + b不来的最大快乐 + c不来的最大快乐
情况2:节点X不参与
X不参与的最大快乐 = 0 + max(a来的最大快乐,a不来的最大快乐) + max(b来的最大快乐,b不来的最大快乐) + max(c来的最大快乐,c不来的最大快乐)
public static class Employee{
public int happy;
public List<Employee> nexts;
}
public static class ReturnType{
public int no_maxhappy;
public int yes_maxhappy;
public ReturnType(int no, int yes) {
no_maxhappy=no;
yes_maxhappy=yes;
}
}
public static ReturnType process(Employee head) {
if(head.nexts.isEmpty()) { // 基层员工
return new ReturnType(0, head.happy);
}
int no_maxhappy=0; // head不来的最大快乐
int yes_maxhappy=head.happy; // head来的最大快乐
for(Employee next:head.nexts) {
ReturnType node=process(next);
yes_maxhappy+=node.no_maxhappy;
no_maxhappy+=Math.max(node.no_maxhappy, node.yes_maxhappy);
}
return new ReturnType(no_maxhappy, yes_maxhappy);
}
三.Morris遍历
笔试不要用Morris遍历,面试阶段用。(学术名词:线索二叉树)
【如果不用栈,如何回到上级节点,用底层节点大量的空闲指针】
标准1:如果没有左树 => 当前节点cur向右树走。
标准2:
如果有左树,找到左树中最右节点 => 最右节点右指针为空 => 右指针指向当前节点cur => 当前节点cur向左树走。
标准3:
如果有左树,找到左树中最右节点 => 最右节点右指针不为空 => 当前节点cur向右树走。
public static class Node {
public int value;
Node left;
Node right;
public Node(int data) {
this.value = data;
}
}
public static void morris(Node head) {
if (head == null) {
return;
}
Node cur1 = head;
Node cur2 = null;
while (cur1 != null) { // 过流程
cur2 = cur1.left; // cur2是cur左孩子
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
// cur2变成cur左子树上,最后的节点
if (cur2.right == null) { // 这是第一次来到cur
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else { // cur2.right == null
cur2.right = null;
}
}
cur1 = cur1.right;
}
}
单独遍历左子树有边界这件事,每个节点至多被遍历1次,总代价至多是O(n),并不会多严重。
morris先序遍历
- 只一次来到的节点 => 直接打印
- 来到两次的节点 => 第一次打印
public static void morrisPre(Node head) {
if (head == null) {
return;
}
Node cur1 = head;
Node cur2 = null;
while (cur1 != null) {
cur2 = cur1.left;
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
if (cur2.right == null) {
cur2.right = cur1;
System.out.print(cur1.value + " ");
cur1 = cur1.left;
continue;
} else {
cur2.right = null;
}
} else {
System.out.print(cur1.value + " ");
}
cur1 = cur1.right;
}
System.out.println();
}
morris中序遍历
- 只一次来到的节点 => 直接打印
- 来到两次的节点 => 第二次打印
public static void morrisIn(Node head) {
if (head == null) {
return;
}
Node cur1 = head;
Node cur2 = null;
while (cur1 != null) { // 过流程
cur2 = cur1.left; // cur2是cur左孩子
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
// cur2变成cur左子树上,最后的节点
if (cur2.right == null) { // 这是第一次来到cur
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else { // cur2.right == null
cur2.right = null;
}
}
System.out.print(cur1.value + " ");
cur1 = cur1.right;
}
System.out.println();
}
moris后序遍历
public static void morrisPos(Node head) {
if (head == null) {
return;
}
Node cur1 = head;
Node cur2 = null;
while (cur1 != null) {
cur2 = cur1.left;
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
if (cur2.right == null) {
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else {
cur2.right = null;
printEdge(cur1.left);
}
}
cur1 = cur1.right;
}
printEdge(head);
System.out.println();
}
public static void printEdge(Node head) {
Node tail = reverseEdge(head);
Node cur = tail;
while (cur != null) {
System.out.print(cur.value + " ");
cur = cur.right;
}
reverseEdge(tail);
}
public static Node reverseEdge(Node from) {
Node pre = null;
Node next = null;
while (from != null) {
next = from.right;
from.right = pre;
pre = from;
from = next;
}
return pre;
}
判断是不是搜索二叉树
在中序遍历的位置判断,是否preValue是一直在递增的。
public static boolean isBST(Node head) {
if (head == null) {
return true;
}
Node cur1 = head;
Node cur2 = null;
int preValue=Integer.MIN_VALUE;
while (cur1 != null) { // 过流程
cur2 = cur1.left; // cur2是cur左孩子
if (cur2 != null) {
while (cur2.right != null && cur2.right != cur1) {
cur2 = cur2.right;
}
// cur2变成cur左子树上,最后的节点
if (cur2.right == null) { // 这是第一次来到cur
cur2.right = cur1;
cur1 = cur1.left;
continue;
} else { // cur2.right == null
cur2.right = null;
}
}
if(cur1.value <= preValue) {
return false;
}
preValue = cur1.value;
cur1 = cur1.right;
}
return true;
}
如果方法必须要节点第3次遍历的强整合
=> 递归套路是最优解(非叶子节点到3次,叶子节点到1次)
如果方法没必要节点第3次遍历的强整合
=> morris遍历(非叶子节点可以到2次,叶子到1次)
四.额外习题
原问题:
使用位图,2^32个数需要2^32bit的空间 = 512MB
进阶问题: