[路飞]_程序员必刷力扣题: 面试题 04.08. 首个共同祖先

130 阅读3分钟

面试题 17.14. 最小K个数

设计并实现一个算法,找出二叉树中某两个节点的第一个共同祖先。不得将其他的节点存储在另外的数据结构中。注意:这不一定是二叉搜索树。

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

   3
   / \
  5   1
 / \ / \
6  2 0  8
  / \
 7   4

示例1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

所有节点的值都是唯一的。
pq 为不同节点且均存在于给定的二叉树中。

提示:

  • 0 <= len(arr) <= 100000
  • 0 <= k <= min(100000, len(arr))

深度优先搜索

思路 从题意可知,我们需要求两个节点的首个共同祖先,

  • 两个节点中一个是祖先,第二个节点是第一个节点的后代节点
  • 两个节点都不是,而是其中一个或者两个的父节点

思路1(记录开始的粗略思路可略过):递归求出两节点的所有父节点,进行比较,前面的父节点必然是相同的,直到最后一个共同的父节点,因为不能将节点存储在另外的数据结构中,故而这里存储的是节点的值,因此只能再跟据节点的值来寻找对应的节点

例: node1父节点:[3,5,2,1,4]

node2父节点: [3,5,2,1,6]

通过对比可知1位最近共同祖先,再跟据[3,5,2,1]的顺讯去寻找1节点并返回

缺点:繁琐且慢

思路2:

只需要一个函数来判断当前节点以及其子节点是否是有需要寻找的节点

  • 寻找到p或者q,则用一个对象来记录已经找到p或者q,继续递归在子节点中查找另一个元素
  • 如果p和q都找到,则结束递归,返回当前状态
  • 父元素跟据返回的状态来标记当前节点(首个公共父元素),用一个对象中的target来表示目标节点,target有值则直接返回该对象,无需做其他处理

优缺点:速度还行,占用空间过大,每次递归都会创建一个对象用来保存当前的状态

优化: 用全局变量,一个对象即可

优化思路:

用一个全局对象来保存所有状态,包括是否找到p,是否找到q,是否找到父节点

var res = {
        target: null,
        hasP: false,
        hasQ: false
    }
  • 用一个函数findRoot去检查每一个元素,并且递归给子元素递归调用(深度遍历搜搜),如果该节点的值为p或者q,则改变res中对应的状态
  • 如果在检查一个节点时,先判断初始状态flag(p和q都没有找到)的状态为false(代表在当前节点之前已经找到p或者q,当前节点不可能是答案),flag为true(代表两节点都没找到,当前节点有可能是答案)
  • 处理当前节点,以及左右子节点
  • 判断p和q如果都找到则当前节点为答案
var lowestCommonAncestor = function (root, p, q) {
    var res = {
        target: null,
        hasP: false,
        hasQ: false
    }
    function findRoot(root, p, q) {
        var flag = false
        if (!res.hasP && !res.hasQ) flag = true
        if (!root) return
        if (root.val === p.val) {
            res.hasP = true
        }
        if (root.val === q.val) {
            res.hasQ = true
        }
        // 如果两个都还没找到,继续递归子元素
        if (!res.hasP || !res.hasQ) {
            findRoot(root.left, p, q)
            findRoot(root.right, p, q)
        }
        if (!res.target && res.hasP && res.hasQ && flag) {
            res.target = root
        }
    }
    findRoot(root, p, q)
    return res.target
};