x.找到二叉树中符合搜索二叉树条件最大拓扑结构
要点
解法 - 1
以每一个节点为最大拓扑结构的根结点,找到这棵构成的搜索二叉树的节点个数。
代码片段
public int bstTopoSize(TreeNode head) {
if (head == null) {//0.终止条件
return 0;
}
int max = maxTopo(head, head);//1.找到以根节点开始的搜索二叉树
max = Math.max(bstTopoSize(head.left), max);//2.找到左侧最大搜索二叉树
max = Math.max(bstTopoSize(head.right), max);//3.找到右侧最大
return max;//5.返回最大值
}
public int maxTopo(TreeNode head, TreeNode cur) {
if (head != null && cur != null && isBSTNode(head, cur)) {//10.如果 cur.val 这个节点在这个搜索二叉树中找到了,继续找cur这个节点的左右子树一共有多少个节点符合。
return maxTopo(head, cur.left) + maxTopo(head, cur.right) + 1;//11.左、右符合的节点数+已经确定的一个元素 cur
}
return 0;//12.找不到符合的节点直接返回0
}
public boolean isBSTNode(TreeNode head, TreeNode cur) {//6.判断cur这个节点是不是符合在 head这个搜索二叉树
if (head == null) {//7.如果head已经是空了,cur肯定不属于head,因为cur肯定不为空啊
return false;
}
if (head.val == cur.val) {//8.当找到了值,则说明这个节点是成立的。
return true;
}
return isBSTNode(head.val > cur.val ? head.left : head.right, cur);//9.看看当前cur.val应该在哪个子树,就继续在这个子树中寻找。
}
代码解释
这里我们通过判断每一个节点的位置是否符合搜索二叉树来计算节点个数。
你会很容易发现有很多的节点已经重复计算了。
比如:
6 > 12 > 13
这条路径已经成立了
12 > 13
也已经成立的,然而我们递归的过程中放弃了这个结果并重复计算。
解法 - 2
拓扑贡献记录: 由下往上 每次记录当前结点的左、右子树符合 搜索二叉树 的节点个数
这里采取后序遍历的方法来实现 由下往上(左右中) 更新拓扑节点数。
代码片段
public class Record {//0.记录当前节点来自左、右子树的拓扑贡献记录,
public int l;
public int r;
public Record(int left, int right) {
this.l = left;
this.r = right;
}
}
public int bstTopoSize2(TreeNode head) {
HashMap<TreeNode, Record> hashMap = new HashMap<>();//1.存放当前节点对应的贡献记录
return bstTopoSize2Helper(head, hashMap);//2.求解结果并返回
}
public int bstTopoSize2Helper(TreeNode head, HashMap<TreeNode, Record> hashMap) {
if (head == null) {//3.递归终止条件。
return 0;
}
int ls = bstTopoSize2Helper(head.left, hashMap);//4.求出左边的符合搜索二叉树条件的最大节点数。
int rs = bstTopoSize2Helper(head.right, hashMap);//5.求出右边的符合搜索二叉树条件的最大节点数。
modifyMap(head.left, head.val, hashMap, true);//6.修改左边的贡献记录
modifyMap(head.right, head.val, hashMap, false);//7.修改右边的贡献记录
Record lRecord = hashMap.get(head.left);//13.获取左边最新的拓扑贡献记录
Record rRecord = hashMap.get(head.right);//14.获取左边最新的拓扑贡献记录
int lBST = lRecord == null ? 0 : lRecord.l + lRecord.r + 1;//15.有记录就取记录,没有就默认0
int rBST = rRecord == null ? 0 : rRecord.l + rRecord.r + 1;//16.有记录就取记录,没有就默认0
hashMap.put(head, new Record(lBST, rBST));//17.记录此节点目前的拓扑贡献记录
return Math.max(lBST + rBST + 1, Math.max(ls, rs));//18.通知上层现在能构成最大搜索二叉树的节点个数是以根节点构成的还是左、右子树
}
public int modifyMap(TreeNode head, int value, HashMap<TreeNode, Record> hashMap, boolean s) {
if (head == null || !hashMap.containsKey(head)) {//8.如果已经到空节点,或者这个节点还没有存放拓扑记录直接返回0
return 0;
}
Record record = hashMap.get(head);//9.获取目前这一侧子树的拓扑记录,开始准备处理更新记录
if ((s && head.val > value) || ((!s) && head.val < value)) {
hashMap.remove(head);
return record.l + record.r + 1;
}
int minus = modifyMap(s ? head.right : head.left, value, hashMap, s);//10.得到当前需要移除的记录,并在后序运算之后更新记录
if (s) {//10.1 这条记录在左侧,运算更新记录右边的记录。
record.r = record.r - minus;//10.1
} else {//10.2 这条记录在右侧,运算更新记录左边的记录。
record.l = record.l - minus;
}
hashMap.put(head, record);//11.更新记录
return minus;//12.返回上一层,通知上层也应该减去这些节点数。
}
结尾
1.博客地址
2.源代码仓库
如果你在代码里看到了用 数字标记的注释 如 //1.xxx 这是我写代码的顺序,希望能给你一点启发。