Difference Between Two Recursive Methods - recursion

I wanted to reflect a binary tree, such that all nodes on the left ended up on the right, and vice versa.
Something like :
A
/ \
B C
/ / \
D E F
would become
A
/ \
C B
/ \ \
F E D
I noticed that, while writing my solution, this code worked:
static Tree getReflection(Tree root) {
if(root == null) {
return null;
}
Tree reflect = root;
Tree subRight = getReflection(root.right);
Tree subLeft = getReflection(root.left);
reflect.left = subRight;
reflect.right = subLeft;
return reflect;
}
And yet, this one doesn't:
static Tree getReflection(Tree root) {
if(root == null) {
return null;
}
Tree reflect = root;
reflect.left = getReflection(root.right);
reflect.right = getReflection(root.left);
return reflect;
}
Can someone explain to me why? To me, they seem like identical methods, except one uses temporary tree variables.

Look at your first statement in each: when you assign
reflect = root
, the two variables now point to the same memory location. Now, let's look at the operation of the second routine:
Tree reflect = root;
// reflect and root now refer to exactly the same tree.
reflect.left = getReflection(root.right);
// reflect the right subtree; make that the new left subtree.
reflect.right = getReflection(root.left);
// Grab that new subtree, re-reflect it, and put it back on the right.
The original left subtree is lost, replaced by a reflection of the right.
In the first routine, you saved them in local variables until you'd done both reflections.

