在二叉树中找到两个节点的最近公共祖先 & 判断一棵二叉树是否为搜索二叉树和完全二叉树
在二叉树中找到两个节点的最近公共祖先#
《程序员代码面试指南》第49题 P155 难度:原问题 士★☆☆☆ 进阶问题 尉★★☆☆
原问题解法,后序遍历二叉树,假设遍历到的当前节点为cur。因为是后序遍历,所以先处理cur的两棵子树。假设处理cur左子树时返回节点为left,处理右子树时返回节点为right。
- 如果cur==null,或者o1、o2,则返回cur。
- 如果left和right都为空,说明cur整棵子树上没有发现过o1或o2,返回null。
- 如果left和right都不为空,说明左子树上发现过o1或o2,右子树上也发现过o2或o1,说明o1向上与o2向上的过程中,首次在cur相遇,返回cur。
- 如果 left和right有一个为空,另一个不为空,假设不为空的那个记为node,此时node有两种可能,要么node是o1或o2中的一个,要么node已经是o1和o2的最近公共祖先。不管是哪种情况,直接返回node即可。
public Node lowestAncestor(Node head, Node o1, Node o2) {
if (head == null || head == o1 || head == o2) {
return head;
}
Node left = lowestAncestor(head.left, o1, o2);
Node right = lowestAncestor(head.right, o1, o2);
if (left != null && right != null) {
return head;
}
return left != null ? left : right;
}
进阶问题核心在于先花较大力气建立一种记录,以后执行每次查询时就可以完全根据记录进行查询。
书上有2种记录的结构,一是建立二叉树中每个节点对应的父节点信息,二是直接建立任意两个节点之间的最近公共祖先记录。
第一种建立的时间复杂度和额外空间复杂度都为O(N),查询的时间复杂度为O(h)。
第二种建立的时间复杂度和额外空间复杂度都为O(N²),查询的时间复杂度为O(1)。
具体算法的步骤与实现见书P157-160。
判断一棵二叉树是否为搜索二叉树和完全二叉树#
《程序员代码面试指南》第46题 P150 难度:士★☆☆☆
判断是否为搜索二叉树,直接检验中序遍历序列的单调递增性即可。当然也可以用树形dp套路来判断(虽然确实麻烦了点),使用ReturnType包装返回值类型,通过判断左右子树是否都满足搜索二叉树,以及和子树的根节点联合起来做判断……不做过多赘述。(书上用了Morris遍历,暂时不看)
另外也可以在中序遍历的途中就去逐一判断单调递增性,以下为摘自牛客网上该题讨论区某代码:
private int pre = 0;
private boolean isBST(node root) {
//是否二叉搜索树
if(root == null) return true;
boolean left = isBST(root.left);
if(left) {
if(pre > root.val) return false;
pre = root.val;
}
return left && isBST(root.right);
}
写的还不错,就是得在方法外定义一个变量来记录上个值。
判断是否为完全二叉树,依据以下标准:
- 按层遍历二叉树,从每层的左边向右边依次遍历所有的节点。
- 如果当前节点有右孩子节点,但没有左孩子节点,则直接返回false。
- 如果当前节点并不是左右孩子节点全有,那么之后的节点必须都为叶节点,否则返回false。
- 遍历过程中如果不返回false,则遍历结束后返回true。
我自己是根据定义“一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。”来编写的代码。
主要使用了一种NodeType的参数来包装Node节点和满二叉树时当前节点的序号num,然后判断节点序号是否与遍历的序号(即当前节点实际的序号)相同。全部相同则返回true,只要有一个不同则返回false。
以下为书上代码:
public boolean isCBT(Node head) {
if (head == null) {
return true;
}
Queue<Node> queue = new LinkedList<Node>();
boolean leaf = false;
Node l = null;
Node r = null;
queue.offer(head);
while (!queue.isEmpty()) {
head = queue.poll();
l = head.left;
r = head.right;
if ((leaf && (l != null || r != null)) || (l == null && r != null)) {
return false;
}
if (l != null) {
queue.offer(l);
}
if (r != null) {
queue.offer(r);
} else {
leaf = true;
}
}
return true;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?