求二叉樹中最大的二叉搜索子樹的頭節點
作者:Grey
原文地址:
題目描述
給定一棵二叉樹的頭節點head, 返回這顆二叉樹中最大的二叉搜索子樹的頭節點。
暴力解法
定義遞歸函數
TreeNode maxSubBSTHead1(TreeNode head)
遞歸含義表示:以head
爲頭的二叉樹中最大的二叉搜索子樹的頭結點是什麼。
接下來是 base case,
if (head == null) {
return null;
}
定義一個輔助函數getBSTSize(head)
,這個函數表示:如果以head
爲頭的樹是二叉搜索樹,則返回其大小,否則返回 0。
getBSTSize(head)
的實現思路也比較簡單,即通過中序遍歷收集以 head 爲頭的樹,如果這個樹滿足二叉搜索子樹,則返回二叉搜索子樹的大小,如果以 head 的頭不是二叉搜索樹,直接返回 0。
代碼如下
public static int getBSTSize(TreeNode head) {
if (head == null) {
return 0;
}
ArrayList<TreeNode> arr = new ArrayList<>();
// 中序遍歷收集以 head 爲頭的二叉樹,存在數組中
in(head, arr);
for (int i = 1; i < arr.size(); i++) {
if (arr.get(i).val <= arr.get(i - 1).val) {
return 0;
}
}
return arr.size();
}
實現瞭如上方法,主函數直接做如下調用即可,代碼說明見註釋:
public static TreeNode maxSubBSTHead1(TreeNode head) {
if (head == null) {
return null;
}
// 以 head 爲頭的二叉搜索子樹大小不爲0,說明head這就是最大的二叉搜索子樹頭!
if (getBSTSize(head) != 0) {
return head;
}
// 去左樹上找哪個結點是最大二叉搜索子樹的頭結點
TreeNode leftAns = maxSubBSTHead1(head.left);
// 去右樹上找哪個結點是最大二叉搜索子樹的頭結點
TreeNode rightAns = maxSubBSTHead1(head.right);
// 左右樹哪個二叉搜索子樹更大,就返回哪個結點
return getBSTSize(leftAns) >= getBSTSize(rightAns) ? leftAns : rightAns;
}
二叉樹的遞歸套路
定義如下數據結構
public static class Info {
public TreeNode maxSubBSTHead;
public int maxSubBSTSize;
public int min;
public int max;
public Info(TreeNode h, int size, int mi, int ma) {
maxSubBSTHead = h;
maxSubBSTSize = size;
min = mi;
max = ma;
}
}
針對每一顆子樹,都有如上結構信息,其中
maxSubBSTHead: 表示某個子樹的最大二叉搜索子樹的頭結點
maxSubBSTSize: 表示某個結點如果是二叉搜索樹,其大小爲多少
min:表示以某個結點爲頭的樹的最小值是多少
max:表示以某個結點爲頭的樹的最大值是多少
接下來定義遞歸函數
Info process(TreeNode X)
以 X 爲頭的樹,返回對應的 Info
接下來整理可能性
-
如果
X == null
則直接返回null
,即 base case; -
接下來問左樹要 Info 信息,再問右樹要 Info 信息,整合成 head 的 info 信息,以代碼註釋來說明
// 問左樹要信息
Info leftInfo = process(X.left);
// 問右樹要信息
Info rightInfo = process(X.right);
int min = X.val;
int max = X.val;
TreeNode maxSubBSTHead = null;
int maxSubBSTSize = 0;
if (leftInfo != null) {
// 左樹信息不爲 null
// 則 head.val 和 左樹的min PK,誰小誰是以head 爲頭的min 信息
min = Math.min(min, leftInfo.min);
// 則 head.val 和 左樹的max PK,誰大誰是以head 爲頭的max 信息
max = Math.max(max, leftInfo.max);
// 以 head 爲頭的最大二叉搜索子樹的頭結點至少是leftInfo.maxSubBSTHead
maxSubBSTHead = leftInfo.maxSubBSTHead;
// 以 head 爲頭的最大二叉搜索子樹的頭結點大小至少是leftInfo.maxSubBSTSize
maxSubBSTSize = leftInfo.maxSubBSTSize;
}
if (rightInfo != null) {
// 右樹信息不爲 null
// 思路和 左樹信息不爲 null 一樣
min = Math.min(min, rightInfo.min);
max = Math.max(max, rightInfo.max);
if (rightInfo.maxSubBSTSize > maxSubBSTSize) {
maxSubBSTHead = rightInfo.maxSubBSTHead;
maxSubBSTSize = rightInfo.maxSubBSTSize;
}
}
// 到了這一步,說明 leftInfo 和 rightInfo 至少有一個爲 null
// 不管哪個爲null,如果要以 X 爲最大二叉搜索子樹的頭結點,則需要滿足以下條件
// 1. leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.val
// 2. rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.val
if ((leftInfo == null || (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.val))
&& (rightInfo == null || (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.val))) {
maxSubBSTHead = X;
maxSubBSTSize =
(leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize)
+ 1;
}
return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
兩個思路完整代碼如下(含測試代碼)
import java.util.ArrayList;
public class Code_MaxSubBSTHead {
public static class TreeNode {
public int val;
public TreeNode left;
public TreeNode right;
public TreeNode(int data) {
this.val = data;
}
}
public static int getBSTSize(TreeNode head) {
if (head == null) {
return 0;
}
ArrayList<TreeNode> arr = new ArrayList<>();
in(head, arr);
for (int i = 1; i < arr.size(); i++) {
if (arr.get(i).val <= arr.get(i - 1).val) {
return 0;
}
}
return arr.size();
}
public static void in(TreeNode head, ArrayList<TreeNode> arr) {
if (head == null) {
return;
}
in(head.left, arr);
arr.add(head);
in(head.right, arr);
}
public static TreeNode maxSubBSTHead1(TreeNode head) {
if (head == null) {
return null;
}
if (getBSTSize(head) != 0) {
return head;
}
TreeNode leftAns = maxSubBSTHead1(head.left);
TreeNode rightAns = maxSubBSTHead1(head.right);
return getBSTSize(leftAns) >= getBSTSize(rightAns) ? leftAns : rightAns;
}
public static TreeNode maxSubBSTHead2(TreeNode head) {
if (head == null) {
return null;
}
return process(head).maxSubBSTHead;
}
// 每一棵子樹
public static class Info {
public TreeNode maxSubBSTHead;
public int maxSubBSTSize;
public int min;
public int max;
public Info(TreeNode h, int size, int mi, int ma) {
maxSubBSTHead = h;
maxSubBSTSize = size;
min = mi;
max = ma;
}
}
public static Info process(TreeNode X) {
if (X == null) {
return null;
}
Info leftInfo = process(X.left);
Info rightInfo = process(X.right);
int min = X.val;
int max = X.val;
TreeNode maxSubBSTHead = null;
int maxSubBSTSize = 0;
if (leftInfo != null) {
min = Math.min(min, leftInfo.min);
max = Math.max(max, leftInfo.max);
maxSubBSTHead = leftInfo.maxSubBSTHead;
maxSubBSTSize = leftInfo.maxSubBSTSize;
}
if (rightInfo != null) {
min = Math.min(min, rightInfo.min);
max = Math.max(max, rightInfo.max);
if (rightInfo.maxSubBSTSize > maxSubBSTSize) {
maxSubBSTHead = rightInfo.maxSubBSTHead;
maxSubBSTSize = rightInfo.maxSubBSTSize;
}
}
if ((leftInfo == null ? true : (leftInfo.maxSubBSTHead == X.left && leftInfo.max < X.val))
&& (rightInfo == null
? true
: (rightInfo.maxSubBSTHead == X.right && rightInfo.min > X.val))) {
maxSubBSTHead = X;
maxSubBSTSize =
(leftInfo == null ? 0 : leftInfo.maxSubBSTSize)
+ (rightInfo == null ? 0 : rightInfo.maxSubBSTSize)
+ 1;
}
return new Info(maxSubBSTHead, maxSubBSTSize, min, max);
}
// for test
public static TreeNode generateRandomBST(int maxLevel, int maxValue) {
return generate(1, maxLevel, maxValue);
}
// for test
public static TreeNode generate(int level, int maxLevel, int maxValue) {
if (level > maxLevel || Math.random() < 0.5) {
return null;
}
TreeNode head = new TreeNode((int) (Math.random() * maxValue));
head.left = generate(level + 1, maxLevel, maxValue);
head.right = generate(level + 1, maxLevel, maxValue);
return head;
}
public static void main(String[] args) {
int maxLevel = 4;
int maxValue = 100;
int testTimes = 1000000;
for (int i = 0; i < testTimes; i++) {
TreeNode head = generateRandomBST(maxLevel, maxValue);
if (maxSubBSTHead1(head) != maxSubBSTHead2(head)) {
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}