It is because in the second function (the one that doesn't work), you are assigning the reflected result to your left node and then using that as input to the reflection that you assign to your right node.
Tree reflect = root;
reflect.left = getReflection(root.right);
reflect.right = getReflection(root.left);

Related

Modified function not working as intended without recursion

I have a recursive function which iterates though directory trees listing the file names located in them.
Here is the function:
void WINAPI SearchFile(PSTR Directory)
{
HANDLE hFind;
WIN32_FIND_DATA FindData;
char SearchName[1024],FullPath[1024];
memset(SearchName,0,sizeof(SearchName));
memset(&FindData,0,sizeof(WIN32_FIND_DATA));
sprintf(SearchName,"%s\\*",Directory);
hFind=FindFirstFile(SearchName,&FindData);
if(hFind!=INVALID_HANDLE_VALUE)
{
while(FindNextFile(hFind,&FindData))
{
if(FindData.cFileName[0]=='.')
{
continue;
}
memset(FullPath,0,sizeof(FullPath));
sprintf(FullPath,"%s\\%s",Directory,FindData.cFileName);
if(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
MessageBoxA(NULL, FullPath, "Directory", MB_OK);
SearchFile(FullPath);
}
else
{
MessageBoxA(NULL, FullPath, "File", MB_OK);
}
}
FindClose(hFind);
}
}
There are obviously differences between both functions but I don't understand what's making them act differently. Does anyone know why I am having this problem?
for fast understand error need look for line
goto label;
//SearchFile(FullPath);
at this point hFind containing valid data and FindClose(hFind); need be called for it. but after goto label; executed - your overwrite hFind with hFind = FindFirstFile(SearchName, &FindData); - so you already never close original hFind, never can return to iterate folder after such go to sub-folder. this is key point - need save original hFind before go to sub directory and restore it after. when you do recursive function call - this is done auto - because every sub directory in this case enumerated in self stack frame, which have separate hFind. this is native solution use recursion here.
but possible convert recursion to loop here because we call self always from the single place and as result to this single place. so we can not save return address in stack but do unconditional jump (goto) to known place.
then code have some extra errors, you never check for string buffers overflow, why 1024 as max length is hard-coded when file path can be up to 32768 chars, you not check for reparse point as result can enter to infinite loop, use FindFirstFile instead FindFirstFileEx, etc.
correct code for enumerate sub-folder in loop can be next
void DoEnum(PCWSTR pcszRoot)
{
SIZE_T FileNameLength = wcslen(pcszRoot);
// initial check for . and ..
switch (FileNameLength)
{
case 2:
if (pcszRoot[1] != '.') break;
case 1:
if (pcszRoot[0] == '.') return;
}
static const WCHAR mask[] = L"\\*";
WCHAR FileName[MAXSHORT + 1];
if (_countof(FileName) < FileNameLength + _countof(mask))
{
return;
}
ULONG dwError;
HANDLE hFindFile = 0;
WIN32_FIND_DATA FindData{};
enum { MaxDeep = 0x200 };
//++ stack
HANDLE hFindFileV[MaxDeep];
PWSTR pszV[MaxDeep];
char prefix[MaxDeep+1];
//--stack
ULONG Level = MaxDeep;
memset(prefix, '\t', MaxDeep);
prefix[MaxDeep] = 0;
PWSTR psz = FileName;
goto __enter;
__loop:
hFindFile = FindFirstFileEx(FileName, FindExInfoBasic, &FindData, FindExSearchNameMatch, 0, FIND_FIRST_EX_LARGE_FETCH);
if (hFindFile != INVALID_HANDLE_VALUE)
{
do
{
pcszRoot = FindData.cFileName;
// skip . and ..
switch (FileNameLength = wcslen(pcszRoot))
{
case 2:
if (pcszRoot[1] != '.') break;
case 1:
if (pcszRoot[0] == '.') continue;
}
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
if ((SIZE_T)(FileName + _countof(FileName) - psz) < FileNameLength + _countof(mask))
{
continue;
}
__enter:
memcpy(psz, pcszRoot, (1 + FileNameLength) * sizeof(WCHAR));
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
{
DbgPrint("%sreparse point: <%S>\n", prefix + Level, pcszRoot);
}
else
{
if (Level)
{
DbgPrint("%s<%S>\n", prefix + Level, psz);
hFindFileV[--Level] = hFindFile;
pszV[Level] = psz;
memcpy(psz += FileNameLength, mask, sizeof(mask));
psz++;
goto __loop;
__return:
*--psz = 0;
psz = pszV[Level];
hFindFile = hFindFileV[Level++];
DbgPrint("%s</%S>\n", prefix + Level, psz);
}
}
}
else
{
DbgPrint("%s[%u%u] %S\n", prefix + Level, FindData.nFileSizeLow, FindData.nFileSizeHigh, pcszRoot);
}
if (!hFindFile)
{
// top level exit
return ;
}
} while (FindNextFile(hFindFile, &FindData));
if ((dwError = GetLastError()) == ERROR_NO_MORE_FILES)
{
dwError = NOERROR;
}
FindClose(hFindFile);
}
else
{
dwError = GetLastError();
}
if (dwError)
{
DbgPrint("<%S> err = %u\n", FileName, dwError);
}
goto __return;
}
The reason for the difference is actually the confusion brought to you by goto label.If you are using the recursive version, after the recursive execution is completed, it will return to the recursive place to continue execution.
In your code, you continue to execute while (FindNextFile(hFind, &FindData)), but when you use goto label, it will jump out of the original loop and restart the program from the label, which leads to what you said list a single directory tree before ending.
If you modify the modified code to the following iterative version, you can understand why there is such a problem.
void fun()
{
char* Directory = "D:\\test";
HANDLE hFind;
WIN32_FIND_DATA FindData;
char SearchName[1024], FullPath[1024];
char LastName[1024] = "";
while (1)
{
memset(SearchName, 0, sizeof(SearchName));
memset(&FindData, 0, sizeof(WIN32_FIND_DATA));
sprintf(SearchName, "%s\\*", Directory);
if (strcmp(SearchName, LastName) == 0)
{
return;
}
strcpy(LastName, SearchName);
hFind = FindFirstFile(SearchName, &FindData);
if (hFind != INVALID_HANDLE_VALUE)
{
while (FindNextFile(hFind, &FindData))
{
if (FindData.cFileName[0] == '.')
{
continue;
}
memset(FullPath, 0, sizeof(FullPath));
sprintf(FullPath, "%s\\%s", Directory, FindData.cFileName);
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
MessageBoxA(NULL, Directory, "Directory", MB_OK);
char cArray[1024];
memset(cArray, 0, sizeof(cArray));
sprintf(cArray, "%s", FullPath);
Directory = cArray;
break;
}
else
{
MessageBoxA(NULL, FullPath, "File", MB_OK);
}
}
FindClose(hFind);
}
}
}
So you cannot achieve the same purpose as recursion by using goto, here you can only use recursion. Of course, I have provided a way to traverse directories non-recursively using queues, which is a more scientific way.
One of the key things that you obtain from recursion is a separate set of local variables for each call to the recursive function. When a function calls itself, and in the recursive call modifies local variables, those local-variable changes do not (directly) affect the local variables of the caller. In your original program, this applies to variables hFind, FindData, SearchName, and FullPath.
If you want similar behavior in a non-recursive version of the function then you need to manually preserve the state of your traversal of one level of the tree when you descend to another level. The goto statement doesn't do any such thing -- it just redirects the control flow of your program. Although there are a few good use cases for goto in C, they are uncommon, and yours is not one of them.
There are several ways to implement manually preserving state, but I would suggest
creating a structure type in which to store those data that characterize the state of your traversal of a particular level. Those appear to be only hFind and FindData -- it looks like the other locals don't need to be preserved. Maybe something like this, then:
struct dir_state {
HANDLE hFind;
WIN32_FIND_DATA FindData;
};
Dynamically allocating an array of structures of that type.
unsigned depth_limit = DEFAULT_DEPTH_LIMIT;
struct dir_state *traversal_states
= malloc(depth_limit * sizeof(*traversal_states));
if (traversal_states == NULL) // ... handle allocation error ...
Tracking the depth of your tree traversal, and for each directory you process, using the array element whose index is the relative depth of that directory.
// For example:
traversal_states[depth].hFind
= FindFirstFile(SearchName, &traversal_states[depth].FindData);
// etc.
Remembering the size of the array, so as to be able to reallocate it larger if the traversal descends too deep for its current size.
// For example:
if (depth >= depth_limit) {
depth_limit = depth_limit * 3 / 2;
struct dir_state *temp
= realloc(traversal_states, depth_limit * sizeof(*traversal_states));
if (temp == NULL) {
// handle error, discontinuing traversal
}
traversal_states = temp;
}
Also, use an ordinary for, while, or do loop instead of a backward-jumping goto. There will be a few details to work out to track when to use FindFirstFile and when FindNextFile (which you would still have with goto), but I'm sure you can sort it out.
Details are left as an exercise.
Unless necessary due to memory or processing constraints or infinite recursion tail conditions that would be complication to introduce there really isn't much need to not use recursion here, since it leads to a readable and elegant solution.
I also want to point out that in "modern" C, any solution using a GOTO is likely not a solution you want since they are so often confusing to use and leads to memory issues (we have loops now to make all of that so much simpler).
Instead of the GOTOs I would suggest implementing a stack of the directories. Wrap the printing logic a while or do-while, and as you are iterating over the files add any directories to the stack. At every new iteration pop and walk the directory at the head of the stack. The loop condition just needs to check if the directory stack is empty, before continuing its block.

