树形DP

133 阅读5分钟

方法步骤

  1. 假设以X节点为头,假设可以向X左树和X右树要任何信息
  2. 在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
  3. 列出所有可能性后,确定到底需要向左树和右树要什么样的信息
  4. 把左树信息和右树信息求全集,就是任何一棵子树都需要返回的信息S
  5. 递归函数都返回S,每一棵子树都这么要求
  6. 写代码,在代码中考虑如何把左树的信息和右树信息整合出整棵树的信息

判断二叉树是否是搜索二叉树

public static boolean isBST2(Node head) {
   if (head == null) {
      return true;
   }
   return process(head).isBST;
}

// 判断当前树是否为搜索二叉树需要哪些信息?
// 1. 左右树是否都是bst
// 2. 左树最大值,是否小于头结点的值,右树最小值,是否大于头结点的值
public static class Info {
   public boolean isBST;
   public int max;
   public int min;

   public Info(boolean i, int ma, int mi) {
      isBST = i;
      max = ma;
      min = mi;
   }

}

public static Info process(Node x) {
   if (x == null) {
      return null; // Info不确定,返回空
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   Boolean isBST = true;
   int max = x.value,min = x.value;
   if(x.left != null) { // 可能返回空的leftInfo,所以要判断
      max = Math.max(max,leftInfo.max);
      min = Math.min(min,leftInfo.min);
      if(!leftInfo.isBST || leftInfo.max >= x.value) {
         isBST = false;
      }
   }
   if(x.right != null) {
      max = Math.max(max,rightInfo.max);
      min = Math.min(min,rightInfo.min);
      if(!rightInfo.isBST || rightInfo.min <= x.value) {
         isBST = false;
      }
   }
   return new Info(isBST,max,min);
}

判断二叉树是不是平衡二叉树

public static boolean isBalanced2(Node head) {
   return process(head).isBalanced;
}

// 判断当前树是否为平衡二叉树需要哪些信息?
// 1. 左右树是否都是平衡二叉树
// 2. 左树和又树高度是否相差小于等于 1
public static class Info{
   public boolean isBalanced;
   public int height;
   
   public Info(boolean i, int h) {
      isBalanced = i;
      height = h;
   }
}

public static Info process(Node x) {
   if(x == null) {
      return new Info(true, 0); // 空树是平衡二叉树,高度为0
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int height = Math.max(leftInfo.height, rightInfo.height)  + 1;
   boolean isBalanced = true;
   if(!leftInfo.isBalanced) {
      isBalanced = false;
   }
   if(!rightInfo.isBalanced) {
      isBalanced = false;
   }
   if(Math.abs(leftInfo.height - rightInfo.height) > 1) {
      isBalanced = false;
   }
   return new Info(isBalanced, height);
}

判断二叉树是不是满二叉树

// 第一种方法
// 收集整棵树的高度h,和节点数n
// 只有满二叉树满足 : 2 ^ h - 1 == n
public static boolean isFull1(Node head) {
   if (head == null) {
      return true;
   }
   Info1 all = process1(head);
   return (1 << all.height) - 1 == all.nodes;
}

public static class Info1 {
   public int height;
   public int nodes;

   public Info1(int h, int n) {
      height = h;
      nodes = n;
   }
}

public static Info1 process1(Node head) {
   if (head == null) {
      return new Info1(0, 0);
   }
   Info1 leftInfo = process1(head.left);
   Info1 rightInfo = process1(head.right);
   int height = Math.max(leftInfo.height, rightInfo.height) + 1;
   int nodes = leftInfo.nodes + rightInfo.nodes + 1;
   return new Info1(height, nodes);
}

// 第二种方法
// 收集子树是否是满二叉树
// 收集子树的高度
// 左树满 && 右树满 && 左右树高度一样 -> 整棵树是满的
public static boolean isFull2(Node head) {
   if (head == null) {
      return true;
   }
   return process2(head).isFull;
}

public static class Info2 {
   public boolean isFull;
   public int height;

   public Info2(boolean f, int h) {
      isFull = f;
      height = h;
   }
}

public static Info2 process2(Node h) {
   if (h == null) {
      return new Info2(true, 0);
   }
   Info2 leftInfo = process2(h.left);
   Info2 rightInfo = process2(h.right);
   boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
   int height = Math.max(leftInfo.height, rightInfo.height) + 1;
   return new Info2(isFull, height);
}

最大二叉搜索子树

public static int maxSubBSTSize2(Node head) {
   if(head == null) {
      return 0;
   }
   return process(head).maxBSTSubtreeSize;
}
// 1. 不考虑x,需要左右树的 maxBSTSubtreeSize
// 2. 考虑x,需要知道,带上x节点,整个树是不是bst,以及整个树的节点数
// 于是就需要,min,max,左右树是不是bst(可以省略化简掉),左右树的节点数
public static class Info {
   public int maxBSTSubtreeSize;
   public int allSize;
   public int max;
   public int min;

   public Info(int m, int a, int ma, int mi) {
      maxBSTSubtreeSize = m;
      allSize = a;
      max = ma;
      min = mi;
   }
}

public static Info process(Node x) {
   if (x == null) {
      return null;
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int max = x.value;
   int min = x.value;
   int allSize = 1;
   int p1 = -1,p2 = -1,p3 = -1;
   if(x.left != null) {
      max = Math.max(max,leftInfo.max);
      min = Math.min(min,leftInfo.min);
      allSize += leftInfo.allSize;
      p1 = leftInfo.maxBSTSubtreeSize;
   }
   if(x.right != null) {
      max = Math.max(max,rightInfo.max);
      min = Math.min(min,rightInfo.min);
      allSize += rightInfo.allSize;
      p2 = rightInfo.maxBSTSubtreeSize;
   }
   // 这里都是在判断,加上x是否为bst
   boolean isLeftBST = leftInfo == null ? true : (leftInfo.maxBSTSubtreeSize == leftInfo.allSize);
   boolean isRightBST = rightInfo == null ? true : (rightInfo.maxBSTSubtreeSize == rightInfo.allSize);
   if(isLeftBST && isRightBST) {
      boolean condition1 = leftInfo == null ? true : leftInfo.max < x.value;
      boolean condition2 = rightInfo == null ? true : rightInfo.min > x.value;
      if(condition1 && condition2) {
         p3 = allSize;
      }
   }
   return new Info(Math.max(p1,Math.max(p2,p3)),allSize,max,min);
}

二叉树的最大距离

给定一棵二叉树的头节点head,任何两个节点之间都存在距离,返回整棵二叉树的最大距离

public static int maxDistance2(Node head) {
   return process(head).maxDistance;
}

// 1. 不考虑x,左右树的最大距离求最大
// 2. 考虑x,需要知道左右树高度,二者较大加 1,就是通过x的最大距离
public static class Info {
   public int maxDistance;
   public int height;

   public Info(int m, int h) {
      maxDistance = m;
      height = h;
   }

}

public static Info process(Node x) {
   if (x == null) {
      return new Info(0, 0);
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int height = Math.max(leftInfo.height,rightInfo.height) + 1;
   int maxDistance = Math.max(Math.max(leftInfo.maxDistance,rightInfo.maxDistance),leftInfo.height + rightInfo.height + 1);
   return new Info(maxDistance,height);
}

最低公共祖先

给定一棵二叉树的头节点head,和另外两个节点a和b。返回a和b的最低公共祖先

public static Node lowestAncestor2(Node head, Node a, Node b) {
   return process(head, a, b).ans;
}

// 1. x不是答案,可能答案在左,也可能在右
// 2. x是答案,可能左树有a,右树有b,也可能x是a或b,左右树有a或b
public static class Info {
   public boolean findA;
   public boolean findB;
   public Node ans;

   public Info(boolean fA, boolean fB, Node an) {
      findA = fA;
      findB = fB;
      ans = an;
   }
}

public static Info process(Node x, Node a, Node b) {
   if(x == null) {
      return new Info(false,false,null);
   }
   Info leftInfo = process(x.left,a,b);
   Info rightInfo = process(x.right,a,b);
   Node ans = null;
   boolean findA = (x == a) || leftInfo.findA || rightInfo.findA;
   boolean findB = (x == b) || leftInfo.findB || rightInfo.findB;
   if(leftInfo.ans != null) {
      ans = leftInfo.ans;
   } else if(rightInfo.ans != null) {
      ans = rightInfo.ans;
   } else if(findA && findB) {  //这里else不能不写,因为如果左右树有答案,就和x无关
      ans = x;
   }
   return new Info(findA,findB,ans);
}

判断是否是完全二叉树

public static boolean isCBT2(Node head) {
   return process(head).isCBT;
}
// 如果是完全二叉树,右哪些可能性呢?
// 1. 左满右满 右比左高1 或 相等
// 2. 左完全右满 右比左高1
// 3. 左满右完全 左右高相等
public static class Info {
   public boolean isFull;
   public boolean isCBT;
   public int height;

   public Info(boolean full, boolean cbt, int h) {
      isFull = full;
      isCBT = cbt;
      height = h;
   }
}

public static Info process(Node x) {
   if (x == null) {
      return new Info(true, true, 0);
   }
   Info leftInfo = process(x.left);
   Info rightInfo = process(x.right);
   int height = Math.max(leftInfo.height,rightInfo.height) + 1;
   boolean isFull = leftInfo.isFull && rightInfo.isFull && leftInfo.height == rightInfo.height;
   boolean isCBT = false;
   if(leftInfo.isFull && rightInfo.isFull) {
      if(leftInfo.height == rightInfo.height + 1 || leftInfo.height == rightInfo.height) {
         isCBT = true;
      }
   }
   if(leftInfo.isCBT && rightInfo.isFull && leftInfo.height == rightInfo.height + 1) {
      isCBT = true;
   }
   if(leftInfo.isFull && rightInfo.isCBT && leftInfo.height == rightInfo.height) {
      isCBT = true;
   }
   return new Info(isFull,isCBT,height);
}

派对的最大快乐值

公司的每个员工都符合 Employee 类的描述。整个公司的人员结构可以看作是一棵标准的、 没有环的多叉树。树的头节点是公司唯一的老板。除老板之外的每个员工都有唯一的直接上级。 叶节点是没有任何下属的基层员工(subordinates列表为空),除基层员工外,每个员工都有一个或多个直接下级。 这个公司现在要办party,你可以决定哪些员工来,哪些员工不来,规则:

  1. 如果某个员工来了,那么这个员工的所有直接下级都不能来
  2. 派对的整体快乐值是所有到场员工快乐值的累加
  3. 你的目标是让派对的整体快乐值尽量大 给定一棵多叉树的头节点boss,请返回派对的最大快乐值。
public static int maxHappy2(Employee head) {
   Info allInfo = process(head);
   return Math.max(allInfo.no, allInfo.yes);
}

// 1. 当前员工来时,最大快乐值,是当前快乐值,加上下下属来时最大快乐值总和
// 2. 当前员工不来时,最大快乐值,是直接下属来时最大快乐值 和 不来时最大快乐值 取较大者的总和
public static class Info {
   public int no;
   public int yes;

   public Info(int n, int y) {
      no = n;
      yes = y;
   }
}

public static Info process(Employee x) {
   if (x == null) {
      return new Info(0, 0);
   }
   int no = 0;
   int yes = x.happy;
   for (Employee next : x.nexts) {
      Info nextInfo = process(next);
      no += Math.max(nextInfo.no, nextInfo.yes);
      yes += nextInfo.no;

   }
   return new Info(no, yes);
}