LeetCode-236的解法和延伸
注意:这篇文章需要你了解C++作为前置
题目描述:
Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree.
According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).”
_______3______/ \_5__ ___1__/ \ / \ 6 _2_ 0 8/ \7 4
For example, the lowest common ancestor (LCA) of nodes 5
and 1
is 3
. Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition.
Subscribe to see which companies asked this question
解题思路
这个题目是一个最为简单的LCA问题,只需要查询一对节点(u, v)的公共祖先,所以实际上不需要使用用到了什么需要先验知识才能解决的算法
下面是一个最为基础的思路:
简单粗暴法
可以很轻松的想到一对节点(u, v)的公共祖先满足下述可能中的某一种:
1. x in (u, v) 为 (u, v) 的LCA;
2. x not in (u, v) 为 (u, v) 的LCA 此时易证明 u, v 分置该祖先的左右两子树中;
所以可以很清楚的看出这个问题可以直接使用遍历的方法来查找LCA
然后注意到需要通过左子树和右子树才能确定LCA,所以应该使用后序遍历:
代码如下:
class Solution { public:TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {LCA = u = v = L = R = NULL;u = p;v = q;//init DFS(root);return LCA;}void DFS(TreeNode* root){if(root == NULL || LCA != NULL){return;}if(root->left != NULL){DFS(root->left);if(root->left == L)L = root;if(root->left == R)R = root;//update L, R to its father }if(root->right != NULL){DFS(root->right); if(root->right == L)L = root;if(root->right == R)R = root;//update L, R to its father }if(root == u)L = root; // first update of Lif(root == v)R = root;// first update of Rif(L != NULL && R != NULL && L == R && LCA == NULL){ //has find LCALCA = L; return;}}// recursion function updating LCATreeNode* L;TreeNode* R;TreeNode* LCA;TreeNode* u;TreeNode* v;};
没什么好说的,肯定能优化,但是这样反正过这题没问题。
但是,LCA问题本来就不是这么简单的玩意
实际上,LCA问题的初始用意是要快速处理多起LCA询问
设询问次数Q,这个朴素算法复杂度为O(QN),跟咸鱼有什么区别
相信院系里其他人也都知道LCA的本来面目是什么样的
然而,这些算法都需要一些先验知识
下面介绍一个比较常用的离线算法: Tarjan算法
Tarjan算法
离线算法:在开始时就需要知道问题的所有输入数据的算法
根据复杂度分析,Tarjan算法对于LCA问题的复杂度为O(N + Q),远优于朴素法
这个算法利用了并查集的特性,节省了空间和时间
为了一次性解决所有需求,为每一个节点分配一块空间来存储针对该节点的询问
(实际上也有别的方式能够解决这个问题,为了简便使用这个方法存储)
由于在朴素法中提出的那些关于解的条件依然成立
所以依然采用后序顺序访问树
当其子节点的访问结束后再处理针对该节点的所有请求
又由于可能在存储的询问(u, v)在访问u 时尚无法求解,如:
_______3______/ \_5__ ___1__/ \ / \ 6 _2_ 0 8/ \7 4针对(7, 8)的查询在访问7时是无法求解的
大概的思路是这样的
建立所有节点的并查集:用并查集所有节点中最顶层的节点作为并查集的代表,初始化时所有节点所在的并查集都只包含它本身
DFS访问
当访问到的节点有一支子树访问结束后,将这支子树的并查集归并到该节点所在的并查集
当两支子树都被归并到该节点所在的并查集中时,开始处理针对该节点的请求,设当前节点为u, 另一节点为v
仅处理v已被访问时的请求
此时我们有了这样的一个事实:
设当前节点为u, 另一节点为v
如果有针对(u ,v)的请求在u节点被处理了,那么在u之前,询问的另一个节点v已经被访问过了
根据后续遍历的特性,此时v所在的并查集的代表就是(u, v)的LCA
并查集的实现通过一个映射f[n]实现, f[n] 指向n的上一层并查集代表,如果n为顶层并查集,则f[n] = n
这样的话并查集的合并仅需要更改f[n] 就可以实现
最后说两句
支线任务什么的,其实我都看过,也都做过一遍,但我加上这篇也只写了两篇随笔,原因也是挺简单的:
我觉得那些我没写的题目讲起来真的不知道能讲什么
比如第一次的题目 ,你只要想到对链表原地反转,这一点就足够解决问题了
第二次的题目是上课的原题
第三次还算有点意思,需要考虑栈的特性
这一次给了道裸题,直接就是LCA
其实这些题目离答案非常近,也没有什么需要抽象的内容
这样的题目能讲些什么呢?
画个图一步一步教你走路?
能讲的东西我觉得只有一两句话,这一两句话也就够了
最后的最后
你是一个小有名气的冒险者导师
城门口的各种怪物,你徒手都能解决
有一天,冒险者协会给了你一个任务
写一个新手专栏:
傻瓜也能看懂的指南:一步一步干掉史莱姆
为了完成它
你拿着剑,游荡在城镇门口,日复一日
把这种黏液组成的杂兵砍碎了一遍又一遍
只为了你能得到的报酬从两百银币变成三百银币
为了这一百银币
你绞尽脑汁,不停地回忆自己出剑的角度,砍上去的手感,切入的深度,造成的伤害,史莱姆躲闪的方向,把这些详细得令人发指的细节写进书里
新手看着指南,将史莱姆砍杀得七零八落,很开心
你拿着手上的钱袋,想着晚上能去酒馆喝两杯,也很开心
看似如此
————
但在无人的夜里
你有时还是会想
这件工作的意义何在?
新手们会满足于史莱姆么?
我为什么不让他们看看更遥远的世界?
于是终于有一天,你放弃了城门外的史莱姆
走向荒野深处,那里是你冒险的地方
你看到喷吐硫磺,火焰和毒气的巨龙
你看到长有巨大触须,操纵邪能的灵吸怪
你看到双头独眼,如山岳般高大的戈隆
这些你的老对手,你以前的敌人
你手里的剑因为你的紧握而发热
你早已遗忘的兴奋感也涌了出来
————
你费劲千辛万苦,打倒了巨龙,无面者与戈隆
兴冲冲的把自己的经历写到了你的专栏里
告诉新手
还有一些怪物,是这样的
它们很强大,也很棘手
你用来杀死史莱姆的剑术,经过一系列的修正,加强,也能够干掉这些怪物
但你得学习这些剑术技巧,这里我无法详细介绍
而冒险者协会的回应是:后面附上的拓展游记有点意思,但我们觉得新手只想知道如何精确杀死史莱姆,越详细越好
————
————
转载于:https://www.cnblogs.com/ocNflag/p/4967695.html
LeetCode-236的解法和延伸相关推荐
- 力扣(LeetCode)236. 二叉树的最近公共祖先(C语言)
一.环境说明 本文是 LeetCode 236. 二叉树的最近公共祖先,使用c语言实现. 递归. 测试环境:Visual Studio 2019. 二.代码展示 精简代码: struct TreeNo ...
- LeetCode单词规律解法
LeetCode单词规律解法 给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律. 这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str ...
- leetcode 236. 二叉树的最近公共祖先 递归解法 c语言
如题: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先.百度百科中最近公共祖先的定义为:"对于有根树 T 的两个结点 p.q,最近公共祖先表示为一个结点 x, 满足 x 是 p.q ...
- LeetCode 236. 二叉树的最近公共祖先
文章目录 解法1:保存祖先节点+逐个判断 解法2:深度优先遍历 解法3:记录祖先节点 https://leetcode-cn.com/problems/lowest-common-ancestor-o ...
- Leetcode算法题-解法转载
版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/fuxuemingzhu/article/details/85112591 作者: 负雪明烛 i ...
- 【一天一道Leetcode】基本计算器的延伸问题
本篇推文共计2000个字,阅读时间约3分钟. 01 题目描述 题目描述: 给你一个字符串表达式s,请你实现一个基本计算器来计算并返回它的值. 整数除法仅保留整数部分. 示例: 输入:s = " ...
- leetcode 236. Lowest Common Ancestor of a Binary Tree | 236. 二叉树的最近公共祖先(Java)
题目 https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ 题解 思路来源:左程云<程序员代码面试指南&g ...
- [leetcode]236.二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先. 百度百科中最近公共祖先的定义为:"对于有根树 T 的两个节点 p.q,最近公共祖先表示为一个节点 x,满足 x 是 p.q 的祖先且 ...
- Leetcode上的解法看不懂?试着用动画的方式去辅助理解
推荐一个用动画的方式演示leetcode题目解题思路的github仓库: https://github.com/MisterBooo/LeetCodeAnimation 超过15000个star: 用 ...
- 【LeetCode之C#解法】 移动零、爬楼梯
题目官网链接 https://leetcode-cn.com/problems/move-zeroes/ 283. 移动零 给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非 ...
最新文章
- linux wc命令参数及用法详解
- 题目1184:二叉树遍历
- Android开发系统版本的区别,开发者对比安卓和iOS系统
- LeetCode-66. 托普利茨矩阵
- 第二次作业动手动脑的解答
- SpringBootSwagger构建REST API并生成API文档
- Notepad++ 查找替换 换行符的方法
- 递归法:整数划分问题(怎么进行划分呢)
- webpack.config.js====CSS相关:插件optimize-css-assets-webpack-plugin
- jq监听子元素被点击_jQuery怎么实现当前被点击元素的父级下的某个元素显示出来了?...
- windows7 android 驱动,Windows7安卓刷机驱动安装教程图文详解
- 编译器之词法分析器(Lexical Analyzer)
- m7405d粉盒清零方法_联想m7605d清零方法
- 91位图和bigemap大地图的区别
- mySQL数据库学习的一些心得
- adb 连接方式汇总
- 精准电流走向分析|用笔记本电脑的供电电路描述MOS管的两大功能:开关作用和隔离功能
- 安卓蓝牙实现即时通讯功能
- 洛谷P4942 小凯的数字
- MySql数据库记录相差14小时排错,使用Java访问Mysql数据库时出现时区异常的解决方案
热门文章
- 魔兽怀旧服联盟服务器不稳定,魔兽世界怀旧服上次被联盟攻击至少三个月前,“单边服”何去何从...
- 江西智汇网络推客SCRM智能获客系统客户倍增的全员营销系统
- 学习模式上的记录之统计篇二 Sigmaplot 非线性回归报告分析
- 用web audio api 在canvas上画出音频的音轨
- 创建选区快捷键是什么_Photoshop选区操作的快捷键
- Markdown基本语法和Typora使用教程
- 找计算机专业老师的说说,学生想念老师的说说 思念老师的唯美句子
- 批量处理word所有回车行
- Deepfashion2数据集
- Quard SPI和QPI的区别