Traversing based on multiple vertexes

I've a graph in OrientDB with vertexes Area & Place with edges visited. Your average path goes Area > visited > Place > visited > Place > visited > Place > visited > Place and so on. It tracks which places user visited after the previous one. visited contains YYYYmmDD datestamp.
I'm trying to find out all Area vertexes based on arbitrary Place vertexes for certain day - i.e. I want to know from which areas users came to a certain place after visiting certain place first.
Traversing from any single Place in the path would be easy but I need to to follow the path for only for a specific datestamp. What I did was that I created index for datestamp to get day's visited edges quickly and then finds the one that has in to the first Place. However now I can't figure out how to create a fast query that finds all Area vertexes based on the first Place while also making sure that the path contains second Place as well. I can get path between first and second Place via shortestPath() but I still have the same problem with extending the path to include Area vertexes.
I found some theory on the subject but if somebody could point me to the right direction how to use OrientDB to do this instead of pure graph theory I would really appreciate it - I've been working on this for the past week now. Originally this was done via bruteforce by traversing everything and then selecting but as the database grows it's not obviously sustainable.
I created the three Vertices 'Area', 'Place' and 'User' and the two Edges 'visited' and 'placed' where datestamp is a property on the edge 'visited.
In this way you don't have to insert everytime the User as a property on the edge.
Edit
Try this JavaScript function that has three parameters(places,date,propertyPlace)
var g=orient.getGraph();
var myPlaces=places.substring(1,places.length-1).split(",");
var b=g.command("sql","select from Area");
var result=[];
if(checkPlaces){
for(i=0;i<b.length;i++){
var listPlaces=[];
for(ind=0;ind<myPlaces.length;ind++){
listPlaces.push(myPlaces[ind]);
}
if(search(b[i],listPlaces)){
result.push(b[i]);
}
}
}
return result;
function checkPlaces() {
for(index=0;index<myPlaces.length;index++){
var place=g.command("sql","select from Place where "+ propertyPlace + "='"+myPlaces[index]+"'");
if(place.length==0){
return false;
}
}
return true;
}
function checkDate(edge){
var datestamp=edge.getRecord().field("datestamp");
var year=datestamp.getYear()+1900;
var month=datestamp.getMonth()+1;
var day=datestamp.getDate();
var app=date.split("-");
var myYear=parseInt(app[0]);
var myMonth=parseInt(app[1]);
var myDay=parseInt(app[2]);
if(year==myYear && month==myMonth && day==myDay){
return true;
}
return false;
}
function search(v,places){
var edge=v.getRecord().field("out_visited");
if(edge!=null){
var edgeIterator=edge.iterator();
while(edgeIterator.hasNext()){
var edge = edgeIterator.next();
if (checkDate(edge)) {
var v1 = edge.field("in");
if(v1!=null){
var name = v1.field(propertyPlace);
for(j=0;j<places.length;j++){
if(name==(places[j])) {
places.splice(j, 1);
break;
}
}
if(places.length==0){
return true;
}
else if(search(v1,places)){
return true;
}
}
}
}
}
return false;
}
Using the following command
select expand(result) from (select myFunction("[place1,place2]","2015-12-03","name") as result)
Let me know if it works
This is not a solution to this exact problem but instead a workaround I came up with. Inspired by Orientdb get last vertex from each path when Traversing by edge property
I changed to structure so that Area is now created per visitation instead of being static and it includes yyyymmdd timestamp also. Now I can use Area to start the query and use visited edges to get Place vertices only for a certain date.
Here's the query:
SELECT $path, $depth FROM (
TRAVERSE * FROM (
SELECT outE('visited') FROM (
SELECT EXPAND(rid) FROM INDEX:area.dt WHERE key = 20151205
)
) WHILE (#class = 'visited' AND dt = 20151205) OR #class = 'place')
WHERE #class = 'place' AND NOT (outE() contains (dt=20151205))
This returns correct paths with vertices and edges so you can verify it's only for a certain day. However note that Area is not contained in the path and I still need to figure out how to do that but if you want, you can just traverse the first visited edge backwards in the path and get it that way.

How do I translate LR(1) Parse into a Abstract syntax tree?

I have coded a table driven LR(1) parser and it is working very well however I am having a bit of a disconnect on the stage of turing a parse into a syntax tree/abstract syntax tree. This is a project that I m very passionate about but I have really just hit a dead end here. Thank you for your help in advance.
Edit: Also my parser just uses a 2d array and an action object that tells it where to go next or if its a reduction where to go and how many items to pop. I noticed that many people use the visitor pattern. Im not sure how they know what type of node to make.
Here is the pushdown automata for context
while (lexer.hasNext() || parseStack.size() > 0) {
Action topOfStack = parseStack.peek();
token = parseStack.size() > 0 ? lexer.nextToken() : new Token(TokenType.EOF, "EOF");
topOfStack.setToken(token);
int row = topOfStack.getTransitionIndex();
int column = getTerminalIndex(token.getLexeme());
column = token.getType() == TokenType.IDENTIFIER
&& !terminalsContain(token.getLexeme()) ? 0 : column;
Action action = actionTable[row][column];
if (action instanceof Accept) {
System.out.println("valid parse!!!!!!");
} else if (action instanceof Reduction) {
Reduction reduction = (Reduction) action;
popStack(parseStack, reduction.getNumberOfItemsToPop());
column = reduction.getTransitionIndex();
row = parseStack.peek().getTransitionIndex();
parseStack.push(new Action(gotoTable[row][column]));
lexer.backupTokenStream();
} else if (action != null) {
parseStack.push(actionTable[row][column]);
} else {
System.out.println("Parse error");
System.out.println("On token: " + token.getLexeme());
break;
}
Each reduction in the LR parsing process corresponds to an internal node in the parse tree. The rule being reduced is the internal AST node, and the items popped off the stack correspond to the children of that internal node. The item pushed for the goto corresponds to the internal node, while those pushed by shift actions correspond to leaves (tokens) of the AST.
Putting all that together, you can easily build an AST by createing a new internal node every time you do a reduction and wiring everything together appropriately.

Converting an iterative function into recursive

One of the hardest concepts I've found in programming has been recursion. I have been trying to convert this function into a recursive call for 2 days now, but I just can't get it to work. Its a delete function for a BST
public void deleteByMerging(T el) {
BSTNode<T> tmp, node, p = root, prev = null;
while (p != null && !p.el.equals(el)) { // find the node p
prev = p; // with element el;
if (el.compareTo(p.el) < 0)
p = p.right;
else p = p.left;
}
node = p;
if (p != null && p.el.equals(el)) {
if (node.right == null) // node has no right child: its left
node = node.left; // child (if any) is attached to its parent;
else if (node.left == null) // node has no left child: its right
node = node.right; // child is attached to its parent;
else { // be ready for merging subtrees;
tmp = node.left; // 1. move left
while (tmp.right != null) // 2. and then right as far as
tmp = tmp.right; // possible;
tmp.right = // 3. establish the link between
node.right; // the rightmost node of the left
// subtree and the right subtree;
node = node.left; // 4.
}
if (p == root)
root = node;
else if (prev.left == p)
prev.left = node;
else prev.right = node; // 5.
}
else if (root != null)
System.out.println("el " + el + " is not in the tree");
else System.out.println("the tree is empty");
}
I got it as far as to find the node, but steps 1 through 5 (in comments) really breaks my brain.
First of all, you need to "find" the node. Let's try to think in recursive way:
function find(el, p)
if p == null, not found! (return p, i.e. null)
if p.el == el, found! (return p)
if el < p.el, return find(el, p.right)
if el > p.el, return find(el, p.left)
This is recursive, right? So, BSTNode<T> node = find(el, root) will return either the node that matches el or return null which implies NOT FOUND.
The remaining delete by merge only manages object reference, so I'm not to comment.
I think there is some problem with the merging algorithm steps 3 and 4. Imagine you are deleting a node with left and right subtree. The empty space is replaced with a node that must satisfy the BST condition (ie. greater than left node and smaller than right node). That replacement node is found in the rightmost left subtree (Step 2 of comments). That node is pointed to by tmp. So now we have "node" which points to the node being deleted and "tmp" which points to the replacement node. In Step 3, this is where the linking happens.
tmp.right = node.right
tmp.left = node.left // missing from the algorithm
Now in Step 4, this is where the "node" variable is changed to point to the replacement node "tmp" so in Step 5, the previous node "prev" can link to the replacement node. I believe Step 4 should be
node = tmp;
instead of
node = node.left;
Please check if my analysis is correct. Maybe that's why you're not able to understand what's going on. The recursive part of the deleteByMerging() is the searching for the node to delete. Once found, the algorithm to merge the tree would not change.

Pathfinding issues

Ok I am trying to make a dynamic pathing system so the player can move from point A to point B without having predefined paths. Note this game is all text based no graphics. The player can move in 10 directions: up, down, n, e, s, w, sw, se, nw and ne.
The map of the entire world is in a database, each row of the database contains a room or a node, each room/node has directions it's capable of going. The room may not be sequential persay. An Example:
Map Number, Room Number, Direction_N, Direction_S, Direction_E, Direction_W, etc.
1 1 1/3 1/100 1/1381 1/101
The Direction_N indicates it goes to Map 1 Room 3, Direction_S Map 1 Room 100, etc...
Ok, I reworked the code with suggestions (thank you guys by the way!) here is revised code. It seems to find the rooms now, even vast distances! But now the issue is finding the shortest path to the destination, I tried traversing the collection but the path is not coming out right...
In the image link below, I have start point in red square in center and stop point at red square at upper left. This returns visitedStartRooms = 103 and visitedStopRooms = 86, when it's only about 16 rooms.
Quess my missing piece of the puzzle is I am not sure how to sort out the rooms in those collection to gain the true shortest route.
Example of map
Here is the new code
public void findRoute(ROOM_INFO startRoom, ROOM_INFO destinationRoom)
{
Dictionary<ROOM_INFO, bool> visitedStartRooms = new Dictionary<ROOM_INFO, bool>();
Dictionary<ROOM_INFO, bool> visitedStopRooms = new Dictionary<ROOM_INFO, bool>();
List<string> directions = new List<string>();
startQueue.Enqueue(startRoom); // Queue up the initial room
destinationQueue.Enqueue(destinationRoom);
visitedStartRooms.Add(startRoom, true);// say we have been there, done that
visitedStopRooms.Add(destinationRoom, true);
string direction = "";
bool foundRoom = false;
while (startQueue.Count != 0 || destinationQueue.Count != 0)
{
ROOM_INFO currentStartRoom = startQueue.Dequeue(); // remove room from queue to check out.
ROOM_INFO currentDestinationRoom = destinationQueue.Dequeue();
ROOM_INFO startNextRoom = new ROOM_INFO();
ROOM_INFO stopNextRoom = new ROOM_INFO();
if (currentStartRoom.Equals(destinationRoom))
{
break;
}
else
{
// Start from destination and work to Start Point.
foreach (string exit in currentDestinationRoom.exitData)
{
stopNextRoom = extractMapRoom(exit); // get adjacent room
if (stopNextRoom.Equals(startRoom))
{
visitedStopRooms.Add(stopNextRoom, true);
foundRoom = true;
break;
}
if (stopNextRoom.mapNumber != 0 && stopNextRoom.roomNumber != 0)
{
if (!visitedStopRooms.ContainsKey(stopNextRoom))
{
if (visitedStartRooms.ContainsKey(stopNextRoom))
{
foundRoom = true;
}
else
{
destinationQueue.Enqueue(stopNextRoom);
visitedStopRooms.Add(stopNextRoom, true);
}
}
}
}
if (foundRoom)
{
break;
}
}
// start from the start and work way to destination point
foreach (string exit in currentStartRoom.exitData)
{
startNextRoom = extractMapRoom(exit); // get adjacent room
if (startNextRoom.Equals(destinationRoom))
{
visitedStartRooms.Add(startNextRoom, true);
foundRoom = true;
break;
}
if (startNextRoom.mapNumber != 0 && startNextRoom.roomNumber != 0)
{
if (!visitedStartRooms.ContainsKey(startNextRoom))
{
if (visitedStopRooms.ContainsKey(startNextRoom))
{
foundRoom = true;
break;
}
else
{
startQueue.Enqueue(startNextRoom);
visitedStartRooms.Add(startNextRoom, true);
}
}
}
}
if (foundRoom)
{
break;
}
}
}
You have a good start. There are a few basic improvements that will help. First, to be able to reconstruct your path, you should create a new data structure to store visited rooms. But for each entry, you want to store the room, plus the previous room in the path back to the starting point. A good data structure for this would be a dictionary where the key is the room identifier, and the value is the previous room identifier. To know if you've visited a room, you look to see if it exists in that data structure, not your openList queue. With this new structure, you can properly check if you've visited a room, and you can reconstruct the path back by repeatedly looking up the previous room in the same structure until you get to the origination.
The second improvement will increase the performance quite a bit. Instead of just doing a breadth-first search from the start point until you bump into the destination, as you currently do, instead create matching data structures like you have for the start room search, but have them be for the destination room. After you've looked one room away from the start, look one room away from the destination. Repeat this...two rooms away from start, then two rooms away from destination.. etc., working your way out, until you discover a room that has been visited by both your search from start and your search from destination. Build the path from this room back to the start, and back to the destination, and that will be your shortest path.
The problem you are trying to solve is the shortest path problem with unweighted edges or where the weights of all edges are equal. The weight of an edge is the time/cost to move from one room to another. If the cost to move from one room to another varies depending on which pair of rooms you're talking about, then the problem is more complicated, and the algorithm you started with and for which I've suggested modifications, will not work as it is. Here are some links about it:
Shortest path (fewest nodes) for unweighted graph
http://en.wikipedia.org/wiki/Shortest_path_problem
You may also be interested in the A* algorithm which uses a different approach. It uses a hueristic approach to concentrate the search in a subset of the solution space more likely to contain the shortest path. http://en.wikipedia.org/wiki/A%2a_search_algorithm
But A* is probably overkill in your case since the weight of all edges is the same between all rooms.

Resources