defFindLCA(node1,node2):# Special cases.ifnotnode1ornotnode2:returnNoneifnode1isnode2:returnnode1# Get the first branch path.ancestors1=set()whilenode1:ancestors1.add(node1)node1=node1.parent# Check if any ancestor of node2 is in the first branch path.whilenode2:ifnode2inancestors1:returnnode2# Got it, the LCA.node2=node2.parent# These two nodes have no common ancestor.returnNone
defFindLCA(node1,node2):# Special cases.ifnotnode1ornotnode2:returnNoneifnode1isnode2:returnnode1# Gets each node's depth.depth=lambdanode:depth(node.parent)+1ifnodeelse0depth1=depth(node1)depth2=depth(node2)# Pulls up the lower node and makes the two nodes in the same depth.mindepth=min(depth1,depth2)foriinxrange(depth1-mindepth):node1=node1.parentforiinxrange(depth2-mindepth):node2=node2.parent# Finds the common ancestor.whilenode1andnode2:ifnode1isnode2:returnnode1node1=node1.parentnode2=node2.parentreturnNone
classDir:(Undef,Left,Right)=range(3)defFindNodes(root,nodeSet,findAll=True):ifnotrootornotnodeSet:returnNonepathDict={}path=[]curr=rootwhilecurrorpath:whilecurr:# Go down along left branchpath.append((curr,Dir.Left))ifcurrinnodeSet:pathDict[curr]=list(path)nodeSet.remove(curr)ifnotnodeSetornotfindAll:returnpathDictcurr=curr.left(curr,dir)=path.pop()whiledir==Dir.Right:# Back from right branchifnotpath:returnpathDict(curr,dir)=path.pop()path.append((curr,Dir.Right))# Trun to right from leftcurr=curr.rightreturnpathDict
其中 Dir 这个类相当于是一个枚举,用来定义当前的分支方向。FindNodes 除了需要二叉树根结点外,还需要一个待查找的结点集合。这个函数可以在二叉树中找到所有(或第一个)待查找结点的分支路径,并返回一个字典(结点
--> 路径)。
defFindLCA(root,node1,node2):# Special cases.ifnotrootornotnode1ornotnode2:returnNoneifnode1isnode2:returnnode1# Try to find the two nodes in the tree, and get their branch paths.nodeSet=set([node1,node2])pathDict=FindNodes(root,nodeSet)ifnodeSet:returnNonepath1=[i[0]foriinpathDict[node1]]path2=[i[0]foriinpathDict[node2]]# Compare the two paths, find out the LCA.lca=NoneminLen=min(len(path1),len(path2))foriinxrange(minLen):ifpath1[i]isnotpath2[i]:breaklca=path1[i]returnlca
defFindLCA(root,node1,node2):nodeset=set([node1,node2])# Also supports 3 or more nodes.s=[]# A stack to help performing N-L-R traversing.lca=None# Records the most possible least common ancestor.mindepth=-1# The depth of lca.whilerootors:ifroot:ifrootinnodeset:nodeset.remove(root)ifmindepth<0:# Yeah, found the first node. The lca must be itself or already in s.lca=rootmindepth=len(s)ifnotnodeset:breaks.append(root)root=root.leftelse:root=s.pop()ifmindepth>len(s):lca=rootmindepth=len(s)root=root.rightreturnNoneifnodesetelselca