《编程之美: 求二叉树中节点的最大距离》的另一个解法
2011-10-11 19:28
411 查看
12
0
(请您对文章做出评价)
Spiga
Posts-23,Articles-0,Comments-1089
Cnblogs
Dashboard
Login
Home
Contact
Gallery
RSS
2010-02-2503:32byMiloYip,7408visits,收藏,
编辑
昨天花了一个晚上为《编程之美》,在豆瓣写了一篇书评《迟来的书评和感想──给喜爱编程的朋友》。书评就不转载到这里了,取而代之,在这里介绍书里其中一条问题的另一个解法。这个解法比较简短易读及降低了空间复杂度,或者可以说觉得比较「美」吧。
计算一个二叉树的最大距离有两个情况:
情况A:路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
情况B:路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离。
我也想不到更好的分析方法。
但接着,原文的实现就不如上面的清楚(源码可从这里下载):
viewsourceprint?
这段代码有几个缺点:
算法加入了侵入式(intrusive)的资料nMaxLeft,nMaxRight
使用了全局变量nMaxLen。每次使用要额外初始化。而且就算是不同的独立资料,也不能在多个线程使用这个函数
逻辑比较复杂,也有许多NULL相关的条件测试。
viewsourceprint?
计算result的代码很清楚;nMaxDepth就是左子树和右子树的深度加1;nMaxDistance则取A和B情况的最大值。
为了减少NULL的条件测试,进入函数时,如果节点为NULL,会传回一个empty变量。比较奇怪的是empty.nMaxDepth=-1,目的是让调用方+1后,把当前的不存在的(NULL)子树当成最大深度为0。
除了提高了可读性,这个解法的另一个优点是减少了O(节点数目)大小的侵入式资料,而改为使用O(树的最大深度)大小的栈空间。这个设计使函数完全没有副作用(sideeffect)。
viewsourceprint?
你想到更好的解法吗?
分类:
数据结构和算法
绿色通道:好文要顶关注我收藏该文与我联系
MiloYip
关注-31
粉丝-486
荣誉:推荐博客
+加关注
«
博主前一篇:混合语言的游戏开发系统架构
»
博主后一篇:解构Unity的腳本物件模型
Categories:数据结构和算法
Addyourcomment
#1楼
yeka2010-02-2506:30
Milo又熬夜啦.......
回复 引用 查看
#2楼
陈硕2010-02-2508:56
第19~21行有线程安全问题:
staticconstRESULTempty={0,-1};//trick:nMaxDepthis-1andthencallerwillplus1tobalanceitaszero.
if(!root)
returnempty;
建议改为:
if(!root){RESULTempty={0,-1};//trick:nMaxDepthis-1andthencallerwillplus1tobalanceitaszero.
returnempty;
//trustcompiler,PODdatawillbeoptimizedwell.
}因为按标准,functionstaticvariable只在函数第一次调用时初始化,这个的初始化只有在最新的编译器里才是线程安全的。
在旧的编译器(GCC3及以前)上,原来的写法可能会读到partialinitialized'empty'变量,如果两个线程同时(首次)调用GetMaximumDistance的话。
回复 引用 查看
#3楼
秋醒半梦时[未注册用户]2010-02-2509:12
这不就是求树的直径的问题吗?
树的直径最简单的解法(无论是几叉):
从任意一点i一次BFS找到树中与他距离最远的点j,从j再一次BFS找到树中里j最远的点k,那么D[j][k](j与k的距离)即为答案。
稍微好理解的方法:树形动态规划
回复 引用
#4楼
Dbger2010-02-2510:07
@陈硕
如果非要考虑多线程安全,我倾向于用“全局变量”来表示这些常用的常量,就和向量,矩阵类中一些单元向量,单元矩阵等。
回复 引用 查看
#5楼
JeffreyZhao2010-02-2510:46
我觉得直接把递归语意翻译过来最直接和清晰吧:
viewsourceprint?
这里我用了F#,不过C#,C++其实也是一回事情吧。
回复 引用 查看
#6楼
ToddWei2010-02-2512:11
@秋醒半梦时
进行两次BFS:先从树根A出发进行广度优先搜索(BFS),找到最远的结点B,然后再从结点B出BFS,找到离B最远的结点C,BC就是最大距离。
下面是正确性证明
假设存在结点X和Y,它们的距离是所有结点中最大的;分两种情况讨论:
1.若路径XY与路径AB有交点O,
...A
...|
X-O--Y
...|
...B
由于|OB|>=|OX|且|OB|>=|OY|,所以,|BX|>=|XY|,|BY|>=|XY|。即从B出发可以构造出最长路径。
2.若路径XY与路径AB无交点,
A...BX...Y
A是树根,XY与B分属不同的子树,假设XY的最近祖先为O,由于
|AB|>=|AO|+|AX|,所以|BY|=|AB|+|AO|+|OY|>|XY|。即从B出发构造出长于XY的路径,与假设XY是最长路径矛盾。
回复 引用 查看
#7楼[楼主]
MiloYip2010-02-2512:19
@Dbger
@陈硕
我覺得兩個方法都可以解決潛在的多線程問題。我現在先相信compiler,改用了陳碩的寫法。
從另一個角度看這個問題,localstaticvariable是會做成sideeffect,所以thread-safe會不成立。
回复 引用 查看
#8楼[楼主]
MiloYip2010-02-2512:40
@JeffreyZhao
我未學過任何一個functionalprogramming語言。希望趙大能指正不對的地方。
用FP的確可以增加可讀性,同時能減少錯誤的機會。
FP能對purefunction用自動的cacheoptimization,這是優點也是缺點。如果沒有這優化,在你提供的代碼中,height的調用次數估計是O(n^2);而有了這優化,就需要O(n)的空間去儲存n個height()的運算結果。而這優化我估計應該需要做tablelookup,帶來額外overhead。
我的嘗試中,並不需要O(n)的額外空間,而且仍維持每節點只遍歷一次。
又反過來說,在效能上,FP的好處是可以自動做並行,用procedural語言手動做這個就會顯得複雜。
回复 引用 查看
#9楼[楼主]
MiloYip2010-02-2513:09
@ToddWei
@秋醒半梦时
多謝你們的回應,我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
這該我找到一點參考文章:
http://www.cs.duke.edu/courses/spring00/cps100/assign/trees/diameter.html
http://www.cs.cmu.edu/afs/cs.cmu.edu/project/phrensy/pub/papers/LeisersonM88/node17.html
發現前一篇文章基本上和Jeffrey的嘗試一樣,但用proceduralprogramming會有O(N^2)的height()調用。我覺得我寫的邊界條件(那個trick)可能不需要,今晚回家試試。
第二篇談到的幾個詞彙我都不太認識,可能要再多看一些參考。也想請教,用BFS的方法會比現時的方法簡單或高效麼?還是現時的方法實際上有錯誤?
回复 引用 查看
#10楼
ToddWei2010-02-2513:20
@MiloYip
BFS是O(N)的,所以复杂度更低。特别是基于BFS的方法不局限于2叉树,而前面递归方法在多叉树情况下复杂度会更高。
回复 引用 查看
#11楼
JeffreyZhao2010-02-2513:23
@MiloYip
其实你的算法还是用了O(h)的空间啦,h是高度,(非尾)递归算法嘛,栈空间是省不了的。
的确这里height会反复调用,所以如果必要的话,还是要做memorization的。
作了momorization以后,时间和空间“复杂度”和你的过程式算法是一致的了。
回复 引用 查看
#12楼[楼主]
MiloYip2010-02-2513:31
@JeffreyZhao
引用
JeffreyZhao:
@MiloYip
其实你的算法还是用了O(h)的空间啦,h是高度,(非尾)递归算法嘛,栈空间是省不了的。
的确这里height会反复调用,所以如果必要的话,还是要做memorization的。
作了momorization以后,时间和空间“复杂度”和你的过程式算法是一致的了。
本文也提及,我的嘗試用了O(h)的棧空間代替原文的O(n)intrusivedata,而你寫的height函數的memorization空間是O(n)。因為h<=n,O(h)應該是比O(n)好吧。
回复 引用 查看
#13楼[楼主]
MiloYip2010-02-2513:36
引用ToddWei:
@MiloYip
BFS是O(N)的,所以复杂度更低。特别是基于BFS的方法不局限于2叉树,而前面递归方法在多叉树情况下复杂度会更高。
我的嘗試也是O(N),而且只需遍歷一次。跟據你的描述,BFS要做兩次,而且要加入parent?不過對於一般的多叉樹,可能BFS的方法是最好的方法。
回复 引用 查看
#14楼
ToddWei2010-02-2515:09
@MiloYip
哦,是的,你的递归也是O(N),开始分析错了。
树哪个结点作为parent没关系,任选即可。图论里面对树的一种定义方式是:具有n个结点和n+1条边的连通图。
回复 引用 查看
#15楼
郑晖2010-02-2516:06
@MiloYip
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
回复 引用 查看
#16楼
秋醒半梦时[未注册用户]2010-02-2517:03
引用ToddWei:
@MiloYip
哦,是的,你的递归也是O(N),开始分析错了。
树哪个结点作为parent没关系,任选即可。图论里面对树的一种定义方式是:具有n个结点和n+1条边的连通图。
我所了解的树的定义是:一个无环的无向图
回复 引用
#17楼[楼主]
MiloYip2010-02-2517:04
@郑晖
引用
郑晖:
@MiloYip
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
謝謝鄭老師的數學指導。在網上找到了關於這個的定義:
http://mathworld.wolfram.com/GeneralizedDiameter.html
http://mathworld.wolfram.com/Supremum.html
回复 引用 查看
#18楼
郑晖2010-02-2517:17
引用MiloYip:
引用郑晖:
@MiloYip
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
謝謝鄭老師的數學指導。在網上找到了關於這個的定義:
http://mathworld.wolfram.com/GeneralizedDiameter.html
http://mathworld.wolfram.com/Supremum.html
http://mathworld.wolfram.com/GeneralizedDiameter.html
上面对的直径定义尚不足够general,它只提到了欧氏空间(EuclideanspaceR^n),
实际可扩展到更广泛的距离空间(metricspace)。事实上,你这里提到的树就不是欧氏空间(因为这里的距离并非欧氏距离)。
回复 引用 查看
#19楼[楼主]
MiloYip2010-02-2517:27
@郑晖
是的,我理解只要是metric就可以定義diameter。
回复 引用 查看
#20楼
flyinghearts2010-05-1914:05
这是我的解法:
http://blog.csdn.net/flyinghearts/archive/2010/05/19/5605995.aspx
欢迎大家指正。
回复 引用 查看
#21楼
gzroy2010-10-0618:24
这是我的递归解法,欢迎交流:
http://blog.csdn.net/yui/archive/2010/10/06/5924020.aspx
回复 引用 查看
注册用户登录后才能发表评论,请
登录或
注册,返回博客园首页。
简洁阅读版式
不3k就业不给1分学费(java,.net,php,android)
首页博问闪存新闻园子招聘知识库
最新IT新闻:
·10个超棒的jQuery工具提示插件推荐
·亲久等了!小米手机零售版销售策略公布
·哥终于知道了苹果为什么发布的是iPhone4S而非iPhone5
·分析师:谷歌三星推迟发NexusPrime是明智之举
·《星际争霸2:虫群之心》新兵种曝光
»更多新闻...
最新知识库文章:
·
Scrum实施经验
·Doclist压缩方法简介
·专家视角看IT与架构
·跨平台的移动开发框架介绍
·为您的Web项目构建一个简单的JSON控制器
»更多知识库文章...
China-pub2011秋季教材巡展
China-Pub计算机绝版图书按需印刷服务
在高一高二时兼职开发游戏《王子传奇》后,便潜心向学(伪),得到认知科学学士和系统工程及工程管理学哲学硕士后,于大学里做游戏科技的相关项目,直至2008年才来到上海,再次投身游戏业界,之前的作品是《美食从天而降》Xbox360/PS3/Wii/PC。
因为写了一个书评而认识到很多朋友,决定在内地设一个blog,和大家分享交流。
Twitter
新浪微博
昵称:MiloYip
园龄:1年8个月
荣誉:推荐博客
粉丝:486
关注:31
+加关注
12-1920:05
更多闪存
Re:12年前的作品──《美绿中国象棋》制作过程及算法简介
@DiryBoy
电脑也看不下去了--霸王降臨
Re:用JavaScript玩转计算机图形学(一)光线追踪入门
写得非常好,可以很朋理论没有看懂,呵--网易小前
Re:C++强大背后
c++学习中--仙道男
Re:《编程之美:分层遍历二叉树》的另外两个实现
考虑一次广度遍历,把节点和它的度都存下来,记录在一个vector中~typedefintDataType;typedefstructBstNode{DataTypedata...--siegeweb
Re:用JavaScript玩转游戏物理(一)运动学模拟与粒子系统
很酷哦关注学习下:)--lmh2072005
2011年6月(1)
2010年9月(2)
2010年8月(1)
2010年7月(1)
2010年6月(3)
2010年5月(2)
2010年4月(5)
2010年3月(4)
2010年2月(4)
女程序员之家
边走边拍
Rss
计算机图形学(5)
Rss
人工智能(1)
Rss
数据结构和算法(6)
Rss
物理模拟(1)
Rss
游戏编程(2)
Rss
游戏引擎(4)
Rss
杂谈(4)
爱丽丝的发丝──《爱丽丝惊魂记:疯狂再临》制作点滴(45046)
C++强大背后(28128)
史上最强女游戏程序员(25950)
C++/C#/F#/Java/JS/Lua/Python/Ruby渲染比试(23025)
用JavaScript玩转计算机图形学(一)光线追踪入门(21145)
www.spiga.com.mx
Copyright©2011MiloYip
博客园
0
(请您对文章做出评价)
Posts-23,Articles-0,Comments-1089
Milo的游戏开发
《编程之美:求二叉树中节点的最大距离》的另一个解法
2010-02-2503:32byMiloYip,7408visits,编辑
昨天花了一个晚上为《编程之美》,在豆瓣写了一篇书评
问题定义
如果我们把二叉树看成一个图,父子节点之间的连线看成是双向的,我们姑且定义"距离"为两节点之间边的个数。写一个程序求一棵二叉树中相距最远的两个节点之间的距离。书上的解法
书中对这个问题的分析是很清楚的,我尝试用自己的方式简短覆述。计算一个二叉树的最大距离有两个情况:
情况A:路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。
情况B:路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。
只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离。
我也想不到更好的分析方法。
但接着,原文的实现就不如上面的清楚(源码可从
01 | //数据结构定义 |
02 | struct NODE |
03 | { |
04 | NODE*pLeft; //左子树 |
05 | NODE*pRight; //右子树 |
06 | int nMaxLeft; //左子树中的最长距离 |
07 | int nMaxRight; //右子树中的最长距离 |
08 | char chValue; //该节点的值 |
09 | }; |
10 |
11 | int
|
12 |
13 | //寻找树中最长的两段距离 |
14 | void
|
15 | { |
16 | //遍历到叶子节点,返回 |
17 | if (pRoot==NULL) |
18 | { |
19 | return ; |
20 | } |
21 |
22 | //如果左子树为空,那么该节点的左边最长距离为0 |
23 | if (pRoot->pLeft==NULL) |
24 | { |
25 | pRoot->nMaxLeft=0; |
26 | } |
27 |
28 | //如果右子树为空,那么该节点的右边最长距离为0 |
29 | if (pRoot->pRight==NULL) |
30 | { |
31 | pRoot->nMaxRight=0; |
32 | } |
33 |
34 | //如果左子树不为空,递归寻找左子树最长距离 |
35 | if (pRoot->pLeft!=NULL) |
36 | { |
37 | FindMaxLen(pRoot->pLeft); |
38 | } |
39 |
40 | //如果右子树不为空,递归寻找右子树最长距离 |
41 | if (pRoot->pRight!=NULL) |
42 | { |
43 | FindMaxLen(pRoot->pRight); |
44 | } |
45 |
46 | //计算左子树最长节点距离 |
47 | if (pRoot->pLeft!=NULL) |
48 | { |
49 | int nTempMax=0; |
50 | if (pRoot->pLeft->nMaxLeft>pRoot->pLeft->nMaxRight) |
51 | { |
52 | nTempMax=pRoot->pLeft->nMaxLeft; |
53 | } |
54 | else |
55 | { |
56 | nTempMax=pRoot->pLeft->nMaxRight; |
57 | } |
58 | pRoot->nMaxLeft=nTempMax+1; |
59 | } |
60 |
61 | //计算右子树最长节点距离 |
62 | if (pRoot->pRight!=NULL) |
63 | { |
64 | int nTempMax=0; |
65 | if (pRoot->pRight->nMaxLeft>pRoot->pRight->nMaxRight) |
66 | { |
67 | nTempMax=pRoot->pRight->nMaxLeft; |
68 | } |
69 | else |
70 | { |
71 | nTempMax=pRoot->pRight->nMaxRight; |
72 | } |
73 | pRoot->nMaxRight=nTempMax+1; |
74 | } |
75 |
76 | //更新最长距离 |
77 | if (pRoot->nMaxLeft+pRoot->nMaxRight>nMaxLen) |
78 | { |
79 | nMaxLen=pRoot->nMaxLeft+pRoot->nMaxRight; |
80 | } |
81 | } |
算法加入了侵入式(intrusive)的资料nMaxLeft,nMaxRight
使用了全局变量nMaxLen。每次使用要额外初始化。而且就算是不同的独立资料,也不能在多个线程使用这个函数
逻辑比较复杂,也有许多NULL相关的条件测试。
我的尝试
我认为这个问题的核心是,情况A及B需要不同的信息:A需要子树的最大深度,B需要子树的最大距离。只要函数能在一个节点同时计算及传回这两个信息,代码就可以很简单:01 | #include<iostream> |
02 |
03 | using namespace std; |
04 |
05 | struct NODE |
06 | { |
07 | NODE*pLeft; |
08 | NODE*pRight; |
09 | }; |
10 |
11 | struct
|
12 | { |
13 | int nMaxDistance; |
14 | int nMaxDepth; |
15 | }; |
16 |
17 | RESULTGetMaximumDistance(NODE*root) |
18 | { |
19 | if (!root) |
20 | { |
21 | RESULTempty={0,-1}; //trick:nMaxDepthis-1andthencallerwillplus1tobalanceitaszero. |
22 | return empty; |
23 | } |
24 |
25 | RESULTlhs=GetMaximumDistance(root->pLeft); |
26 | RESULTrhs=GetMaximumDistance(root->pRight); |
27 |
28 | RESULTresult; |
29 | result.nMaxDepth=max(lhs.nMaxDepth+1,rhs.nMaxDepth+1); |
30 | result.nMaxDistance=max(max(lhs.nMaxDistance,rhs.nMaxDistance),lhs.nMaxDepth+rhs.nMaxDepth+2); |
31 | return result; |
32 | } |
为了减少NULL的条件测试,进入函数时,如果节点为NULL,会传回一个empty变量。比较奇怪的是empty.nMaxDepth=-1,目的是让调用方+1后,把当前的不存在的(NULL)子树当成最大深度为0。
除了提高了可读性,这个解法的另一个优点是减少了O(节点数目)大小的侵入式资料,而改为使用O(树的最大深度)大小的栈空间。这个设计使函数完全没有副作用(sideeffect)。
测试代码
以下也提供测试代码给读者参考(页数是根据第7次印刷,节点是由上至下、左至右编号):01 | void
int
int left, int right) |
02 | { |
03 | if (left!=-1) |
04 | nodes[parent].pLeft=&nodes[left]; |
05 |
06 | if (right!=-1) |
07 | nodes[parent].pRight=&nodes[right]; |
08 | } |
09 |
10 | void
|
11 | { |
12 | //P.241Graph3-12 |
13 | NODEtest1[9]={0}; |
14 | Link(test1,0,1,2); |
15 | Link(test1,1,3,4); |
16 | Link(test1,2,5,6); |
17 | Link(test1,3,7,-1); |
18 | Link(test1,5,-1,8); |
19 | cout<< "test1:" <<GetMaximumDistance(&test1[0]).nMaxDistance<<endl; |
20 |
21 | //P.242Graph3-13left |
22 | NODEtest2[4]={0}; |
23 | Link(test2,0,1,2); |
24 | Link(test2,1,3,-1); |
25 | cout<< "test2:" <<GetMaximumDistance(&test2[0]).nMaxDistance<<endl; |
26 |
27 | //P.242Graph3-13right |
28 | NODEtest3[9]={0}; |
29 | Link(test3,0,-1,1); |
30 | Link(test3,1,2,3); |
31 | Link(test3,2,4,-1); |
32 | Link(test3,3,5,6); |
33 | Link(test3,4,7,-1); |
34 | Link(test3,5,-1,8); |
35 | cout<< "test3:" <<GetMaximumDistance(&test3[0]).nMaxDistance<<endl; |
36 |
37 | //P.242Graph3-14 |
38 | //SameasGraph3-2,nottest |
39 |
40 | //P.243Graph3-15 |
41 | NODEtest4[9]={0}; |
42 | Link(test4,0,1,2); |
43 | Link(test4,1,3,4); |
44 | Link(test4,3,5,6); |
45 | Link(test4,5,7,-1); |
46 | Link(test4,6,-1,8); |
47 | cout<< "test4:" <<GetMaximumDistance(&test4[0]).nMaxDistance<<endl; |
48 | } |
分类:
数据结构和算法
绿色通道:好文要顶关注我收藏该文
荣誉:
+加关注
«
博主前一篇:
»
博主后一篇:
Categories:
21条回复
1929379yeka
Milo又熬夜啦.......
陈硕
第19~21行有线程安全问题:
staticconstRESULTempty={0,-1};//trick:nMaxDepthis-1andthencallerwillplus1tobalanceitaszero.
if(!root)
returnempty;
建议改为:
if(!root){RESULTempty={0,-1};//trick:nMaxDepthis-1andthencallerwillplus1tobalanceitaszero.
returnempty;
//trustcompiler,PODdatawillbeoptimizedwell.
}因为按标准,functionstaticvariable只在函数第一次调用时初始化,这个的初始化只有在最新的编译器里才是线程安全的。
在旧的编译器(GCC3及以前)上,原来的写法可能会读到partialinitialized'empty'变量,如果两个线程同时(首次)调用GetMaximumDistance的话。
秋醒半梦时[未注册用户]2010-02-2509:12
这不就是求树的直径的问题吗?
树的直径最简单的解法(无论是几叉):
从任意一点i一次BFS找到树中与他距离最远的点j,从j再一次BFS找到树中里j最远的点k,那么D[j][k](j与k的距离)即为答案。
稍微好理解的方法:树形动态规划
Dbger
如果非要考虑多线程安全,我倾向于用“全局变量”来表示这些常用的常量,就和向量,矩阵类中一些单元向量,单元矩阵等。
JeffreyZhao
我觉得直接把递归语意翻译过来最直接和清晰吧:
01 | type
|
02 | |Node of BinaryTree*BinaryTree |
03 | |Empty |
04 |
05 | let rec height(tree:BinaryTree)= |
06 | match tree with |
07 | |Empty->0 |
08 | |Node(l,r)->1+ max (heightl)(heightr) |
09 |
10 | let rec calculate(tree:BinaryTree)= |
11 | match tree with |
12 | |Empty->0 |
13 | |Node(l,r)-> |
14 | (heightl)+(heightr) |
15 | |> max (calculatel) |
16 | |> max (calculater) |
ToddWei
进行两次BFS:先从树根A出发进行广度优先搜索(BFS),找到最远的结点B,然后再从结点B出BFS,找到离B最远的结点C,BC就是最大距离。
下面是正确性证明
假设存在结点X和Y,它们的距离是所有结点中最大的;分两种情况讨论:
1.若路径XY与路径AB有交点O,
...A
...|
X-O--Y
...|
...B
由于|OB|>=|OX|且|OB|>=|OY|,所以,|BX|>=|XY|,|BY|>=|XY|。即从B出发可以构造出最长路径。
2.若路径XY与路径AB无交点,
A...BX...Y
A是树根,XY与B分属不同的子树,假设XY的最近祖先为O,由于
|AB|>=|AO|+|AX|,所以|BY|=|AB|+|AO|+|OY|>|XY|。即从B出发构造出长于XY的路径,与假设XY是最长路径矛盾。
MiloYip
@陈硕
我覺得兩個方法都可以解決潛在的多線程問題。我現在先相信compiler,改用了陳碩的寫法。
從另一個角度看這個問題,localstaticvariable是會做成sideeffect,所以thread-safe會不成立。
MiloYip
我未學過任何一個functionalprogramming語言。希望趙大能指正不對的地方。
用FP的確可以增加可讀性,同時能減少錯誤的機會。
FP能對purefunction用自動的cacheoptimization,這是優點也是缺點。如果沒有這優化,在你提供的代碼中,height的調用次數估計是O(n^2);而有了這優化,就需要O(n)的空間去儲存n個height()的運算結果。而這優化我估計應該需要做tablelookup,帶來額外overhead。
我的嘗試中,並不需要O(n)的額外空間,而且仍維持每節點只遍歷一次。
又反過來說,在效能上,FP的好處是可以自動做並行,用procedural語言手動做這個就會顯得複雜。
MiloYip
@秋醒半梦时
多謝你們的回應,我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
這該我找到一點參考文章:
發現前一篇文章基本上和Jeffrey的嘗試一樣,但用proceduralprogramming會有O(N^2)的height()調用。我覺得我寫的邊界條件(那個trick)可能不需要,今晚回家試試。
第二篇談到的幾個詞彙我都不太認識,可能要再多看一些參考。也想請教,用BFS的方法會比現時的方法簡單或高效麼?還是現時的方法實際上有錯誤?
ToddWei
BFS是O(N)的,所以复杂度更低。特别是基于BFS的方法不局限于2叉树,而前面递归方法在多叉树情况下复杂度会更高。
JeffreyZhao
其实你的算法还是用了O(h)的空间啦,h是高度,(非尾)递归算法嘛,栈空间是省不了的。
的确这里height会反复调用,所以如果必要的话,还是要做memorization的。
作了momorization以后,时间和空间“复杂度”和你的过程式算法是一致的了。
MiloYip
JeffreyZhao:
@MiloYip
其实你的算法还是用了O(h)的空间啦,h是高度,(非尾)递归算法嘛,栈空间是省不了的。
的确这里height会反复调用,所以如果必要的话,还是要做memorization的。
作了momorization以后,时间和空间“复杂度”和你的过程式算法是一致的了。
本文也提及,我的嘗試用了O(h)的棧空間代替原文的O(n)intrusivedata,而你寫的height函數的memorization空間是O(n)。因為h<=n,O(h)應該是比O(n)好吧。
MiloYip
@MiloYip
BFS是O(N)的,所以复杂度更低。特别是基于BFS的方法不局限于2叉树,而前面递归方法在多叉树情况下复杂度会更高。
我的嘗試也是O(N),而且只需遍歷一次。跟據你的描述,BFS要做兩次,而且要加入parent?不過對於一般的多叉樹,可能BFS的方法是最好的方法。
ToddWei
哦,是的,你的递归也是O(N),开始分析错了。
树哪个结点作为parent没关系,任选即可。图论里面对树的一种定义方式是:具有n个结点和n+1条边的连通图。
郑晖
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
秋醒半梦时[未注册用户]2010-02-2517:03
@MiloYip
哦,是的,你的递归也是O(N),开始分析错了。
树哪个结点作为parent没关系,任选即可。图论里面对树的一种定义方式是:具有n个结点和n+1条边的连通图。
我所了解的树的定义是:一个无环的无向图
MiloYip
郑晖:
@MiloYip
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
謝謝鄭老師的數學指導。在網上找到了關於這個的定義:
郑晖
引用郑晖:
@MiloYip
>>我方知道這個「距離」應該是叫「直徑」(TreeDiameter)。
的确是“直径”——在数学中直径的定义是:一个距离空间中任意两点间距离的上确界(supremum)。
謝謝鄭老師的數學指導。在網上找到了關於這個的定義:
上面对的直径定义尚不足够general,它只提到了欧氏空间(EuclideanspaceR^n),
实际可扩展到更广泛的距离空间(metricspace)。事实上,你这里提到的树就不是欧氏空间(因为这里的距离并非欧氏距离)。
MiloYip
是的,我理解只要是metric就可以定義diameter。
flyinghearts
这是我的解法:
欢迎大家指正。
gzroy
这是我的递归解法,欢迎交流:
注册用户登录后才能发表评论,请
登录或
注册,
最新IT新闻:
·
·
·
·
·
»
最新知识库文章:
·
Scrum实施经验
·
·
·
·
»
About
MiloYip是香港同胞,现任职于上海麻辣马,开发多平台游戏项目。在高一高二时兼职开发游戏《王子传奇》后,便潜心向学(伪),得到认知科学学士和系统工程及工程管理学哲学硕士后,于大学里做游戏科技的相关项目,直至2008年才来到上海,再次投身游戏业界,之前的作品是《美食从天而降》Xbox360/PS3/Wii/PC。
因为写了一个书评而认识到很多朋友,决定在内地设一个blog,和大家分享交流。
昵称:
园龄:
荣誉:
粉丝:
关注:
+加关注
最新闪存
在上海最後一個晚上。三年裡,在兩間公司工作過,在兩處居所暫住過,開發過兩個遊戲,製造過兩個兒女,撰寫過兩編雜誌稿,結識到無數好友。不枉此行。12-1920:05
最新评论
电脑也看不下去了--霸王降臨
写得非常好,可以很朋理论没有看懂,呵--网易小前
c++学习中--仙道男
考虑一次广度遍历,把节点和它的度都存下来,记录在一个vector中~typedefintDataType;typedefstructBstNode{DataTypedata...--siegeweb
很酷哦关注学习下:)--lmh2072005
随笔档案
我参加的小组
日历
| ||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 |
---|---|---|---|---|---|---|
31 | 1 | 2 | 3 | 4 | 5 | |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 | 24 | 27 | |||
28 | 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 |
随笔分类
阅读排行榜
Copyright©2011MiloYip
相关文章推荐
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法(转)
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法(觉得比书上的好)
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 《编程之美: 求二叉树中节点的最大距离》的另一个解法
- 求二叉树中节点的最大距离【编程之美解法】
- 编程之美: 求二叉树中节点的最大距离》的好解法
- 求二叉树中节点的最大距离 的另一个解法
- 《编程之美》 3.8 求二叉树中节点的最大距离
- 求二叉树中节点的最大距离递归解法
- 变形二叉树中节点的最大距离(树的最长路径)——非递归解法
- 《编程之美》读书笔记12: 3.8 求二叉树中节点的最大距离