zoukankan      html  css  js  c++  java
  • LeetCode 236:二叉树的最近公共祖先

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

    解题思路:

      一般二叉树相关的算法题,都可以使用递归这个编程技巧来解题,本题也不例外。

    分析: 2个节点存在的位置,不外乎以下4种情况:

    • 要么都在左子树上
    • 要么都在右子树上
    • 要么一个在左,一个在右
    • 至少有一个不在这颗二叉树中【就是不在这个二叉树上的情况】

    我们先看代码,然后来讲解解法,代码如下:

     
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     public var val: Int
     *     public var left: TreeNode?
     *     public var right: TreeNode?
     *     public init(_ val: Int) {
     *         self.val = val
     *         self.left = nil
     *         self.right = nil
     *     }
     * }
     */
     
    class Solution {
        func lowestCommonAncestor(_ root: TreeNode?, _ p: TreeNode?, _ q: TreeNode?) -> TreeNode? {
            if root == nil || root?.val == p?.val || root?.val == q?.val { return root }
            let left = lowestCommonAncestor(root?.left, p, q)
            let right = lowestCommonAncestor(root?.right, p, q)
            if left != nil && right != nil { return root }
            return left == nil ? right : left
        }
    }

    代码解析:
    先是边界条件的判断
       1. if root为nil,直接 return
       2. if root.val == q.val 或者 root.val == p.val,直接 return

    下面是递归逻辑:
       1. 先从左子树上找共同的祖先,记为left
       2. 再从右子树上找共同的祖先,记为right

       如果left和right都不为nil,说明满足上述的条件3【一个在左,一个在右】,所以很显然,祖先节点就是根节点root;

       如果left不为nil,则返回left,否则返回right。这里面就包含了剩下3种条件,left不为nil,说明2个节点都在左子树上。如果left为nil,则说明要么2个节点都在右子树上,或者至少有一个不在这个二叉树上。

    关于递归:
       递归这种编程技巧是非常有用的,掌握它,可以为我们解决很多思考起来很麻烦的题目。为了更好的理解递归,大家可以看我之前写的文章:leetcode 递归编程技巧-链表算法题。为了让大家体会到递归的强大,我们再看1道题: LeetCode 144. 二叉树的前序遍历
    如下图:一颗二叉数

    前序遍历就是:先访问根节点、前序遍历左子树、前序遍历右子树。


    过程如下:

    先访问5,然后使用中序遍历去访问 5.left,再然后使用前序遍历去访问 5.right。
    1.1. 中序遍历去访问5.left
    先访问3,然后使用中序遍历去访问 3.left,再然后使用前序遍历去访问 3.right。
    ...
    ...
    1.2. 中序遍历去访问5.right
    先访问7,然后使用中序遍历去访问 7.left,再然后使用前序遍历去访问 7.right。
    ...
    ...

    打印结果就是:5-3-1-0-2-4-7-6-8-9

    代码:

     
    class Solution {
        var nums = [Int]()
        func inorderTraversal(_ root: TreeNode?) -> [Int] {
        guard let root = root else { return [] }
                nums.append(root.val)
                inorderTraversal(root.left)
                inorderTraversal(root.right)
        return nums
            }
    }

    代码解析:
    先是边界条件的判断
       1. 先访问根节点
       2. 然后前序遍历左子树
       2. 然后前序遍历右子树

    如果问你中序遍历【左根右】,你会写吗?

    class Solution {
        var nums = [Int]()
        func inorderTraversal(_ root: TreeNode?) -> [Int] {
        guard let root = root else { return [] }
                inorderTraversal(root.left)
                nums.append(root.val)
                inorderTraversal(root.right)
        return nums
            }
    }

    对,你没看错。就是这么简单,把它的访问放在中间就是【以后写一些算法的时候,可以把逻辑写在中间】。那么后序遍历了?一样,把访问 nums.append(root.val) 代码放在最后一行就是了。这就是递归的魅力。

    总结:
      今天主要讲了一道leetcode上面的第236道算法题,其核心思想就是使用递归来实现的。后续为了让大家体会到递归编程技巧的强大,我又借二叉树的遍历例子用递归来实现为大家展示递归的强大。大家如果想更好的理解递归的调用栈的话,可以看我自己写的文章:leetcode 递归编程技巧-链表算法题。希望能帮助到大家。

    欢迎关注【无量测试之道】公众号,回复【领取资源】
    Python编程学习资源干货、
    Python+Appium框架APP的UI自动化、
    Python+Selenium框架Web的UI自动化、
    Python+Unittest框架API自动化、
    资源和代码 免费送啦~
    文章下方有公众号二维码,可直接微信扫一扫关注即可。

    备注:我的个人公众号已正式开通,致力于测试技术的分享,包含:大数据测试、功能测试,测试开发,API接口自动化、测试运维、UI自动化测试等,微信搜索公众号:“无量测试之道”,或扫描下方二维码:


    添加关注,让我们一起共同成长!

  • 相关阅读:
    html5 中的 css样式单 的 两种调用方式的区别
    互联网公司的相关人员及业务简介
    require.js 入门学习-备
    IOS修改webView背景透明以及IOS调用前台js的方法
    Javascript AMD模块化规范-备用
    Meta 的两个 相关属性
    <meta http-equiv="pragma" content="no-cache"/>
    css:中文词不断开,整体换行
    linux驱动开发---导出内核符号
    web html 防盗链
  • 原文地址:https://www.cnblogs.com/Wu13241454771/p/15156936.html
Copyright © 2011-2022 走看看