Range Minimum Query and Lowest Common Ancestor
2008-12-29 22:29
399 查看
Introduction
TheproblemoffindingtheLowestCommonAncestor(LCA)ofapairofnodesinarootedtreehasbeenstudiedmorecarefullyinthesecondpartofthe20thcenturyandnowisfairlybasicinalgorithmicgraphtheory.Thisproblemisinterestingnotonlyforthetrickyalgorithmsthatcanbeusedtosolveit,butforitsnumerousapplicationsinstringprocessingandcomputationalbiology,forexample,whereLCAisusedwithsuffixtreesorothertree-likestructures.
Let'sconsideralessabstractexampleofLCA:thetreeoflife.It'sawell-knownfactthatthecurrenthabitantsofEarthevolvedfromotherspecies.Thisevolvingstructurecanberepresentedasatree,inwhichnodesrepresentspecies,andthesonsofsomenoderepresentthedirectlyevolvedspecies.Nowspecieswithsimilarcharacteristicsaredividedintogroups.ByfindingtheLCAofsomenodesinthistreewecanactuallyfindthecommonparentoftwospecies,andwecandeterminethatthesimilarcharacteristicstheyshareareinheritedfromthatparent.
RangeMinimumQuery(RMQ)isusedonarraystofindthepositionofanelementwiththeminimumvaluebetweentwospecifiedindices.WewillseelaterthattheLCAproblemcanbereducedtoarestrictedversionofanRMQproblem,inwhichconsecutivearrayelementsdifferbyexactly1.
However,RMQsarenotonlyusedwithLCA.Theyhaveanimportantroleinstringpreprocessing,wheretheyareusedwithsuffixarrays(anewdatastructurethatsupportsstringsearchesalmostasfastassuffixtrees,butuseslessmemoryandlesscodingeffort).
InthistutorialwewillfirsttalkaboutRMQ.Wewillpresentmanyapproachesthatsolvetheproblem--someslowerbuteasiertocode,andothersfaster.InthesecondpartwewilltalkaboutthestrongrelationbetweenLCAandRMQ.FirstwewillreviewtwoeasyapproachesforLCAthatdon'tuseRMQ;thenshowthattheRMQandLCAproblemsareequivalent;and,attheend,we'lllookathowtheRMQproblemcanbereducedtoitsrestrictedversion,aswellasshowafastalgorithmforthisparticularcase.
Notations
Supposethatanalgorithmhaspreprocessingtimef(n)andquerytimeg(n).Thenotationfortheoverallcomplexityforthealgorithmis<f(n),g(n)>.
WewillnotethepositionoftheelementwiththeminimumvalueinsomearrayAbetweenindicesiandjwithRMQA(i,j).
ThefurthestnodefromtherootthatisanancestorofbothuandvinsomerootedtreeTisLCAT(u,v).
RangeMinimumQuery(RMQ)
GivenanarrayA[0,N-1]findthepositionoftheelementwiththeminimumvaluebetweentwogivenindices.
TrivialalgorithmsforRMQ
Foreverypairofindices(i,j)storethevalueofRMQA(i,j)inatableM[0,N-1][0,N-1].Trivialcomputationwillleadustoan<O(N3),O(1)>complexity.However,byusinganeasydynamicprogrammingapproachwecanreducethecomplexityto<O(N2),O(1)>.Thepreprocessingfunctionwilllooksomethinglikethis:
voidprocess1(intM[MAXN][MAXN],intA[MAXN],intN)
{
inti,j;
for(i=0;i<N;i++)
M[i][i]=i;
for(i=0;i<N;i++)
for(j=i+1;j<N;j++)
if(A[M[i][j-1]]<A[j])
M[i][j]=M[i][j-1];
else
M[i][j]=j;
}
ThistrivialalgorithmisquiteslowandusesO(N2)memory,soitwon'tworkforlargecases.
An<O(N),O(sqrt(N))>solution
Aninterestingideaistosplitthevectorinsqrt(N)pieces.WewillkeepinavectorM[0,sqrt(N)-1]thepositionfortheminimumvalueforeachsection.McanbeeasilypreprocessedinO(N).Hereisanexample:
Nowlet'sseehowcanwecomputeRMQA(i,j).Theideaistogettheoverallminimumfromthesqrt(N)sectionsthatlieinsidetheinterval,andfromtheendandthebeginningofthefirstandthelastsectionsthatintersecttheboundsoftheinterval.TogetRMQA(2,7)intheaboveexampleweshouldcompareA[2],A[M[1]],A[6]andA[7]andgetthepositionoftheminimumvalue.It'seasytoseethatthisalgorithmdoesn'tmakemorethan3*sqrt(N)operationsperquery.
Themainadvantagesofthisapproacharethatistoquicktocode(aplusforTopCoder-stylecompetitions)andthatyoucanadaptittothedynamicversionoftheproblem(whereyoucanchangetheelementsofthearraybetweenqueries).
SparseTable(ST)algorithm
AbetterapproachistopreprocessRMQforsubarraysoflength2kusingdynamicprogramming.WewillkeepanarrayM[0,N-1][0,logN]whereM[i][j]istheindexoftheminimumvalueinthesubarraystartingatihavinglength2j.Hereisanexample:
ForcomputingM[i][j]wemustsearchfortheminimumvalueinthefirstandsecondhalfoftheinterval.It'sobviousthatthesmallpieceshave2j-1length,sotherecurrenceis:
Thepreprocessingfunctionwilllooksomethinglikethis:
voidprocess2(intM[MAXN][LOGMAXN],intA[MAXN],intN)
{
inti,j;
//initializeMfortheintervalswithlength1
for(i=0;i<N;i++)
M[i][0]=i;
//computevaluesfromsmallertobiggerintervals
for(j=1;1<<j<=N;j++)
for(i=0;i+(1<<j)-1<N;i++)
if(A[M[i][j-1]]<A[M[i+(1<<(j-1))][j-1]])
M[i][j]=M[i][j-1];
else
M[i][j]=M[i+(1<<(j-1))][j-1];
}
Oncewehavethesevaluespreprocessed,let'sshowhowwecanusethemtocalculateRMQA(i,j).Theideaistoselecttwoblocksthatentirelycovertheinterval[i..j]andfindtheminimumbetweenthem.Letk=[log(j-i+1)].ForcomputingRMQA(i,j)wecanusethefollowingformula:
So,theoverallcomplexityofthealgorithmis<O(NlogN),O(1)>.
Segmenttrees
ForsolvingtheRMQproblemwecanalsousesegmenttrees.Asegmenttreeisaheap-likedatastructurethatcanbeusedformakingupdate/queryoperationsuponarrayintervalsinlogarithmicaltime.Wedefinethesegmenttreefortheinterval[i,j]inthefollowingrecursivemanner:
·thefirstnodewillholdtheinformationfortheinterval[i,j]
·ifi<jtheleftandrightsonwillholdtheinformationfortheintervals[i,(i+j)/2]and[(i+j)/2+1,j]
NoticethattheheightofasegmenttreeforanintervalwithNelementsis[logN]+1.Hereishowasegmenttreefortheinterval[0,9]wouldlooklike:
Thesegmenttreehasthesamestructureasaheap,soifwehaveanodenumberedxthatisnotaleaftheleftsonofxis2*xandtherightson2*x+1.
ForsolvingtheRMQproblemusingsegmenttreesweshoulduseanarrayM[1,2*2[logN]+1]whereM[i]holdstheminimumvaluepositionintheintervalassignedtonodei.AtthebeginningallelementsinMshouldbe-1.Thetreeshouldbeinitializedwiththefollowingfunction(bandearetheboundsofthecurrentinterval):
voidinitialize(intnode,intb,inte,intM[MAXIND],intA[MAXN],intN)
{
if(b==e)
M[node]=b;
else
{
//computethevaluesintheleftandrightsubtrees
initialize(2*node,b,(b+e)/2,M,A,N);
initialize(2*node+1,(b+e)/2+1,e,M,A,N);
//searchfortheminimumvalueinthefirstand
//secondhalfoftheinterval
if(A[M[2*node]]<=A[M[2*node+1]])
M[node]=M[2*node];
else
M[node]=M[2*node+1];
}
}
Thefunctionabovereflectsthewaythetreeisconstructed.Whencalculatingtheminimumpositionforsomeintervalweshouldlookatthevaluesofthesons.Youshouldcallthefunctionwithnode=1,b=0ande=N-1.
Wecannowstartmakingqueries.Ifwewanttofindthepositionoftheminimumvalueinsomeinterval[i,j]weshouldusethenexteasyfunction:
intquery(intnode,intb,inte,intM[MAXIND],intA[MAXN],inti,intj)
{
intp1,p2;
//ifthecurrentintervaldoesn'tintersect
//thequeryintervalreturn-1
if(i>e||j<b)
return-1;
//ifthecurrentintervalisincludedin
//thequeryintervalreturnM[node]
if(b>=i&&e<=j)
returnM[node];
//computetheminimumpositioninthe
//leftandrightpartoftheinterval
p1=query(2*node,b,(b+e)/2,M,A,i,j);
p2=query(2*node+1,(b+e)/2+1,e,M,A,i,j);
//returnthepositionwheretheoverall
//minimumis
if(p1==-1)
returnM[node]=p2;
if(p2==-1)
returnM[node]=p1;
if(A[p1]<=A[p2])
returnM[node]=p1;
returnM[node]=p2;
}
Youshouldcallthisfunctionwithnode=1,b=0ande=N-1,becausetheintervalassignedtothefirstnodeis[0,N-1].
It'seasytoseethatanyqueryisdoneinO(logN).Noticethatwestopwhenwereachcompletelyin/outintervals,soourpathinthetreeshouldsplitonlyonetime.
Usingsegmenttreeswegetan<O(N),O(logN)>algorithm.Segmenttreesareverypowerful,notonlybecausetheycanbeusedforRMQ.Theyareaveryflexibledatastructure,cansolveeventhedynamicversionofRMQproblem,andhavenumerousapplicationsinrangesearchingproblems.
LowestCommonAncestor(LCA)
GivenarootedtreeTandtwonodesuandv,findthefurthestnodefromtherootthatisanancestorforbothuandv.Hereisanexample(therootofthetreewillbenode1forallexamplesinthiseditorial):
An<O(N),O(sqrt(N))>solution
Dividingourinputintoequal-sizedpartsprovestobeaninterestingwaytosolvetheRMQproblem.ThismethodcanbeadaptedfortheLCAproblemaswell.Theideaistosplitthetreeinsqrt(H)parts,wereHistheheightofthetree.Thus,thefirstsectionwillcontainthelevelsnumberedfrom0tosqrt(H)-1,thesecondwillcontainthelevelsnumberedfromsqrt(H)to2*sqrt(H)-1,andsoon.Hereishowthetreeintheexampleshouldbedivided:
Now,foreachnode,weshouldknowtheancestorthatissituatedonthelastleveloftheuppernextsection.WewillpreprocessthisvaluesinanarrayP[1,MAXN].HereishowPshouldlooklikeforthetreeintheexample(forsimplity,foreverynodeiinthefirstsectionletP[i]=1):
Noticethatforthenodessituatedonthelevelsthatarethefirstonesinsomesections,P[i]=T[i].WecanpreprocessPusingadepthfirstsearch(T[i]isthefatherofnodeiinthetree,nris[sqrt(H)]andL[i]isthelevelofthenodei):
voiddfs(intnode,intT[MAXN],intN,intP[MAXN],intL[MAXN],intnr){
intk;
//ifnodeissituatedinthefirst
//sectionthenP[node]=1
//ifnodeissituatedatthebeginning
//ofsomesectionthenP[node]=T[node]
//ifnoneofthosetwocasesoccurs,then
//P[node]=P[T[node]]
if(L[node]<nr)
P[node]=1;
else
if(!(L[node]%nr))
P[node]=T[node];
else
P[node]=P[T[node]];
foreachsonkofnode
dfs(k,T,N,P,L,nr);
}
Now,wecaneasilymakequeries.ForfindingLCA(x,y)wewewillfirstfindinwhatsectionitlays,andthentriviallycomputeit.Hereisthecode:
intLCA(intT[MAXN],intP[MAXN],intL[MAXN],intx,inty)
{
//aslongasthenodeinthenextsectionof
//xandyisnotonecommonancestor
//wegetthenodesituatedonthesmaller
//levercloser
while(P[x]!=P[y])
if(L[x]>L[y])
x=P[x];
else
y=P[y];
//nowtheyareinthesamesection,sowetriviallycomputetheLCA
while(x!=y)
if(L[x]>L[y])
x=T[x];
else
y=T[y];
returnx;
}
Thisfunctionmakesatmost2*sqrt(H)operations.Usingthisapproachwegetan<O(N),O(sqrt(H))>algorithm,whereHistheheightofthetree.IntheworstcaseH=N,sotheoverallcomplexityis<O(N),O(sqrt(N))>.Themainadvantageofthisalgorithmisquickcoding(anaverageDivision1codershouldn'tneedmorethan15minutestocodeit).
Anothereasysolutionin<O(NlogN,O(logN)>
Ifweneedafastersolutionforthisproblemwecouldusedynamicprogramming.First,let'scomputeatableP[1,N][1,logN]whereP[i][j]isthe2j-thancestorofi.Forcomputingthisvaluewemayusethefollowingrecursion:
Thepreprocessingfunctionshouldlooklikethis:
voidprocess3(intN,intT[MAXN],intP[MAXN][LOGMAXN])
{
inti,j;
//weinitializeeveryelementinPwith-1
for(i=0;i<N;i++)
for(j=0;1<<j<N;j++)
P[i][j]=-1;
//thefirstancestorofeverynodeiisT[i]
for(i=0;i<N;i++)
P[i][0]=T[i];
//bottomupdynamicprograming
for(j=1;1<<j<N;j++)
for(i=0;i<N;i++)
if(P[i][j-1]!=-1)
P[i][j]=P[P[i][j-1]][j-1];
}
ThistakesO(NlogN)timeandspace.Nowlet'sseehowwecanmakequeries.LetL[i]bethelevelofnodeiinthetree.WemustobservethatifpandqareonthesamelevelinthetreewecancomputeLCA(p,q)usingameta-binarysearch.So,foreverypowerjof2(betweenlog(L[p])and0,indescendingorder),ifP[p][j]!=P[q][j]thenweknowthatLCA(p,q)isonahigherlevelandwewillcontinuesearchingforLCA(p=P[p][j],q=P[q][j]).Attheend,bothpandqwillhavethesamefather,soreturnT[p].Let'sseewhathappensifL[p]!=L[q].Assume,withoutlossofgenerality,thatL[p]<L[q].Wecanusethesamemeta-binarysearchforfindingtheancestorofpsituatedonthesamelevelwithq,andthenwecancomputetheLCAasdescribedbelow.Hereishowthequeryfunctionshouldlook:
intquery(intN,intP[MAXN][LOGMAXN],intT[MAXN],
intL[MAXN],intp,intq)
{
inttmp,log,i;
//ifpissituatedonahigherlevelthanqthenweswapthem
if(L[p]<L[q])
tmp=p,p=q,q=tmp;
//wecomputethevalueof[log(L[p)]
for(log=1;1<<log<=L[p];log++);
log--;
//wefindtheancestorofnodepsituatedonthesamelevel
//withqusingthevaluesinP
for(i=log;i>=0;i--)
if(L[p]-(1<<i)>=L[q])
p=P[p][i];
if(p==q)
returnp;
//wecomputeLCA(p,q)usingthevaluesinP
for(i=log;i>=0;i--)
if(P[p][i]!=-1&&P[p][i]!=P[q][i])
p=P[p][i],q=P[q][i];
returnT[p];
}
Now,wecanseethatthisfunctionmakesatmost2*log(H)operations,whereHistheheightofthetree.IntheworstcaseH=N,sotheoverallcomplexityofthisalgorithmis<O(NlogN),O(logN)>.Thissolutioniseasytocodetoo,andit'sfasterthanthepreviousone.
ReductionfromLCAtoRMQ
Now,let'sshowhowwecanuseRMQforcomputingLCAqueries.Actually,wewillreducetheLCAproblemtoRMQinlineartime,soeveryalgorithmthatsolvestheRMQproblemwillsolvetheLCAproblemtoo.Let'sshowhowthisreductioncanbedoneusinganexample:
clicktoenlargeimage
NoticethatLCAT(u,v)istheclosestnodefromtherootencounteredbetweenthevisitsofuandvduringadepthfirstsearchofT.So,wecanconsiderallnodesbetweenanytwoindicesofuandvintheEulerTourofthetreeandthenfindthenodesituatedonthesmallestlevelbetweenthem.Forthis,wemustbuildthreearrays:
·E[1,2*N-1]-thenodesvisitedinanEulerTourofT;E[i]isthelabelofi-thvisitednodeinthetour
·L[1,2*N-1]-thelevelsofthenodesvisitedintheEulerTour;L[i]isthelevelofnodeE[i]
·H[1,N]-H[i]istheindexofthefirstoccurrenceofnodeiinE(anyoccurrencewouldbegood,soit'snotbadifweconsiderthefirstone)
AssumethatH<H[v](otherwiseyoumustswapuandv).It'seasytoseethatthenodesbetweenthefirstoccurrenceofuandthefirstoccurrenceofvareE[H[u]...H[v]].Now,wemustfindthenodesituatedonthesmallestlevel.Forthis,wecanuseRMQ.So,LCAT(u,v)=E[RMQL(H[u],H[v])](rememberthatRMQreturnstheindex).HereishowE,LandHshouldlookfortheexample:
clicktoenlargeimage
NoticethatconsecutiveelementsinLdifferbyexactly1.
FromRMQtoLCA
WehaveshownthattheLCAproblemcanbereducedtoRMQinlineartime.HerewewillshowhowwecanreducetheRMQproblemtoLCA.ThismeansthatweactuallycanreducethegeneralRMQtotherestrictedversionoftheproblem(whereconsecutiveelementsinthearraydifferbyexactly1).Forthisweshouldusecartesiantrees.
ACartesianTreeofanarrayA[0,N-1]isabinarytreeC(A)whoserootisaminimumelementofA,labeledwiththepositioniofthisminimum.TheleftchildoftherootistheCartesianTreeofA[0,i-1]ifi>0,otherwisethere'snochild.TherightchildisdefinedsimilaryforA[i+1,N-1].NotethattheCartesianTreeisnotnecessarilyuniqueifAcontainsequalelements.Inthistutorialthefirstappearanceoftheminimumvaluewillbeused,thustheCartesianTreewillbeunique.It'seasytoseenowthatRMQA(i,j)=LCAC(i,j).
Hereisanexample:
NowweonlyhavetocomputeC(A)inlineartime.Thiscanbedoneusingastack.Atthebeginningthestackisempty.WewilltheninserttheelementsofAinthestack.Atthei-thstepA[i]willbeaddednexttothelastelementinthestackthathasasmallerorequalvaluetoA[i],andallthegreaterelementswillberemoved.TheelementthatwasinthestackonthepositionofA[i]beforetheinsertionwasdonewillbecometheleftsonofi,andA[i]willbecometherightsonofthesmallerelementbehindhim.Ateverystepthefirstelementinthestackistherootofthecartesiantree.It'seasiertobuildthetreeifthestackwillholdtheindexesoftheelements,andnottheirvalue.
Hereishowthestackwilllookateachstepfortheexampleabove:
Step | Stack | Modificationsmadeinthetree |
0 | 0 | 0istheonlynodeinthetree. |
1 | 01 | 1isaddedattheendofthestack.Now,1istherightsonof0. |
2 | 02 | 2isaddednextto0,and1isremoved(A[2]<A[1]).Now,2istherightsonof0andtheleftsonof2is1. |
3 | 3 | A[3]isthesmallestelementinthevectorsofar,soallelementsinthestackwillberemovedand3willbecometherootofthetree.Theleftchildof3is0. |
4 | 34 | 4isaddednextto3,andtherightsonof3is4. |
5 | 345 | 5isaddednextto4,andtherightsonof4is5. |
6 | 3456 | 6isaddednextto5,andtherightsonof5is6. |
7 | 34567 | 7isaddednextto6,andtherightsonof6is7. |
8 | 38 | 8isaddednextto3,andallgreaterelementsareremoved.8isnowtherightchildof3andtheleftchildof8is4. |
9 | 389 | 9isaddednextto8,andtherightsonof8is9. |
voidcomputeTree(intA[MAXN],intN,intT[MAXN])
{
intst[MAXN],i,k,top=-1;
//westartwithanemptystack
//atstepiweinsertA[i]inthestack
for(i=0;i<N;i++)
{
//computethepositionofthefirstelementthatis
//equalorsmallerthanA[i]
k=top;
while(k>=0&&A[st[k]]>A[i])
k--;
//wemodifythetreeasexplainedabove
if(k!=-1)
T[i]=st[k];
if(k<top)
T[st[k+1]]=i;
//weinsertA[i]inthestackandremove
//anybiggerelements
st[++k]=i;
top=k;
}
//thefirstelementinthestackistherootof
//thetree,soithasnofather
T[st[0]]=-1;
}
An<O(N),O(1)>algorithmfortherestrictedRMQ
NowweknowthatthegeneralRMQproblemcanbereducedtotherestrictedversionusingLCA.Here,consecutiveelementsinthearraydifferbyexactly1.Wecanusethisandgiveafast<O(N),O(1)>algorithm.FromnowwewillsolvetheRMQproblemforanarrayA[0,N-1]where|A[i]-A[i+1]|=1,i=[1,N-1].WetransformAinabinaryarraywithN-1elements,whereA[i]=A[i]-A[i+1].It'sobviousthatelementsinAcanbejust+1or-1.NoticethattheoldvalueofA[i]isnowthesumofA[1],A[2]..A[i]plustheoldA[0].However,wewon'tneedtheoldvaluesfromnowon.
TosolvethisrestrictedversionoftheproblemweneedtopartitionAintoblocksofsizel=[(logN)/2].LetA'[i]betheminimumvalueforthei-thblockinAandB[i]bethepositionofthisminimumvalueinA.BothAandBareN/llong.Now,wepreprocessA'usingtheSTalgorithmdescribedinSection1.ThiswilltakeO(N/l*log(N/l))=O(N)timeandspace.AfterthispreprocessingwecanmakequeriesthatspanoverseveralblocksinO(1).Itremainsnowtoshowhowthein-blockqueriescanbemade.Notethatthelengthofablockisl=[(logN)/2],whichisquitesmall.Also,notethatAisabinaryarray.Thetotalnumberofbinaryarraysofsizelis2l=sqrt(N).So,foreachbinaryblockofsizelweneedtolockupinatablePthevalueforRMQbetweeneverypairofindices.ThiscanbetriviallycomputedinO(sqrt(N)*l2)=O(N)timeandspace.ToindextableP,preprocessthetypeofeachblockinAandstoreitinarrayT[1,N/l].Theblocktypeisabinarynumberobtainedbyreplacing-1with0and+1with1.
Now,toanswerRMQA(i,j)wehavetwocases:
·iandjareinthesameblock,soweusethevaluecomputedinPandT
·iandjareindifferentblocks,sowecomputethreevalues:theminimumfromitotheendofi'sblockusingPandT,theminimumofallblocksbetweeni'sandj'sblockusingprecomputedqueriesonA'andtheminimumfromthebeginingofj'sblocktoj,againusingTandP;finallyreturnthepositionwheretheoverallminimumisusingthethreevaluesyoujustcomputed.
Conclusion
RMQandLCAarestronglyrelatedproblemsthatcanbereducedonetoanother.Manyalgorithmscanbeusedtosolvethem,andtheycanbeadaptedtootherkindofproblemsaswell.
Herearesometrainingproblemsforsegmenttrees,LCAandRMQ:
SRM310->
References
-"
-"
-"
相关文章推荐
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query(RMQ) and Lowest Common Ancestor(LCA)
- Range Minimum Query and Lowest Common Ancestor(RMQ和LCA 找个时间翻译下)
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor 区间最值查询和最近公共祖先问题
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor[翻译]
- Range Minimum Query and Lowest Common Ancestor
- Range Minimum Query and Lowest Common Ancestor
- Binary Search Tree--find Lowest Common Ancestor--C++ and Java
- LeetCode 235. Lowest Common Ancestor of a Binary Search Tree
- 235. Lowest Common Ancestor of a Binary Search Tree
- 【LeetCode】235. Lowest Common Ancestor of a Binary Search Tree