- 浏览: 1461843 次
- 性别:
- 来自: 上海
文章分类
最新评论
-
luhouxiang:
写的很不错,学习了
Extjs 模块化动态加载js实践 -
kingkongtown:
如果想改成淘宝后台那样,可以在编辑器批量上传图片呢?
kissy editor 阶段体会 -
317966578:
兄弟我最近也在整jquery和caja 开放一些接口。在git ...
caja 原理 : 前端 -
liuweihug:
Javascript引擎单线程机制及setTimeout执行原 ...
setTimeout ,xhr,event 线程问题 -
辽主临轩:
怎么能让浏览器不进入 文档模式的quirks模式,进入标准的
浏览器模式与文本模式
理论:
LCA 即 Least Common Ancestor 问题 ,用于在树结构中查找任意两个结点的最小公共祖先,很容易的算法是O(logn),但是对于查询很大的情况,可能并不完美,完美的当然就是O(1)。
那么就采用空间换时间的方法,及可转化为 RMQ (Range Minimum/Maximum Query) 问题 。
效果示例 ( 下载 ):
可以方便的查看任意两个节点的最小公共祖先
LCA In HTML
HTML DOM 树是天生的树结构,可以很方便的做演示,那么就写段 html 用 javascript 演示下 LCA 算法。
html 代码:
<div id="a" style="width:600px;height:550px;border:1px solid green;padding:50px;"> a <div id="b" style="width:300px;height:450px;border:1px solid green;padding:20px;margin:20px 0;"> b <div id="d" style="width:100px;height:100px;border:1px solid green;padding:20px;margin-bottom:20px;"> d <div id="f" style="width:20px;height:20px;border:1px solid green;padding:10px;margin-bottom:10px;"> f </div> <div id="i" style="width:20px;height:20px;border:1px solid green;padding:10px;"> i </div> </div> <div id="e" style="width:100px;height:100px;border:1px solid green;padding:20px;"> e <div id="g" style="width:20px;height:20px;border:1px solid green;padding:10px;margin-bottom:10px;"> g </div> <div id="h" style="width:20px;height:20px;border:1px solid green;padding:10px;"> h </div> </div> </div> <div id="c" style="width:10px;height:10px;border:1px solid green;padding:10px;margin-top:10px;"> c </div> </div>
则对应树结构为 :
1.按先序遍历整棵树,记下两个信息:结点访问顺序和结点深度
则上述为
a,b,d,f,d,i,d,b,e,g,e,h,e,b,a,c,a
结点深度:
0,1,2,3,2,3,2,1,2,3,2,3,2,1,0,1,0
2. 则可首先找到两节点在访问序列中的位置,并在深度序列中找到之间最小的值的位置,再找到该位置对应的结点即为最小公共祖先了。
3. 而找到序列中任意子序列最小值,即是 RMQ 问题,采用 ST(Sparse Table)算法 ,在预处理( O(NlogN) )完成的前提下,可以保证在 O(1) 的时间内找到任意子序列间的最小值。
这样既可保证在预先处理后,则在查询频繁时,保证最快( O(1) )的反映速度。
代码示例 ( 下载 ):
LCA javascript :
/*** 对于频繁计算树中任意两个节点最小公共祖先问题的最优解法. ***/ function Lca(root) { //记录节点访问顺序与深度 this.sequence = []; //RMQ sparse table this.matrix = []; this.preprocessForLca(root, 0); this.processForRmq(); } /* 将树转化为对应的先序访问顺序的层次队列,利用 rmq 算法快速求解 */ Lca.prototype.preprocessForLca = function(node, l) { //第一次访问记录节点与节点深度 this.sequence.push({ node: node, level: l }); var childs = node.childNodes; if (childs) { for (var i = 0, len = childs.length; i < len; ++i) { var c = childs[i]; if (c.nodeType == 3) continue; this.preprocessForLca(c, l + 1); //子节点子树处理完毕,子树父节点即当前节点顺序记录。 this.sequence.push({ node: node, level: l }); } } }; /* 利用动态规划算法计算 sparse table matrix , M[0, N-1][0, logN] , log 以 2 为底 M[i][j] 是从原数组i开始的子数组中最小元素在原数组中的下标, 这个子数组的长度为2^j 可以再 O(1) 的时间复杂度中得到 数组中任意子数组的最小值 */ Lca.prototype.processForRmq = function() { var sl = this.sequence.length; var col = Math.ceil(Math.log(sl) / Math.log(2)); for (var i = 0; i < sl; i++) { this.matrix[i] = []; } // initialize M for the intervals with length 1 for (var i = 0; i < sl; i++) this.matrix[i][0] = i; // 从小间隔计算,直到最后的整个数组间隔 ,即 M[0][sl] for (var j = 1; (1 << j) <= sl; j++) { for (i = 0; i + (1 << j) - 1 < sl; i++) { if (this.sequence[this.matrix[i][j - 1]].level < this.sequence[this.matrix[i + (1 << (j - 1))][j - 1]].level) this.matrix[i][j] = this.matrix[i][j - 1]; else this.matrix[i][j] = this.matrix[i + (1 << (j - 1))][j - 1]; } } }; Lca.prototype.equalNode = function(n1, n2) { if (n1 == n2) return true; //if(n1.id == n2.id) return true; }; /* 利用matrix得到 n1,n2 在树中的最小祖先 每次时间复杂度为 O(1) */ Lca.prototype.getCommonParent = function(n1, n2) { if (this.equalNode(n1, n2)) return n1; if (n1.parentNode == n2) return n2; if (n2.parentNode == n1) return n1; var sl = this.sequence.length; var n1Index = -1; var n2Index = -1; //找到节点对应于访问顺序的下标 for (var i = 0; i < sl; i++) { var t = this.sequence[i].node; if (n1 == t) { n1Index = i; break; } } for (var i = 0; i < sl; i++) { var t = this.sequence[i].node; if (n2 == t) { n2Index = i; break; } } if (n1Index == -1 || n2Index == -1) { alert("-1"); return; } //确保n1Index小 if (n1Index > n2Index) { var t = n1Index; n1Index = n2Index; n2Index = t; } // 2^k*2>= |n1Index-n2Index|. //这样就可以在 //子数组 n1Index ~ n1Index+2^k //子数组 n2Index-2^k ~ n2Index //两个子数组中的深度最小值比较得到 //n1Index ~ n2Index //的深度最小值,即 n1 n2最小公共祖先节点在访问顺序数组中的下标 var k = Math.ceil(Math.log(Math.abs(n1Index - n2Index)) / Math.log(2)) - 1; var n1Min = this.matrix[n1Index][k]; //2^k == 1 << k var n2Min = this.matrix[n2Index - (1 << k)][k]; if (this.sequence[n1Min].level > this.sequence[n2Min].level) return this.sequence[n2Min].node; return this.sequence[n1Min].node; };
使用代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <script type="text/javascript"> //<![CDATA[ /*** 对于频繁计算树中任意两个节点最小公共祖先问题的最优解法. ***/ function Lca(root){ //记录节点访问顺序与深度 this.sequence=[]; //RMQ sparse table this.matrix=[]; this.preprocessForLca(root,0); this.processForRmq(); } /* 将树转化为对应的先序访问顺序的层次队列,利用 rmq 算法快速求解 */ Lca.prototype.preprocessForLca = function(node,l){ //第一次访问记录节点与节点深度 this.sequence.push({ node:node, level:l }); var childs=node.childNodes; if(childs) { for(var i=0 , len=childs.length;i<len;++i) { var c=childs[i]; if(c.nodeType == 3) continue; this.preprocessForLca(c,l+1); //子节点子树处理完毕,子树父节点即当前节点顺序记录。 this.sequence.push({ node:node, level:l }); } } }; /* 利用动态规划算法计算 sparse table matrix , M[0, N-1][0, logN] , log 以 2 为底 M[i][j] 是从原数组i开始的子数组中最小元素在原数组中的下标, 这个子数组的长度为2^j 可以再 O(1) 的时间复杂度中得到 数组中任意子数组的最小值 */ Lca.prototype.processForRmq = function () { var sl=this.sequence.length; var col = Math.ceil(Math.log(sl) / Math.log(2)); for(var i=0;i<sl;i++) { this.matrix[i] = []; } // initialize M for the intervals with length 1 for (var i = 0; i < sl; i++) this.matrix[i][0] = i; // 从小间隔计算,直到最后的整个数组间隔 ,即 M[0][sl] for (var j = 1; (1 << j) <= sl; j++) { for (i = 0; i + (1 << j) - 1 < sl; i++) { if (this.sequence[this.matrix[i][j - 1]].level < this.sequence[this.matrix[i + (1 << (j - 1))][j - 1]].level) this.matrix[i][j] = this.matrix[i][j - 1]; else this.matrix[i][j] = this.matrix[i + (1 << (j - 1))][j - 1]; } } }; Lca.prototype.equalNode =function(n1,n2) { if(n1 == n2 ) return true; //if(n1.id == n2.id) return true; }; /* 利用matrix得到 n1,n2 在树中的最小祖先 每次时间复杂度为 O(1) */ Lca.prototype.getCommonParent = function(n1,n2) { if (this.equalNode(n1,n2)) return n1; if (n1.parentNode == n2) return n2; if (n2.parentNode == n1) return n1; var sl=this.sequence.length; var n1Index=-1; var n2Index=-1; //找到节点对应于访问顺序的下标 for(var i=0;i<sl;i++) { var t=this.sequence[i].node; if(n1==t){ n1Index=i; break; } } for(var i=0;i<sl;i++) { var t=this.sequence[i].node; if(n2==t){ n2Index=i; break; } } if(n1Index == -1 || n2Index == -1) { alert("-1"); return; } //确保n1Index小 if(n1Index > n2Index) { var t=n1Index; n1Index=n2Index; n2Index=t; } // 2^k*2>= |n1Index-n2Index|. //这样就可以在 //子数组 n1Index ~ n1Index+2^k //子数组 n2Index-2^k ~ n2Index //两个子数组中的深度最小值比较得到 //n1Index ~ n2Index //的深度最小值,即 n1 n2最小公共祖先节点在访问顺序数组中的下标 var k = Math.ceil(Math.log(Math.abs(n1Index - n2Index)) / Math.log(2)) - 1; var n1Min = this.matrix[n1Index][k]; //2^k == 1 << k var n2Min = this.matrix[n2Index - (1<<k) ][k]; if(this.sequence[n1Min].level > this.sequence[n2Min].level ) return this.sequence[n2Min].node; return this.sequence[n1Min].node; }; //]]> </script> <title> Test Lca In Javascript </title> </head> <body> <p> node1 的 id :<input id="node1" /> </p> <p> node2 的 id :<input id="node2" /> </p> <p> <input id="btn" type="button" value="点击得到公共祖先id" /> </p> <div id="a" style="width:600px;height:550px;border:1px solid green;padding:50px;"> a <div id="b" style="width:300px;height:450px;border:1px solid green;padding:20px;margin:20px 0;"> b <div id="d" style="width:100px;height:100px;border:1px solid green;padding:20px;margin-bottom:20px;"> d <div id="f" style="width:20px;height:20px;border:1px solid green;padding:10px;margin-bottom:10px;"> f </div> <div id="i" style="width:20px;height:20px;border:1px solid green;padding:10px;"> i </div> </div> <div id="e" style="width:100px;height:100px;border:1px solid green;padding:20px;"> e <div id="g" style="width:20px;height:20px;border:1px solid green;padding:10px;margin-bottom:10px;"> g </div> <div id="h" style="width:20px;height:20px;border:1px solid green;padding:10px;"> h </div> </div> </div> <div id="c" style="width:10px;height:10px;border:1px solid green;padding:10px;margin-top:10px;"> c </div> </div><script type="text/javascript"> //<![CDATA[ window.onload=function(){ //整个子树转化为访问顺序队列数组 var lca=new Lca(document.getElementById("a")); /* var visit=""; var levels=""; for(var i=0 , len=lca.sequence.length;i<len;++i) { var c=lca.sequence[i]; visit+=","+c.node.id; levels+=","+c.level; } console.log(visit); console.log(levels); */ //O(1) 时间内得到任意两个节点的最小公共祖先 document.getElementById("btn").onclick=function(){ var n1=document.getElementById("node1").value; var n2=document.getElementById("node2").value; if(document.getElementById(n1) && document.getElementById(n2)){ var cp=lca.getCommonParent(document.getElementById(n1),document.getElementById(n2)); alert("最小公共祖先 :"+cp.id); } else { alert("DOM 树中没有输入id节点"); } } } //]]> </script> </body> </html>
发表评论
-
构建前端 DSL
2012-10-11 22:10 5361目前在传统的软件开 ... -
circular dependency
2011-12-11 18:23 3925循环依赖是和语言无关 ... -
write html parser
2011-12-01 02:48 2918首先需要声明 html 不能用正则表达式来直接匹配进行内容抽取 ... -
转载:瀑布流布局浅析
2011-09-29 19:02 2847简介 如果你经 ... -
循环引用下的深度克隆
2011-08-04 20:39 2308深度复制和浅度复制 是当初初学 c 遇到的第一批问题,似乎使 ... -
开关状态信息的保存
2010-08-30 15:23 1682系统中常常会存在大量的状态信息,特别是0-1值信息,某个条件是 ... -
LL文法算法-1
2010-03-12 22:30 3473为了实现自顶向下的语法分析器,需要将文法的 1.左递归消 ... -
NFA到DFA的转换演示
2010-03-07 20:57 12731复习一下编译,在龙书中提到的NFA(不确定有穷自动机)到D ... -
gzip压缩实现注意
2010-01-18 22:19 0给你提点建议,你自己实现的compress不是很好哦,1. C ... -
三点共线判断
2010-01-12 19:43 14357经典的计算几何方面问题,判断二维坐标系中是否三个点在一条直线上 ... -
多维数组迭代器应用
2010-01-10 18:04 1717在代码之美中提到了这个问题,经常遇到嵌套数组的情况即多维数组情 ... -
google 开源项目
2009-12-28 20:25 0Google是支持开源运动的最大公司之一,它们现在总共发布 ... -
大数据量,海量数据 处理方法总结
2009-12-12 02:14 0最近有点忙,稍微空闲下来,发篇总结贴。 大数据量的问题是很 ... -
Bloom Filter Technical Report
2009-12-12 01:57 0Bloom Filter Technical Report ... -
找零问题
2009-10-31 16:07 2378问题描述: 有n美元需找零. 美 ... -
背包问题javascript演示
2009-10-26 16:28 2519背景: 经典递归示例:背包问题 ... -
hanoi问题求解
2009-10-19 23:54 0http://jnotnull.iteye.com/ ... -
后缀表达式的javascript转化演示
2009-10-19 23:46 1637复习经典算法,原算法:数据结构(用面向对象方法与c++描述) ... -
Array.prototype.sort 稳定性问题
2009-09-16 13:49 2897引例 首先看一段代码: ... -
编程之美:javascript的数独实现
2009-07-22 15:46 4978书中没有给出具体实现 ...
相关推荐
最近公共祖先(LCA)问题在计算机科学,特别是在图论和数据结构中具有重要的应用,尤其是在树形数据结构的处理中。LCA指的是在一棵有根树中,给定两个节点u和v,找到离根最远的那个节点r,使得r既是u的祖先也是v的...
【标题】"LCA2015:LCA2015 演示幻灯片" 提供的信息表明这是一个关于Linux Conference Australia 2015(LCA2015)活动的演示文稿。LCA是一个年度会议,聚集了全球各地的开源技术爱好者、开发者和专家,讨论Linux及其...
其中,PROC LCA(Latent Class Analysis)是SAS中的一个过程,用于执行潜在类别分析,这是一种统计方法,旨在从观测数据中识别出隐藏的、不可见的类别结构。PROC LCA尤其适用于处理多分类变量,它可以帮助研究人员...
**Tarjan的LCA算法详解** 最近公共祖先(Lowest Common Ancestor,简称LCA)是图论中的一个重要概念,特别是在树形结构中。在树中,两个节点的最近公共祖先指的是距离根节点最近的那个同时是这两个节点的祖先的节点...
【LCA词汇复杂度分析软件】是一款用于自然语言处理(NLP)的专业工具,它主要聚焦于语言的复杂度分析。LCA,全称为“Language Complexity Analysis”,此软件旨在帮助用户理解和评估文本的语言难度,这对于教育、...
LCA265显示器是Systeme Lauer GmbH & Co. KG公司生产的一款专业显示器,用于显示和处理用户数据。在操作这款显示器时,有时需要进行数据传输,特别是文本文件的交换,这通常涉及到与个人计算机(PC)之间的通信。...
《51单片机开发工具LCA51正式版2.06详解》 51单片机,作为微控制器领域中的经典型号,被广泛应用于各种电子设备和控制系统中。对于51单片机的开发者而言,拥有一款高效、易用的编辑器、编译器和仿真工具至关重要。...
例如,在一个树形结构中,A是B和C的LCA,D是E和F的LCA。 1. **Tarjan离线算法**: Tarjan提出了一种离线算法来解决LCA问题,使用并查集维护树结构,并通过深度优先搜索(DFS)进行预处理。每个节点u的father[u]...
关于RMQ和LCA的关系的知识,如何用RMQ和LCA的转换
这导致无法从中提取任何有关“日立LCA电气图纸(K3500490).pdf”文件的知识点。为了满足题目要求,我将基于标题和描述部分提供的信息,对“日立LCA电气图纸”进行知识性的解释。 ### LCA型微机控制变压变频调速无...
【LCA51早期版本】是一款专用于模拟和开发基于MCS-51(8051)微控制器的应用程序的软件工具。这个版本是2.02,可能对于一些研究历史版本、进行兼容性测试或者对旧项目进行维护的开发者来说具有一定的价值。在本文中...
最近公共祖先(Lowest Common Ancestor,简称LCA)是图论中的一个重要概念,主要应用于树形结构的数据问题。在给定的树中,两个节点的最近公共祖先是指距离这两个节点最近的那个共同父节点。在计算机科学中,尤其是...
LCA 中文教程,内部资料--产品结构管理,还不错啊,可以看下
在IT领域,特别是数据结构和算法的设计中,"最近公共祖先"(Lowest Common Ancestor,简称LCA)是一个常见的问题。这个问题出现在许多场景中,比如计算机科学中的树形结构处理,生物信息学中的基因进化分析等。最近...
LCA生命周期评价,LCA生命周期评价课件,LCA生命周期评价PPT
LCA Tarjan: 实现原理 理解:离线算法,建好树后再查询,一次DFS 吧所有查询解决完。 时间复杂度:O(n+q); n个点 q次询问 补一下:链式向前星,并查集 ,Tarjan 代码 #include #include #include #include #...
在IT领域,特别是数据结构和算法分析中,"LCA"是"Lowest Common Ancestor"(最近公共祖先)的缩写,这是一个经典的问题,常常出现在树形结构的处理中。给定一个树形结构和若干对节点,我们需要找出这些节点的最近...
首先,定义了一些辅助变量,如f数组记录节点所属集合的根节点,r数组记录集合的秩,indegree数组记录节点的入度,visit数组标记节点是否已处理,tree数组存储树的邻接表,Qes数组存储查询对,以及ancestor数组记录每...