Algorithms and Data Structures I
Algorithms and Data Structures I
AlgorithmsandDataStructuresI
Module9:Recursion,PartII
Supplementalmaterial
TheTowerofHanoi
IntheTowerofHanoipuzzle:
Therearethreestacks(towers),numbered0,1,2.
ThereareNdisksnumbered0,...,N1.
=>0isthesmallest.
Initially,thedisksareplacedintower0,intheorderlargesttosmallest.
=>Smallestontop.
Goal:tomovethediskstotower1usingonlylegalmoves.
What'salegalmove?
Youcanmoveonlyadiskatthetopofatower.
Youcanmoveonlyonediskatatime.
Youcannotplaceadiskontopofasmallerone.
Wewillsolvetheproblemusing(ofcourse)recursion:
Therearethreekeystepsintherecursion:
Step1:movedisks0,...,N2fromtower0totower2
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
1/20
1/15/2016
AlgorithmsandDataStructuresI
=>Subproblemofsmallersize.
Step2:movediskN1fromtower0totower1.
Step3:movedisks0,...,N2fromtower2totower1.
=>Subproblemofsmallersize.
Inpseudocode(withoutbasecase):
AlgorithmtowerOfHanoi(n,i,j)
Input:Disksnumbered0,...,naretobemovedfromtoweritotowerj
1.//...basecase...
//Firstfindthethirdtower,otherthaniandj:
2.k=otherTower(i,j)
//Step1:movedisks0,..,n1fromitok
3.towerOfHanoi(n1,i,k)
//Step2:movedisk#nfromitoj
4.move(n,i,j)
//Step3:movedisks0,...,n1fromktoj
5.towerOfHanoi(n1,k,j)
Thebasecase:moveasingledisk
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
2/20
1/15/2016
AlgorithmsandDataStructuresI
Here'stheprogram:(sourcefile)
publicclassTowerOfHanoi{
publicstaticvoidmain(String[]argv)
{
//A3diskpuzzle:
System.out.println("3Disksolution:");
solveHanoi(2,0,1);
//A4diskpuzzle:
System.out.println("4Disksolution:");
solveHanoi(3,0,1);
}
staticvoidsolveHanoi(intn,inti,intj)
{
//Bottomout.
if(n==0){
//Thesmallestdisk.
move(0,i,j);
return;
intk=other(i,j);
solveHanoi(n1,i,k);//Step1.
move(n,i,j);//Step2.
solveHanoi(n1,k,j);//Step3.
staticvoidmove(intn,inti,intj)
{
//Fornow,we'llmerelyprintoutthemove.
System.out.println("=>Movedisk#"+n+"fromtower"+i+"totower"+j);
}
staticintother(inti,intj)
{
//Giventwotowers,returnthethird.
if((i==0)&&(j==1)){
return2;
}
elseif((i==1)&&(j==0)){
return2;
}
elseif((i==1)&&(j==2)){
return0;
}
elseif((i==2)&&(j==1)){
return0;
}
elseif((i==0)&&(j==2)){
return1;
}
elseif((i==2)&&(j==0)){
return1;
}
//Weshouldn'treachhere.
return1;
}
}
Note:
Theaboveprogrammerelyprintsoutthemovesneeded
=>Wedon'tactuallymaintainthestateofeachtower.
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
3/20
1/15/2016
AlgorithmsandDataStructuresI
Next,supposewewanttomaintainthestateofeachtower:
Theidealdatastructureisastack.
We'lluseonestackforeachtower.
Here'stheprogram:(sourcefile)
importjava.util.*;
publicclassTowerOfHanoi2{
//towers[0],towers[1]andtowers[2]arethethreestacks.
staticStack<Integer>[]towers;
publicstaticvoidmain(String[]argv)
{
//A3diskpuzzle:
System.out.println("3Disksolution:");
solveHanoi(2,0,1);
//A4diskpuzzle:
System.out.println("4Disksolution:");
solveHanoi(3,0,1);
}
staticvoidsolveHanoi(intn,inti,intj)
{
//Createthethreestacks.
towers=newStack[3];
for(intk=0;k<3;k++){
towers[k]=newStack<Integer>();
}
//Putdisks0,...,nonstack0.
for(intk=n;k>=0;k){
towers[0].add(k);
}
//Printtheinitialstack.
printTowers();
//Nowsolverecursively.Note:thisisthemethodbelow.
solveHanoiRecursive(n,i,j);
}
staticvoidsolveHanoiRecursive(intn,inti,intj)
{
//Bottomout.
if(n==0){
move(0,i,j);
return;
intk=other(i,j);
solveHanoiRecursive(n1,i,k);//Step1.
move(n,i,j);//Step2.
solveHanoiRecursive(n1,k,j);//Step3.
}
staticvoidmove(intn,inti,intj)
{
//Pulloutthetopdiskonstacki.
inttopVal=towers[i].pop();
//Putitonstackj.
towers[j].push(topVal);
//Printstatus.
System.out.println("Towersaftermoving"+n+"fromtower"+i+"totower"+j);
printTowers();
}
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
4/20
1/15/2016
AlgorithmsandDataStructuresI
staticintother(inti,intj)
{
//...
}
staticvoidprintTowers()
{
for(inti=0;i<towers.length;i++){
System.out.print("Tower"+i+":");
if(!towers[i].isEmpty()){
for(IntegerI:towers[i]){
System.out.print(""+I);
System.out.println();
}
}
}
Note:
Sinceweneededtocreatethestacksaheadoftime,wemovedtherecursivepartofthecodeintoanewrecursive
methodsolveHanoiRecursive().
Toremoveadiskfromthetopofatower,wepop()fromthattower(stack):
//Pulloutthetopdiskonstacki.
inttopVal=towers[i].pop();
Toplaceadiskontopofatower,wepush()it:
//Putitonstackj.
towers[j].push(topVal);
NoticehowprintTowers()usesthe"collection"versionoftheforloop:
if(!towers[i].isEmpty()){
//Newtypeofforloop:
for(IntegerI:towers[i]){
System.out.print(""+I);
Astrangecompilationmessage:
Incompilingtheaboveprogram(withaJava1.5compiler),wegetawarning(notanerror):
Note:TowerOfHanoi2.javausesuncheckedorunsafeoperations.
Note:RecompilewithXlint:uncheckedfordetails.
Thisallowsyoutocontinuewithexecution.
Youcan,asitsuggests,recompilewiththeXlint:uncheckedoption:
javacXlint:uncheckedTowerOfHanoi2.java
Whatyouseeasaresult:
TowerOfHanoi2.java:25:warning:[unchecked]uncheckedconversion
found:java.util.Stack[]
required:java.util.Stack[]
towers=newStack[3];
^
Thecompilerdidnotlikethestatement:
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
5/20
1/15/2016
AlgorithmsandDataStructuresI
towers=newStack[3];
So,shouldthisinsteadhavebeen
towers=newStack<Integer>[3];
asonemightintuitivelyexpect?
Unfortunately,thatresultsinacompilationerror.
ThereasonisquiteesotericandhastodowiththegorydetailsofgenerictypesarehandledinJava.
Essentially,itboilsdowntothis:adeclarationlike
towers=newStack<Integer>[3];
isunsafebecauseonecanassignnonIntegerstackstotowers,whichiswhythecompilerdoesn'tallowit.
Thus,weareleftwithusingtheslightlylessunsafe
towers=newStack[3];
whichthecompilerallowsbutwarnsagainst.
GenerictypesinJavaconstitutealargeandsomewhatadvancedtopic,toocomplicatedforthiscourse.
Anapplication:diskbackupschedules
Supposewehavefourdisks(A,B,CandD)onwhichwewishtoperformbackupseachday.
=>Thisisadifferentmeaningofdisk(harddrive,removablemedia)
Eachdayweneedtochooseadiskonwhichtobackup
=>Thebackupschedule.
Oneoptionistogoroundrobin
Day0usediskA
Day1usediskB
Day2usediskC
Day3usediskD
Day4usediskA
Day5usediskB
...
(andsoon)
Withthissystem,wecanonlygobackatmostfourdays.
Wearenotabletoretrievethestateofthesystemfrom,say,6daysago.
Anotherapproach:staggerthedisks:
Day0usediskA
Day1usediskB
Day2usediskA
Day3usediskC
Day4usediskA
Day5usediskB
Day6usediskA
Day7usediskD
Day8usediskA
...
(andsoon)
Withthisapproach,we'dhaveadistributionlike
DiskA1or2daysbefore
DiskBatmost4daysbefore
DiskCatmost8daysbefore
DiskDatmost16daysbefore
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
6/20
1/15/2016
AlgorithmsandDataStructuresI
Thisdistributionallowsadeeperreachintothepast.
ThesequenceaboveisexactlytheTowerofHanoimovesequencefordisksnumberedA,B,CandD.
Recursion:areview
First,let'sreviewasimpleexamplefromModule4:
Recalltherecursivecomputationofpower:
//Computeab
staticintpower(inta,intb)
{
//Bottomout:
if(b==0){
return1;
}
//Recursion:
return(a*power(a,b1));
}
What'simportant:
Wemusttestforthebottomoutcasebeforerecursing.
Thebottomoutcaseteststhevalue(orvalues)oftheparameter(orparameters)thatchangesinthe
recursion.
=>Thesearetheparametersthatcontroltherecursion.
Therecursivecallsmustchange(usuallydecrease)theparametersthatcontroltherecursion.
=>Above,thereisonlyonerecursivecall,butTowerofHanoihastwo.
Howrecursionworks:
Eachmethodcallcreatesanactivationrecordonthesystemstack.
Theactivationrecordconsistsofspaceallocatedfortheparticularparametersandlocalvariablesforthat
methodcall.
Thus,arecursivecallresultsinanewactivationrecordforeachsuchrecursivecall.
=>Thisiswhyeachexecutionofthemethodretainsitsownparametersandlocalvariables.
Theactivationrecordalsoknowsthatwhenexecutionofamethodcallcompletes,executionreturnstothe
callingmethodjustafterthecalledmethodwascalled.
Forrecursionit'snodifferentexceptthatit'sthesamecodeinvolvedinallmethodcalls.
Thebestwaytounderstandrecursioninitiallyistodrawthestackpictureandworkthroughanexample.
Let'snowreviewanotherexamplefromModule4:
Thisisthepermutationseatingexamplewhereweprintthearrangement:
//Theparameterstothismethodare:
//numSpacestotalnumberofremainingseatsavailable
//numRemainingtotalnumberofpeoplelefttoseat
//seatsanarraywhereseats[i]==0ifseatisavailable
//personwhichpersonweneedtoseat
staticvoidprintPermutations(intnumSpaces,intnumRemaining,int[]seats,intperson)
{
//Bottomoutcase.
if(numRemaining==0){
//Print.
System.out.println(Arrays.toString(seats));
return;
}
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
7/20
1/15/2016
AlgorithmsandDataStructuresI
//Otherwise,nonbasecase:lookforanemptyspotfor"person"
for(inti=0;i<seats.length;i++){
if(seats[i]==0){
//Emptyspot.
seats[i]=person;
//Recursivelyassignremaining,startingwithperson+1
printPermutations(numSpaces1,numRemaining1,seats,person+1);
//Important:weneedtoundotheseatingforothertrials.
seats[i]=0;
}//endfor
}
What'ssimilaraboutthisrecursivemethod:
Theparameterthatcontrolsrecursionis:numSpaces
=>Thisiswhatwecheckinthebottomoutcase.
Otherparameterspassalonginformation:personandseats.
What'sdifferent(fromthepower()example):
Whenwefillintheseatsarray,weundothisassignmentaftertherecursivecall:
//Makesomechangetoaparameter:
seats[i]=person;
//Recurse:
printPermutations(numSpaces1,numRemaining1,seats,person+1);
//Undothechange(restoretheoriginal)forfuturerecursions:
seats[i]=0;
Twotypesofrecursion:
Recursionwhereyoudon'tundochanges.
=>Sometimesthisiscalledtailrecursion.
Recursionwhereyouneedtoundochangessothatyoucanproperlyexploreallpossibilities
=>Sometimesthisiscalledrecursionwithbacktracking.
Manyexamplesoftailrecursioncaneasilybewrittennonrecursively(usingiteration)
=>Formanyoftheseexamples(power,factorial,fibonacci),it'sbettertouseiteration.
However,whenthere'sbacktracking,itcanbeverydifficulttoavoidrecursion.
Mazeconstructionandtraversal:a(long)exampleofrecursionwithbacktracking
Wewillnowexamineamazeapplicationindetailtoillustraterecursionwithbacktracking:
First,wewilluserecursiontocreateamazelikethis:
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
8/20
1/15/2016
AlgorithmsandDataStructuresI
Then,wewilluserecursiontosolvesuchamaze(byfindingapathfromagivenstartcell(e.g.,thetopleft)toa
givenendcell(e.g.,thebottomright):
WewillputawholebunchofusefulcodeinaclasscalledMazeandinsteadfocusonusingthatclassthroughits
methods.
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
9/20
1/15/2016
AlgorithmsandDataStructuresI
We'llusethiscellnumberingconvention:
Rowsstartnumberingat0,andincreasedownwards.
Columnsstartat0andincreaserightwards.
Thus,thetopleftcellis(0,0).Theonetoitsrightis(0,1)andtheonedirectlybelowis(1,0).
Tostartwith,let'sexaminethemethodsintheclassMaze:
Theconstructor:wecreateaninstancebyspecifyingthenumberofrowsandcolumns:
Mazemaze=newMaze(5,5);
Ourexampleswillusesquaremazes.
display():callthismethodtodisplaythemaze:
Mazemaze=newMaze(5,5);
//BringuptheGUI:
maze.display();
breakWall():
Initially,themazeconsistsofcompletecells.Toactuallycreateawalkablemaze,wewill"breakwalls"
betweenneighboringcells.
Forexample,wecanbreakthewallbetweencells(3,4)and(2,4)asfollows:
Mazemaze=newMaze(5,5);
//NotetheuseoftheCoordclass:
Coordc1=newCoord(3,4);
Coordc2=newCoord(2,4);
maze.breakWall(c1,c2);
maze.display();
Or,alternatively,withshortercode:
Mazemaze=newMaze(5,5);
//Createaninstancedirectlyinthemethodargumentlist:
maze.breakWall(newCoord(3,4),newCoord(2,4));
maze.display();
TheCoordclasslookslikethis:
publicclassCoord{
publicintrow=1,col=1;
publicCoord(intr,intc)
{
row=r;
col=c;
}
publicStringtoString()
{
return"["+row+","+col+"]";
}
Thus,toaccessthestoredcoordinates:
Coordc=newCoord(3,4);
System.out.println("Row="+c.row+"Col="+c.col);
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
10/20
1/15/2016
AlgorithmsandDataStructuresI
//Or,toprintbothusingtoString():
System.out.println(c);
Anumberofmethods(intheclassMaze)allowustomarkandunmarkcellsasvisited:
markVisited(Coordc):markcellcasvisited.
markUnVisited(Coordc):markcellcasnotvisited.
markAllUnvisited():markallcellsasunvisited.
isVisited(Coordc):seewhethercellchasbeenvisited.
Anumberofmethodsrelatedtofetchingalist(array)ofacell'sneighbors:
Coord[]getUnvisitedClosedNeighbors(Coordc):getcellc'sneighborsthatareclosedofffromc(there's
awallbetween)andhaven'tbeenvisitedyet.
Coord[]getClosedNeighbors(Coordc):getneighborsofcthatshareawall(unbroken)withcwhether
visitedornot.
Coord[]getUnvisitedOpenNeighbors(Coordc):getthoseneighborsofcforwhichwecanwalkthrough
totheneighbor(nowall)andwhichhaven'tbeenvisited.
EachofthesemethodsreturnanarrayofCoordinstances.
Afewadditionalusefulmethods:
copy():makeafullcopyofthemaze,asin:
Mazem=newMaze(5,5);
//...dostuff:breakwallsetc...
Mazem2=m.copy();
//Nowm2isacopyofm.Makingchangestomwon'taffectm2.
setSolutionPath(LinkedList<Coord>solutionPath):wewillcallthismethodoncewehaveconstructed
asolution.
We'llnowwriteourfirstattempt:
Wewilltrytogenerateamazepathofagivenpathlength.
Thekeyidea:
Algorithm:recursiveGenerate(Coordc)
1.ifpathlengthhasbeenreached
2.return
//Otherwise...
3.c'=Pickarandomneighborofcellc
4.recursiveGenerate(c')
Here'stheprogram:(sourcefile)
publicclassMazeMaker{
//Thesevariableswillbeusedacrossmethods:
staticintdesiredPathLength;
staticMazemaze;
publicstaticvoidmain(String[]argv)
{
generateMaze(5,5);
}
publicstaticvoidgenerateMaze(intnumRows,intnumCols)
{
maze=newMaze(numRows,numCols);
//Wewanttogenerateapathofthislength:
desiredPathLength=numRows*numCols;
//Initially,we'llstartwiththetopleftcellandmarkthatasvisited.
Coordstart=newCoord(0,0);
maze.markVisited(start);
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
11/20
1/15/2016
AlgorithmsandDataStructuresI
//Generatethemazepathrecursively.
booleanfound=recursiveGenerate(start,1);
if(!found){
System.out.println("Couldnotcreatethewholemaze");
}
maze.display();
}
staticbooleanrecursiveGenerate(Coordc,intpathLength)
{
//Bottomoutcondition1:
if(pathLength==desiredPathLength){
//Done.We'vecreateamazepathofthedesiredlength.
returntrue;
}
//Bottomoutcondition2:seeifthere'saneighbortovisit.
Coord[]validNeighbors=maze.getUnvisitedClosedNeighbors(c);
if((validNeighbors==null)||(validNeighbors.length==0)){
//There'snoneighborwhosewallwecanbreak.Soquittrying.
returnfalse;
}
//Otherwise,pickarandomneighborandproceed.
inti=UniformRandom.uniform(0,validNeighbors.length1);
//Breakthewallandmarktheneighborasvisited.
maze.breakWall(c,validNeighbors[i]);
maze.markVisited(validNeighbors[i]);
//Recurse.
returnrecursiveGenerate(validNeighbors[i],pathLength+1);
}
}
Nextattempt:
Simpletailrecursioncangetstuck.
=>Weneedawayto"undo"pathsandtrydifferentneighbors.
Wewillpickaneighbortoexplorerecursively
=>Ifthatdoesn'tworkout,we'lltryanother.
Here'stheprogram:(sourcefile)
publicclassMazeMaker2{
staticintdesiredPathLength;
staticMazemaze;
publicstaticvoidmain(String[]argv)
{
generateMaze(5,5);
}
publicstaticvoidgenerateMaze(intnumRows,intnumCols)
{
maze=newMaze(numRows,numCols);
desiredPathLength=numRows*numCols;
//Initially,we'llstartwiththetopleftcell.
Coordstart=newCoord(0,0);
maze.markVisited(start);
//Generatethemazepathrecursively.
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
12/20
1/15/2016
AlgorithmsandDataStructuresI
booleanfound=recursiveGenerate(start,1);
if(!found){
System.out.println("Couldnotcreatethewholemaze");
}
maze.display();
}
staticbooleanrecursiveGenerate(Coordc,intpathLength)
{
//Bottomoutcondition1:
if(pathLength==desiredPathLength){
returntrue;
}
//Bottomoutcondition1:seeifwe'restuck.
Coord[]validNeighbors=maze.getUnvisitedClosedNeighbors(c);
if((validNeighbors==null)||(validNeighbors.length==0)){
returnfalse;
//Otherwise,wehavesomeneighborstoexplore.
//Permutethedirectionsrandomly.
permute(validNeighbors);
for(inti=0;i<validNeighbors.length;i++){
//Tryneighbori.
maze.breakWall(c,validNeighbors[i]);
maze.markVisited(validNeighbors[i]);
booleanok=recursiveGenerate(validNeighbors[i],pathLength+1);
if(ok){
//Ifneighboriworkedout,great.
returntrue;
//Otherwise,undoassignmenttoi.
maze.fixWall(c,validNeighbors[i]);
maze.markUnvisited(validNeighbors[i]);
}//endfor
//Couldn'tmakeitwork.
returnfalse;
staticvoidpermute(Coord[]coords)
{
for(inti=0;i<coords.length;i++){
//Findarandomelementtoplaceintoithplace.
intk=(int)UniformRandom.uniform(i,coords.length1);
Coordtemp=coords[i];
coords[i]=coords[k];
coords[k]=temp;
}
}
}
Note:
Thekeyideaistotryasmanyneighborsasneeded:
Algorithm:recursiveGenerate(Coordc)
1.ifpathlengthhasbeenreached
2.returntrue
3.endif
//Otherwise...
4.foreachneighborc'ofc
5.found=recursiveGenerate(c')
6.iffound
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
13/20
1/15/2016
AlgorithmsandDataStructuresI
7.returntrue
8.endif
9.endfor
//Wereachhereonlyifeveryneighbordidnotworkout.
10.returnfalse
Notehowweundoabrokenwall:
for(inti=0;i<validNeighbors.length;i++){
//Tryneighbori.
maze.breakWall(c,validNeighbors[i]);
maze.markVisited(validNeighbors[i]);
//...recursionhere...
//Toundo:repairthewallandmarkasUNvisited.
maze.fixWall(c,validNeighbors[i]);
maze.markUnvisited(validNeighbors[i]);
}//endfor
Amazewithchoices:
Theabovemazepathhasnochoices
=>There'sonlyonewayfromthetoplefttotheendofthepath.
Wewillbuilduponthistocreateamazewithchoices
=>Aftergeneratingthelongpath,we'llbreaksomerandomwalls.
Here'stheprogram:(sourcefile)
publicclassMazeMaker3{
//...variables...
publicstaticvoidmain(String[]argv)
{
generateMaze(5,5);
}
publicstaticvoidgenerateMaze(intnumRows,intnumCols)
{
//...createandgeneratesinglepathmaze...
//Breakafewmorewalls,randomly.
breakRandomWalls(maze,10);
maze.display();
}
staticbooleanrecursiveGenerate(Coordc,intpathLength)
{
//...sameasbefore...
}
staticvoidbreakRandomWalls(Mazemaze,intnumWalls)
{
for(intk=0;k<numWalls;k++){
//Createrandomcoordinates,i.e.,identifyarandomcell.
intx=UniformRandom.uniform(0,maze.numRows1);
inty=UniformRandom.uniform(0,maze.numCols1);
Coordc=newCoord(x,y);
//Getitsneighborsthatareseparatedbyawall.
Coord[]validNeighbors=maze.getClosedNeighbors(c);
if(validNeighbors!=null){
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
14/20
1/15/2016
AlgorithmsandDataStructuresI
//Pickoneandbreakthewall.
intm=UniformRandom.uniform(0,validNeighbors.length1);
maze.breakWall(c,validNeighbors[m]);
}
}
}
Let'sturnourattentiontosolvingthemaze:
We'llfirsttryasimpleidea:
Algorithm:recursivelyFindPath(Coordc)
1.ifcistheend
2.returntrue
3.endif
4.//Otherwisesearchfurther...
5.c'=pickarandom"open"neighbor
6.ifc'isnull
//Stuck:couldn'tfindapath.
7.returnfalse
8.endif
9.returnrecursivelyFindPath(c')
Here'stheprogram:(sourcefile)
importjava.util.*;
publicclassMazeMaker4{
staticintdesiredPathLength;
staticMazemaze;
publicstaticvoidmain(String[]argv)
{
Mazemaze=generateMaze(5,5);
if(maze!=null){
//Wewillseekapathfromthetoplefttothebottomrightcorner.
Coordstart=newCoord(0,0);
Coordend=newCoord(4,4);
solveMaze(maze,start,end);
maze.display();
else{
System.out.println("Mazecreationdidnotwork");
}
}
//Apathisalistofcells,i.e.,alistofCoordinstances.
staticLinkedList<Coord>solutionPath;
staticvoidsolveMaze(Mazemaze,Coordstart,Coordend)
{
//We'llmarkvisitedcellsaswegoalongourpath.Initially:
maze.markAllUnvisited();
//Markthestartcellasvisited.
maze.markVisited(start);
//Createthelist.
solutionPath=newLinkedList<Coord>();
//Recursivelyfindthepathandfillincoord'sintothelist.
recursivelyFindPath(maze,start,end);
//PassthepathintotheGUI.
maze.setSolutionPath(solutionPath);
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
15/20
1/15/2016
AlgorithmsandDataStructuresI
}
staticbooleanrecursivelyFindPath(Mazemaze,Coordc,Coordend)
{
//Firstaddthecurrentcellintothepath.
solutionPath.add(c);
//Ifwe'vereachedtheend,we'redone.
if((c.row==end.row)&&(c.col==end.col)){
returntrue;
}
//Otherwise,let'sfindaneighbortoexplore.
Coord[]validNeighbors=maze.getUnvisitedOpenNeighbors(c);
if(validNeighbors==null){
//Iftherearen'tany,we'redone,butcouldn'tfindapath.
returnfalse;
//Takethefirstoneandexplorefromthere.
maze.markVisited(validNeighbors[0]);
returnrecursivelyFindPath(maze,validNeighbors[0],end);
//...Mazegenerationcode...sameasbefore...
}
Asolutionthatworks:
Whenexploringaneighbor,weneedawaytoundo(backtrack)ifitdoesn'tleadtoasolution.
Theideaistotryallpossibleneighbors,asmanyasneeded:
Algorithm:recursivelyFindPath(Coordc)
1.ifcistheend
2.returntrue
3.endif
4.//Otherwisesearchfurther...
5.foreach"open"neighborc'
6.found=recursivelyFindPath(c')
7.iffound
8.addc'topath
9.returntrue
10.endif
10.endfor
11.returnfalse
Here'stheprogram:(sourcefile)
importjava.util.*;
publicclassMazeMaker5{
//...variables...sameasbefore(forgeneration)...
publicstaticvoidmain(String[]argv)
{
//...same...
}
//Apathisalistofcells,i.e.,alistofCoordinstances.
staticLinkedList<Coord>solutionPath;
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
16/20
1/15/2016
AlgorithmsandDataStructuresI
staticvoidsolveMaze(Mazemaze,Coordstart,Coordend)
{
//We'llmarkvisitedcellsaswegoalongourpath.Initially:
maze.markAllUnvisited();
//Markthestartcellasvisited.
maze.markVisited(start);
//Createthelist.
solutionPath=newLinkedList<Coord>();
//Recursivelyfindthepathandfillincoord'sintothelist.
recursivelyFindPath(maze,start,end);
//Thestartnodegetsaddedlast.Why?
solutionPath.addFirst(start);
//PassthepathintotheGUI.
maze.setSolutionPath(solutionPath);
}
staticbooleanrecursivelyFindPath(Mazemaze,Coordc,Coordend)
{
//Ifwe'vereachedtheend,we'redone.
if((c.row==end.row)&&(c.col==end.col)){
returntrue;
}
//Otherwise,let'sfindaneighbortoexplore.
Coord[]validNeighbors=maze.getUnvisitedOpenNeighbors(c);
if(validNeighbors==null){
//Ifwecouldn'tfindanyneighborstoexplore,we'restuck.
returnfalse;
}
//Tryeachneighbor,asmanyasneeded.
for(inti=0;i<validNeighbors.length;i++){
//Tryneighbori.
maze.markVisited(validNeighbors[i]);
booleanfound=recursivelyFindPath(maze,validNeighbors[i],end);
if(found){
//Noticethatweaddthistothefrontofthelist,andonly
//afterthesolutionhasbeenfounduptohere.
solutionPath.addFirst(validNeighbors[i]);
returntrue;
//Ifneighborididn'tworkout,undothevisitandcontinue.
maze.markUnvisited(validNeighbors[i]);
}
//Ifwereachedhere,thennoneoftheneighborsworkedout.
returnfalse;
}
}
TheNQueensproblem
Inthisproblem:
WearegivenanNxNchessboardandMqueens(whereM<=N).
Thegoalistoplacethequeensontheboardsothatnoqueen"attacks"another.
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
17/20
1/15/2016
AlgorithmsandDataStructuresI
Forexample(N=8):
Recallthataqueencanattackanysquareinitsrow,columnordiagonal,e.g.
Wewillofcoursesolvethisrecursively:
Thisisanotherexampleofrecursionwithbacktracking.
Keyideas:
We'llproceedcolummbycolumn(lefttoright).
Whensolvingforthecurrentcolumn,trytoplaceaqueenoneachpossiblerow,andsolvethesub
problem(startingwiththenextcolumn)recursively.
Pseudocode:
Algorithm:solve(n,c)
Input:n=thenumberofqueenstoassign,c=thecolumntostartwith
//...Bottomoutconditions...(we'lldothislater)
1.foreachrowr
2.if[r,c]isanonattackedsquare
3.placequeennoncell[r,c]
4.found=solve(n1,c+1)//Assignremainingn1recursively
5.if(found)
6.returntrue
7.endif
8.endif
9.endfor
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
18/20
1/15/2016
AlgorithmsandDataStructuresI
//Wereachhereifnoneoftherowsincolumncworkedout.
10.returnfalse
Thus,wetryeveryrowforaqueenandtrytosolvethesubproblemwithfewerqueens.
=>Ififthatgetssolved,wecanplacethequeenthereandrecursebackup
Thebottomoutconditions:
Checkiftherearenomorequeenstoassign
=>Problemgetssolved.
Checkiftherearenomorecolumnstotry
=>Nosolutionexists.
WewillbuildaclasscalledChessBoardwithmethodsthathidemostofthemessyboardmanipulationdetails:
addQueen(row,col):addaqueeninsquare[row,col].
removeQueen(row,col):removethequeeninsquare[row,col].
booleanisForbidden(row,col):seeif[row,col]isanattackedsquare.
display():tobringupaGUIwiththeboarddisplayed.
Here'stheprogram:(sourcefile)
importjava.awt.*;
importjava.util.*;
publicclassNQueens{
staticChessBoardboard;
publicstaticvoidmain(String[]argv)
{
solveQueens(8,8);
}
staticvoidsolveQueens(intnumQueens,intsize)
{
board=newChessBoard(size);
booleansolutionExists=solve(numQueens,0);
if(!solutionExists){
System.out.println("Nosolutionfor"+numQueens+"ona"+size+"x"+size+"board");
return;
}
System.out.println("Solutionfoundfor"+numQueens+"ona"+size+"x"+size+"board");
System.out.println(board);
board.display();
}
staticbooleansolve(intnumQueens,intwhichCol)
{
//Bottomoutcondition1:
if(numQueens==0){
//Nonetoassigndone.
returntrue;
}
//Bottomoutcondition2:
if(whichCol>=board.size()){
//Nocolumnslefttotrydone.
returnfalse;
}
//Tryeveryunforbiddenspotineachrow.
for(introw=0;row<board.size();row++){
if(!board.isForbidden(row,whichCol)){
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
19/20
1/15/2016
AlgorithmsandDataStructuresI
//Trythislocation.
board.addQueen(row,whichCol);
booleansolutionExists=solve(numQueens1,whichCol+1);
if(solutionExists){
returntrue;
}
//Else,undo
board.removeQueen(row,whichCol);
}
}
//Couldn'tfindasolution.
returnfalse;
}
}
http://www.seas.gwu.edu/~drum/cs1112/lectures/module9/module9.html
20/20