I'm very confused on how I would solve a maze using a queue. I've provided some javadocs and some psuedo code that my professor has given us. Help if possibble. I've looked on other topics but I could not understand there's hopefully someone can help me with my solve method. thanks
public class QueueMazeSolver implements MazeSolver {
private MazeGUI gui;
public static class Cell {
private int r;
private int c;
public Cell(int row, int col){
r = row;
c = col;
}
}
public QueueMazeSolver(){
gui = new MazeGUI( this );
}
/**
* This method is called when the start button is
* clicked in the MazeGUI. This method should solve the maze.
* This method may call MazeGUI.drawMaze(...) whenever the
* GUI display should be updated (after each step of the solution).
*
* The maze is provided as the first parameter. It is a 2D array containing
* characters that represent the spaces in the maze. The following
* characters will be found in the array:
* '#' - This represents a wall.
* ' ' - This represents an open space (corridor)
*
* When calling MazeGUI.drawMaze(...) to update the display, the GUI
* will recognize the '#' and ' ' characters as well as the following:
* '#' - Means the cell is a space that has been explored
* '%' - Means that the cell is part of the best path to the goal.
*
* #param maze the maze (see above).
* #param startR the row of the start cell.
* #param startC the column of the start cell.
* #param endR the row of the end (goal) cell.
* #param endC the column of the end (goal) cell.
*/
#Override
public void solve(char[][] maze, int startR, int startC, int endR, int endC) {
maze[startR][startC] = '#';
ArrayQueue<Cell> agenda = new ArrayQueue<Cell>();
Cell temp = new Cell(startR, startC);
agenda.offer(temp);
// while agenda is not empty and red not found
while(!agenda.isEmpty() && maze[endR][endC] != '#' ){
Cell current = agenda.poll(); //remove front square from queue
/*
if front square is red
found it
else
mark amaze all unexplored neighbors of front
square and add them to the square
*/
if(current == new Cell(endR, endC) ){
break;
}
else{
=
}
}
/** Notes
maze[r][c] = '#' //marking cell seen
up = r-1, c
down = r+1, c
left = r, c-1
right = r, c+1
*/
}
if (!agenda.isEmpty())
gui.setStatusText("Maze is solvable");
else
gui.setStatusText("Maze is unsolvable");
gui.drawMaze(maze);
try {Thread.sleep(150);}
catch (InterruptedException e){
System.err.println("Thread interrupted");
}
}
public static void main(String[] args){
QueueMazeSolver solver = new QueueMazeSolver();
}
}
it seems you are trying to get possible paths to move in a maze and reach red cell , which has walls (can't cross) or open space .
Basically this code is applying a breadth first search .
we remove a cell from queue and if the surrounding cells [at distance 1 unit] are not visited , add them to queue and visit them .
Pseudocode (from wikipedia) :
1 procedure BFS(G,v) is
2 create a queue Q
3 create a vector set V
4 enqueue v onto Q
5 add v to V
6 while Q is not empty loop
7 t ← Q.dequeue()
8 if t is what we are looking for then
9 return t
10 end if
11 for all edges e in G.adjacentEdges(t) loop
12 u ← G.adjacentVertex(t,e)
13 if u is not in V then
14 add u to V
15 enqueue u onto Q
16 end if
17 end loop
18 end loop
19 return none
20 end BFS
suppose you are at cell(i,j) , therefore t=(i,j) and adjacentEdges(t) are (i+1,j) , (i,j+1) , (i-1,j) . (i,j-1)
If (i+1,j) is not visited before , add it to queue (so , next time you pop from queue , you will get it) else if it is visited (i.e in V) then we are done with it . Repeat same for other three cells .
This way you do O(m*n) operations and visit each cell exactly once .
Related
I'm building a Sudoku Game. I came here to get some help because I'm completely stuck in my code. I'm not asking for you to complete my code, I know that's not your job. Just few hints as what to do next would be great!
I use MVC and Swing Components for GUI to make the code lighter. I divided each field and method so I can understand what to do next but I'm confused. I'm particularly having trouble understanding how to do the following methods:
initializeGrid
chooseGameDifficulty
makeMove
cancelMove
Model
public class GameSudokuModel {
// states -- fields
Scanner userInput = new Scanner (System.in); // accept user input
// int levelDifficulty = 0; // level of difficulties
int [] gridSize ; // Sudoku 9x9 == 81 cells -- used to initialize grid or solve puzzle --
int [] subGridSize ; // a sub-grid = 9 cells
int gameMove = 0; // calculate the total number of moves per game // ++makeMove and --cancelMove
int [] gameCell = {1, 2, 3, 4, 5, 6, 7, 8, 9}; // a cell contain a list of choices numbers 1-9
int currentGameTime = 0; // calculates the total time to complete a puzzle
String currentPlayerName = userInput.nextLine(); // player name
// end of fields
//behaviors -- methods
/******************************************************
*
* Method calculateGameTime (initialiserGrille)
*
*
* Calculates time
*
* The stopwatch starts when the player makes his first move
*
*
*
******************************************************/
public class calculateGameTime{
}
/******************************************************
*
* Method initializeGrid (initialiserGrille)
*
*
* Used to initialize a grid
*
* Reset the grid ( back to the original Sudoku grid ) using the list of moves .
*
*
*
*
*
******************************************************/
public class initializeGrid {
}
/******************************************************
*
* Method levelDifficulty
*
*
* Established the parameters of level of difficulty
*
*
* #param beginner
* #param expert
* #return
******************************************************/
public int levelDifficulty (int beginner, int expert){
while(true)
{
int levelDifficulty = 0;
levelDifficulty= userInput.nextInt();
System.out.println (" ");
if(levelDifficulty < beginner || levelDifficulty> expert){
System.out.print (" You must choose 1, 2 or 3." + "Please try again : ");
System.out.println (" ");
}
else
return levelDifficulty;
}
}
/****************************************************
* Method chooseGameDifficulty (chosisirNiveauDifficulte)
*
* The method makes possible to choose the level of complexity of a grid
*
* (1) beginner: the player starts the game with a grid made up to 75% (81 * 0.75)
*
* (2) Intermediate : the player starts the game with a grid made up to 50% (81 * 0.50)
*
* (3) Expert : the player starts the game with a grid made up to 25% (81 * 0.25)
*
* Numbers are set randomly on the grid every new game
*
* #param beginner
* #param intermediate
* #param expert
******************************************************/
public void chooseGameDifficulty(int beginner, int intermediate, int expert){
boolean entreeValide;
int levelDifficulty;
String reponse;
levelDifficulty= levelDifficulty(beginner,expert); // call function levelDifficulty()
if(levelDifficulty==beginner)
//get easy level grid (getter)
//set easy level grid (setter)
if(levelDifficulty==intermediate)
//get intermediate level grid (getter)
//set intermediate level grid (setter)
if(levelDifficulty==expert)
//get expert level grid (getter)
//set easy expert grid (setter)
}
/****************************************************
* Method solvePuzzle (resoudrePuzzle)
*
* This method makes possible to solve the entire grid meaning all the 81 cells
*
******************************************************/
public class solvePuzzle {
}
/****************************************************
* Method makeMove (fairePlacement)
*
* Save a record of the player's actions on the grid.
*
*
*
* (1) make move on the grid ;
* (2) save moves in an array list
*
******************************************************/
public class makeMove {
//choose a cell , enter a number on the cell and confirm the selection
// adds move to the array list
int makeMove = gameMove++;
}
/****************************************************
* Method cancelMove (annulerPlacement)
*
*
*
* (1) retrieve the last instance in the arraylist (using the remove method and the size method to determine the number of elements )
* (2) cancel the move in the grid.
*
******************************************************/
public class cancelMove {
//choose a cell , remove the number on the cell and confirm the cancellation
//substracts move from array list
int cancelMove = gameMove--;
}
}
initializeGrid and chooseGameDifficulty aren't really features of the model. The model maintains the current state of the data and the rules uses to manage it.
Technically, these features should be functions of some kind of factory that given a difficult level will return a instance of the model
public class SudokuFactory {
public enum Difficulty {
HARD,
MODERATE,
EASY
}
public SudokuModel createModel(Difficulty difficult) {
// Make a new model based on the rules for your difficulty
// settings
}
}
The model would then simply contain the information and functionality to manage it
You should also avoid static where practically possible, it should never be used as a cross class communication mechanism, if you need to share data, you should pass it. static just makes the whole thing a lot more difficult to manage and debug
The view would get the information from the user (like the difficulty level), which would be used by the controller to build a new model. The model would then be passed to a new controller, which would generate a new view, which should present the current state of the model.
The controller would then respond to changes in the view, updating the model and the controller would respond to changes in the model and update the view.
You should also prefer using interfaces over implementation
So, based on my (rather pathetic) understanding of Sudoku you could use a model as simple as ...
public interface SudokuModel {
public void setValueAt(int value, int row, int col) throws IllegalArgumentException;
public int getValueAt(int row, int col);
}
Now, me, personally, I'd have an implementation that had two buffers, one which represents the actual game/solution and one which represents the player data (pre-filled based on the difficulty level), now you could have a single buffer, but you'd have constantly scan the grid to see if the new value was valid and I'm just too lazy ;)
Problem link :https://www.hackerrank.com/challenges/jim-and-the-skyscrapers
Problem Statement
Let us describe the problem in one dimensional space. We have in total N skyscrapers aligned from left to right. The ith skyscraper has a height of hi. A flying route can be described as (i,j) with i≠j, which means, Jim starts his HZ42 at the top of the skyscraper i and lands on the skyscraper j. Since HZ42 can only fly horizontally, Jim will remain at the height hi only. Thus the path (i,j) can be valid, only if each of the skyscrapers i,i+1,...,j−1,j is not strictly greater than hi and if the height of the skyscraper he starts from and arrives on have the same height. Formally, (i,j) is valid iff ∄k∈[i,j]:hk>hi and hi=hj.
My Approach :
I have used stack. If next integer is lesser than top of stack, it is pushed into the stack. If it is greater, top of stack (first top) is popped and compared with the top of stack. If they are equal counter is increased and again top is popped and new top is compared with previous top of stack (first top). Process is repeated till top of stack (first top) is not equal to the top of stack.
Finally If stack is not empty, I am again counting duplicate element.
This counters of a specific element are stored in an array list on the fly.
Is this a good approach? Is stack the right choice? Can my code be improvised to speed up?
My code is correct but few test cases are terminated due to timeout.
Input Format
The first line contains N, the number of skyscrapers. The next line contains N space separated integers representing the heights of the skyscrapers.
Output Format
Print an integer that denotes the number of valid routes.
My Code:
import java.io.*;
import java.util.*;
import java.text.*;
import java.math.*;
import java.util.regex.*;
public class Solution {
// method to calculate nCr
public static long choose(long total, long choose){
if(total < choose)
return 0;
if(choose == 0 || choose == total)
return 1;
return choose(total-1,choose-1)+choose(total-1,choose);
}
public static void main(String[] args) {
/* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */
Scanner sc = new Scanner(System.in);
int t = sc.nextInt();
int count = 1;
long x = 0;
Stack s = new Stack();
ArrayList<Integer> alist = new ArrayList<Integer>();
for(int i=0 ; i<t ; i++){
int a = sc.nextInt();
if(s.isEmpty() || ((int)s.peek()>= a) ){
s.push(a);
} else {
while(!(s.isEmpty()) && a > (int)s.peek() ){
int top = (int)s.pop();
while(!(s.isEmpty()) && top == (int)s.peek()){
count++;
s.pop();
}
if(count>1){
alist.add(count);
count=1;
}
}
s.push(a);
}
}
while(!(s.isEmpty()) ){
int tp = (int)s.pop();
while(!(s.isEmpty()) && tp == (int)s.peek()){
count++;
s.pop();
}
if(count>1){
alist.add(count);
count=1;
}
}
for(Integer n : alist){
x += choose(n,2);
}
System.out.println(2*x);
}
}
your approach seems to me somewhat complicated and (consequently) time-consuming.
Here's an alternative which I doubt will ever use any relevant amount of time - in VB; I'm sure you can translate it in whatever language you'd like.
I present only the "heart" of the method (without handling of input and output)
Dim result As New SortedDictionary(Of Integer, List(Of Flight)) 'key: index of skyscraper where the flight starts.
Dim flightsWithSameStart As List(Of Flight) = Nothing 'placeholder
Dim hValue As Integer
Dim openFlights As New List(Of Flight)(hValues.Count) 'hValues is a list of (integer) skyscraper heights, provided by the user.
For indx As Integer = 0 To hValues.Count - 1
hValue = hValues(indx)
Dim n As Integer = openFlights.Count
For k As Integer = n - 1 To 0 Step -1
Dim fl As Flight = openFlights(k)
If hValue > fl.height Then
' Remove the Flight: it cannot continue and (apparently) it hasn't landed.
openFlights.RemoveAt(k)
ElseIf hValue = fl.height Then
' This flight will branch:
' a) it can stop here, landing at this height.
' b) it can also continue.
' We achieve this by storing a CLONE of the current flight and NOT removing this current flight from the open flights.
Dim flCopy As Flight = fl.Clone
flCopy.last = indx
If Not result.TryGetValue(flCopy.first, flightsWithSameStart) Then
flightsWithSameStart = New List(Of Flight)
result.Add(flCopy.first, flightsWithSameStart)
End If
flightsWithSameStart.Add(flCopy)
Else
Exit For
End If
Next
' At each skyscraper we hopefully launch another flight.
Dim flNew As New Flight
flNew.first = indx
flNew.height = hValue
openFlights.Add(flNew)
Next
' Discard any remaining open flights: they cannot land.
' (just ignore whatever is left in 'openFlights')
A simple helper class Flight is used:
Private Class Flight
Public first As Integer
Public last As Integer
Public height As Integer
Public Function Clone() As Flight
Dim copy As Flight = CType(Me.MemberwiseClone, Flight)
Return copy
End Function
End Class
Apologize if I made errors (or maybe misunderstood the problem) - just ran a few testcases and they seemed ok to me.
Regards
(forgot to mention that whenever i=>j is a valid path/flight, then so is j=>i, so it suffices to compute the cases with j>i, as in my code (and then reversing them, if you like, to add their mirrors).
I am seeking guidance in the respect of optimising code. The code I have written is for a text-based game in which you type in commands into a command bar. One feature I wished to incorporate into my interface was the ability to scroll through a history of one's last 100 commands entered using the up and down arrow keys so that it would be more convenient for the user to play the game.
I have designed a class in which uses a String[] that will store each new entry in the second position (Array[1]) and move all entries back one position while the first position of the array (Array[0]) is just a blank, empty string. The code initialises the array to have 101 values to compensate for the first position being a blank line.
When a user inputs 0 - 100 in that order, it should then give me the reverse of the order (almost like a last in, first out kind of situation, but storing the last 100 values as opposed to removing them once they are accessed), and since 0 - 100 is 101 values, the last value will be overwritten.
Thus, scrolling up through the history, it would give me 100, 99, 98, ..., 2, 1. If I were to select 50 from the list, it would then be 50, 100, 99, ..., 3, 2. The code indeed does this.
The code is listed below:
public class CommandHistory {
private String[] history;
private final int firstIndex = 1;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
history = new String[101];
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity + 1];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
private int size() {
return history.length;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
history[0] = "";
if (!command.equals("")) {
for (int i = firstIndex; i < size();) {
if (history[i] == null) {
history[i] = command;
break;
} else {
for (int j = size() - 1; j > firstIndex; j--) {
history[j] = history[j - 1];
}
history[firstIndex] = command;
break;
}
}
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex > 0) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex >= 0 && (history[currentIndex + 1] != null)) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = firstIndex; i < size(); i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size(); i++) {
history += this.history[i];
}
return history;
}
}
In my interface class, once the user types something into the command bar and hits enter, it will get the text currently stored in the bar, uses the add method to add it to the history, parses the command via another class, and then sets the text in the bar to blank.
Pressing the up arrow calls the next method which scrolls up the list, and the down arrow calls the previous method which scrolls down the list.
It seems to work in every way I wish it to, but I was wondering if there was some way to optimise this code or perhaps even code it in a completely different way. I am making this game to keep myself practiced in Java and also to learn new and more advanced things, so I'd love to hear any suggestions on how to do so.
The comments to your question have already pointed out that you are somehow trying to reinvent the wheel by implementing functionality that the standard Java class library already provides to some extent (see LinkedList/Queue and Arraylist). But since you say you want to keep yourself practiced in Java I guess it is perfectly fine if you try to implement your own command history from scratch.
Here are some of my observations/suggestions:
1) It is not necessary and very counter-intuitive to declare a final first index of 1. It would be easy to start with a default index of 0 and add corresponding checks where necessary.
2) Forget about your private size() method - it is just returning the length of the internal array anyway (i.e. the initial capacity+1). Instead consider adding a public size() method that returns the actual number of added commands and internally update the actual size when adding new commands (see e.g. java.util.ArrayList for reference).
3) At the moment every call to add(String command) will set history[0] = "", which is not necessary. If you want the first index to be "", set it in the constructor. This is also a clear sign, that it would perhaps be better to start with an initial index of 0 instead of 1.
4) A minor issue: "if (!command.equals(""))" during your add method is perhaps OK for such a specialized class but it should definitely be commented in the documentation of the method. Personally I would always let the calling class decide if an empty "" command is considered valid or not. Also this method will throw an undocumented NullPointerException, when null is used as an argument. Consider changing this to "if (!"".equals(command))" or throw an IllegalArgumentException if null is added.
5) "if (history[i] == null)" during the add method is completely unnecessary, if you internally keep a pointer to the actual size of the commands - this is actually a special case that will only be true, when the very first command is added to the command history (i.e. when it's actual size == 0).
6) Having two nested for loops in your add method implementation is also unnecessary, if you keep a pointer to the actual size (see example below)
7) I would reconsider if it is necessary to keep a pointer to the current index in the command history. Personally I would avoid storing such a pointer and leave these details to the calling class - i.e. remove the previous and next methods and either provide a forward/backward Iterator and/or a random access to the index of the available commands. Interestingly, when this functionality is removed from your command history class, it actually comes down to either an implementation of a LinkedList or an ArrayList- whichever way you go. So in the end using one of the built in Java collections would actually be the way to go.
8) Last but nor least I would reconsider if it is useful to insert added commands at the beginning of the list - I believe it would be more natural to append them to the end as e.g. ArrayList does. Adding the commands to the end would make the swapping of all current commands during each call to add() unnecessary...
Here are some of the suggested changes to your class (not really tested...)
public class CommandHistory {
private String[] history;
private int size;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
this(100);
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
public int size() {
return size;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
if (!"".equals(command)) {
if (this.size < history.length) {
this.size++;
}
for (int i = size-1; i >0; i--) {
history[i] = history[i-1];
}
history[0] = command;
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex >= 0 && currentIndex < size-1) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex > 0 && currentIndex < size) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = 0; i < size; i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size; i++) {
history += this.history[i] + ", ";
}
return history;
}
}
Well, I guess I have invested far too much time for this, but I learned quite a bit myself on the way - so thanks ;-)
Hope some of this is useful for you.
This is my first post so apologies if I haven't quite got the framing of the question quite right yet. I'll put my hands up straight away and say this is part of my 'homework', I've got about 99% of it done but I've got this niggling bug that has been driving me crazy all week and I simply can't see the wood through the trees here.
The basic concept is that we have a text file, a poem of sorts, that our program has to look at and then place into a binary tree. We should record each word, the occurrence of each word and which line it appears in. I have managed to do that, save for that for in the example "And I went to StackOverFlow and I asked the question, and the answer was..." my program should return that the word "And" appears in line 1 (which it currently does), but it is currently recording every occurrence of that word and it should only record that the word "and" occurs in line 1 and not every instance of it.
So in the above example "And I went to StackOverFlow and I asked the question, and the answer was.."
So just picking out the word "And" here since it occurs multiple times, my program is currently returning:
"And [1, 1, 1]"
But it should be returning:
"And [1]"
I have supplied the code for the entire program, but the method I think to focus on is the recordWord method, at the final else if, something isn't quite right there clearly! Apologies if this explanation is hugely convoluted but I couldn't think of another way to explain it! Any help would be hugely appreciated
import java.util.*;
/**
* A class representing a binary tree containing words.
*
*/
public class WordTree {
public String word;
public ArrayList<Integer> lineNumbers;
public WordTree left, right;
/**
* Constructs a tree consisting of a single node, with the given word and
* line number.
*
* #param w
* the word
* #param lineNo
* the line number
* #pre true
* #post word tree containing word w on line lineNo has been constructed
*/
public WordTree(String w, int lineNo) {
word = w;
lineNumbers = new ArrayList<Integer>();
lineNumbers.add(lineNo);
left = null;
right = null;
}
/**
* Records a new occurrence of the given word, in the tree.
*
* #param w
* the word
* #param lineNo
* the line number
* #pre this is a well formed binary search tree
* #post if word was not in this tree, then the word and its line number
* line have been inserted into ordered word tree, else line has been
* appended to line-number list for word (if we haven't already
* recorded that line number for this word)
*/
public void recordWord(String word2, int lineNo) {
if (word.compareToIgnoreCase(word2) < 0) {
if (right != null) {
right.recordWord(word2, lineNo);
} else {
right = new WordTree(word2, lineNo);
}
} else if (word.compareToIgnoreCase(word2) > 0) {
if (left != null) {
left.recordWord(word2, lineNo);
} else {
left = new WordTree(word2, lineNo);
}
} else if (word.compareToIgnoreCase(word2) == 0) {
lineNumbers.add(lineNo);
}
}
// System.out.println("method recordWord not implemented yet");
/**
* Displays all the words in this tree.
*
* #pre this is a well formed binary search tree
* #post words have been written out in alphabetical order, each followed by
* ascending list of line numbers on which the word occurs
*/
public void display() {
if (left != null) {
left.display();
}
System.out.println(word + lineNumbers);
if (right != null) {
right.display();
}
}
/**
* Counts how many different words there are in this tree.
*
* #pre this is a well formed binary search tree
* #return the number of different words in tree
*/
public int numberOfEntries() {
int count = 1;
if (left != null) {
count += left.numberOfEntries();
}
if (right != null) {
count += right.numberOfEntries();
}
}
}
Since List accepts duplication, it may accepts elements with the same value. Instead, you could use Set interface which eliminates duplication for you automatically. In your example, you can declare lineNumbers as a TreeSet to print the output in ascending line number.
Declare lineNumbers as a Set
public Set<Integer> lineNumbers;
Then initialize it in the constructor using TreeSet
lineNumbers = new TreeSet<Integer>();
It's all that you need to change. Hope it's clear enough.
Intro to problem:
I am given recipes how to craft items. Recipe is in format : {element that is being crafter}: {list of elements, that is needed}. Before I can craft element x, I need to know how to craft elements it's made of. So I want to find in what order do I have to learn recipes.
For valid input, like following everything works:
// Input:
{
"F1: F2 F3 F4", "F5: F6 F4", "F6: F7 F8 F4", "F2: F3 F8 F4", "F8: F4",
"F9: F4", "F7: F4", "F10: F7 F4", "F11: F4", "F4:", "F3: F6"
}
// Output:
[F4, F7, F8, F6, F3, F2, F1, F5, F9, F10, F11]
The problem is, that task is more complex. Time to time I have some recipes missing or thy are invalid. Example of invalid input: { "F1: F2", "F2: F1" }.
Code example:
mp contains recipe name as key and elements as value, labels are unique mp keys and result will contain answer. I'm looking for a way to return empty result if infinite loop is met.
private void getArray(HashMap<String, ArrayList<String>> mp,
ArrayList<String> result, ArrayList<String> labels) {
for (String a : labels) {
if (mp.get(a) != null)
for (String label : mp.get(a))
getArray(mp, result, label);
if (!result.contains(a))
result.add(a);
}
}
private void getArray(HashMap<String, ArrayList<String>> mp,
ArrayList<String> result, String label) {
if (result.contains(label))
return;
if (mp.get(label) == null) {
result.add(label);
return;
}
for (String l : mp.get(label))
getArray(mp, result, l);
if (!result.contains(label))
result.add(label);
}
Edit
Problem solved.
For any Google's that stumble up this, this is what I came up with:
/** <p>
* <b>Topological sort</b> solves a problem of - finding a linear ordering
* of the vertices of <i>V</i> such that for each edge <i>(i, j) ∈ E</i>,
* vertex <i>i</i> is to the left of vertex <i>j</i>. (Skiena 2008, p. 481)
* </p>
*
* <p>
* Method is derived from of <a
* href="http://en.wikipedia.org/wiki/Topological_sort#Algorithms" > Kahn's
* pseudo code</a> and traverses over vertices as they are returned by input
* map. Leaf nodes can have null or empty values. This method assumes, that
* input is valid DAG, so if cyclic dependency is detected, error is thrown.
* tSortFix is a fix to remove self dependencies and add missing leaf nodes.
* </p>
*
* <pre>
* // For input with elements:
* { F1=[F2, F3, F4], F10=[F7, F4], F11=[F4], F2=[F3, F8, F4], F3=[F6],
* F4=null, F5=[F6, F4], F6=[F7, F8, F4], F7=[F4], F8=[F4], F9=[F4]}
*
* // Output based on input map type:
* HashMap: [F4, F11, F8, F9, F7, F10, F6, F5, F3, F2, F1]
* TreeMap: [F4, F11, F7, F8, F9, F10, F6, F3, F5, F2, F1]
* </pre>
*
* #param g
* <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph"
* > Directed Acyclic Graph</a>, where vertices are stored as
* {#link java.util.HashMap HashMap} elements.
*
* #return Linear ordering of input nodes.
* #throws Exception
* Thrown when cyclic dependency is detected, error message also
* contains elements in cycle.
*
*/
public static <T> ArrayList<T> tSort(java.util.Map<T, ArrayList<T>> g)
throws Exception
/**
* #param L
* Answer.
* #param S
* Not visited leaf vertices.
* #param V
* Visited vertices.
* #param P
* Defined vertices.
* #param n
* Current element.
*/
{
java.util.ArrayList<T> L = new ArrayList<T>(g.size());
java.util.Queue<T> S = new java.util.concurrent.LinkedBlockingDeque<T>();
java.util.HashSet<T> V = new java.util.HashSet<T>(),
P = new java.util.HashSet<T>();
P.addAll(g.keySet());
T n;
// Find leaf nodes.
for (T t : P)
if (g.get(t) == null || g.get(t).isEmpty())
S.add(t);
// Visit all leaf nodes. Build result from vertices, that are visited
// for the first time. Add vertices to not visited leaf vertices S, if
// it contains current element n an all of it's values are visited.
while (!S.isEmpty()) {
if (V.add(n = S.poll()))
L.add(n);
for (T t : g.keySet())
if (g.get(t) != null && !g.get(t).isEmpty() && !V.contains(t)
&& V.containsAll(g.get(t)))
S.add(t);
}
// Return result.
if (L.containsAll(P))
return L;
// Throw exception.
StringBuilder sb = new StringBuilder(
"\nInvalid DAG: a cyclic dependency detected :\n");
for (T t : P)
if (!L.contains(t))
sb.append(t).append(" ");
throw new Exception(sb.append("\n").toString());
}
/**
* Method removes self dependencies and adds missing leaf nodes.
*
* #param g
* <a href="http://en.wikipedia.org/wiki/Directed_acyclic_graph"
* > Directed Acyclic Graph</a>, where vertices are stored as
* {#link java.util.HashMap HashMap} elements.
*/
public static <T> void tSortFix(java.util.Map<T, ArrayList<T>> g) {
java.util.ArrayList<T> tmp;
java.util.HashSet<T> P = new java.util.HashSet<T>();
P.addAll(g.keySet());
for (T t : P)
if (g.get(t) != null || !g.get(t).isEmpty()) {
(tmp = g.get(t)).remove(t);
for (T m : tmp)
if (!P.contains(m))
g.put(m, new ArrayList<T>(0));
}
}
The problem you are solving is known as topological sort. Kahn's algorithm solves the problem while also detecting invalid input (that is, containing cycles).
The quick way to do this is to remember the set of items that you've already seen, and simply throw an exception if you're about to work out the requirements for an item already in that list. This will definitely indicate some kind of circularity which we can probably assume is a bad thing.
A more advanced solution, if loops in the object graph are allowable, would be to not just store the items, but also map them to their solution. Since you're in the process of calculating the solutions, this would perhaps need to be a Future that you pop in the map to indicate that the evaluation may not be complete yet. Then you can add the "solution placeholders" in the map as soon as you visit a given item. Consequently infinite loops work fine (looking at your trivial invalid case):
Visit F1. Put a Future for this in the map. Recurse to work out F2.
Visit F2. Put a Future for this in the map. See that F1 has already been "solved", and record the concrete solution for F2 as simply creating an F1.
This of course represents a loop in the solution's object model, but that's actually a legitimate representation of the input. If you were displaying this graphically, perhaps as a tree that gets expanded one level at a time, this would render appropriately too ("How do I create an F1? You need to make an F2. How do I create that? You need to make an F1", and so on for as many levels as the user expanded the tree).
(Note that while this sounds silly it can actually reflect some valid scenarios; e.g. in Fallout: New Vegas, several crafting recipes convert between different energy ammo types. Thus to make a Microfusion Cell you need three Small Energy Cells. And to make three Small Energy Cells you need a Microfusion Cell. This would create a loop in the graph, but in this case is actually valid input. I'm just pointing this out in case you're assuming that loops are always wrong input.)
If it is possible for a recipe to have alternatives, perhaps somewhere between these two approaches is the best bet. Follow all of the alternative paths, but if you get into an infinite loop, stop following that one and go with the others.