找到二叉树中符合搜索二叉树条件的最大拓扑结构
找到二叉树中符合搜索二叉树条件的最大拓扑结构#
《程序员代码面试指南》第39题 P124 难度:校★★★☆
本题有两种解法,时间复杂度分别为O(N²)和O(N)。
首先来看方法一,核心思路就是把整个二叉树中每个节点都作为一次头节点,寻找以它为头节点的情况下的最大搜索二叉树拓扑结构。其中最大的那个就是我们想找的结构。
直接来看代码:
public int bstTopoSize1(Node head) {
if (head == null) {
return 0;
}
int max = maxTopo(head, head);
max = Math.max(bstTopoSize1(head.left), max);
max = Math.max(bstTopoSize1(head.right), max);
return max;
}
public int maxTopo(Node h, Node n) {
if (h != null && n != null && isBSTNode(h, n, n.value)) {
return maxTopo(h, n.left) + maxTopo(h, n.right) + 1;
}
return 0;
}
public boolean isBSTNode(Node h, Node n, int value) {
if (h == null) {
return false;
}
if (h == n) {
return true;
}
return isBSTNode(h.value > value ? h.left : h.right, n, value);
}
算法用到了三层递归。
第一层递归如上所述,就是以各个节点为头节点的情况下来对比各种情况下的搜索二叉树最大拓扑结构。并且不断的取最大值,最后返回的值就是整个二叉树中符合条件的最大值。
第二层递归则是在以当前节点为头节点的情况下,寻找最大拓扑结构。如果一个节点不满足条件,则不再往其子节点寻找。反之,则一直沿着子树往下寻找。
第三层递归就是判断当前节点是否可以作为这个拓扑的一部分。即从当前头节点出发,按照二叉搜索的方式移动。能找到当前节点,则返回true。不能则返回false。
三层递归的关系也很好理解。第一层递归需要用到什么?需要用到以各个节点为头节点的情况下的最大拓扑结构的节点数。这就是第二层递归。而第二层递归需要用到什么?需要用到判断每个节点是否可以作为这个拓扑的一部分。这就是第三层递归。而第三层递归就是最内层,从当前头节点开始不断往下递归寻找当前节点,来返回true或者false。
时间复杂度为O(N)的第二种方法比较复杂。
主要是用到了一个重要的概念——拓扑贡献记录(2个值分别代表左、右子树可以贡献多少个节点)
递归的过程中不断更新这个拓扑贡献记录,对于当前头节点,左子树只需要不断考查其右边界、右子树只需要不断考查其左边界。
从小树的记录整合成大树的记录,从而求出整棵树中符合搜索二叉树条件的最大拓扑的大小。
整个过程大体说来就是利用二叉树的后序遍历,对每个节点来说,首先生成其左孩子节点的记录,然后是右孩子节点的记录,接着把两组记录修改成以这个节点为头的拓扑贡献记录,并找出所有节点的最大拓扑结构中最大的那个。
详细解释见书P127-131,代码如下:
public class Record {
public int l;
public int r;
public Record(int left, int right) {
this.l = left;
this.r = right;
}
}
public int bstTopoSize2(Node head) {
Map<Node, Record> map = new HashMap<Node, Record>();
return posOrder(head, map);
}
public int posOrder(Node h, Map<Node, Record> map) {
if (h == null) {
return 0;
}
int ls = posOrder(h.left, map);
int rs = posOrder(h.right, map);
modifyMap(h.left, h.value, map, true);
modifyMap(h.right, h.value, map, false);
Record lr = map.get(h.left);
Record rr = map.get(h.right);
int lbst = lr == null ? 0 : lr.l + lr.r + 1;
int rbst = rr == null ? 0 : rr.l + rr.r + 1;
map.put(h, new Record(lbst, rbst));
return Math.max(lbst + rbst + 1, Math.max(ls, rs));
}
public int modifyMap(Node n, int v, Map<Node, Record> m, boolean s) {
if (n == null || (!m.containsKey(n))) {
return 0;
}
Record r = m.get(n);
if ((s && n.value > v) || ((!s) && n.value < v)) {
m.remove(n);
return r.l + r.r + 1;
} else {
int minus = modifyMap(s ? n.right : n.left, v, m, s);
if (s) {
r.r = r.r - minus;
} else {
r.l = r.l - minus;
}
m.put(n, r);
return minus;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?