Background: Imagine I have a little Robot. I place this Robot at some Node in a Map (Graph). The Robot can call the giveMeMapCopy() method to get a copy of the whole map that he is sat in. I want to give my little Robot a function by which he can use a breadth first traversal to find the shortest path to the Exit node. Here is an example of such a map:
I have watched videos on YouTube on how to do a breadth first traversal of a graph, so I have a good idea of what needs to be done. The problem is, I am finding it hard to make my logic recursive. Here is my code:
public class Robot
{
// fields required for traversal
private Queue<ArrayList<String>> queue;
private ArrayList<ArrayList<String>> result;
private String workingNode;
private ArrayList<String> routeSoFar;
private Queue<String> knownShortestPath;
public Robot() {
queue = new LinkedList<ArrayList<String>>();
result = new ArrayList<ArrayList<String>>();
routeSoFar = new ArrayList<String>();
knownShortestPath = new LinkedList<String>();
}
// Runs when Robot has reached a node.
public void enterNodeActions() {
knownShortestPath = determineIdealPath();
}
// Runs to determine where to go next
public String chooseNextNode() {
if(!knownShortestPath.isEmpty())
{
// TODO: Need to go through the
}
}
public LinkedList<String> determineIdealPath()
{
try {
// Get the map
Map m = giveMeMapCopy();
// Get all entry nodes of map
Set<String> entryNodes = m.getEntryNodes();
/*
* Loop through all Entry nodes, and find out where we are.
* Set that as current working node.
*/
for (String n : entryNodes) {
if(n == getMyLocation())
{
workingNode = n;
}
}
// All enighbours of working node.
Set<String> neighboursNames = getNeighboursNames(workingNode);
/*
* For each neighbour, construct a path from working node to the neighbour node
* And add path to Queue and Result (if not already present).
*/
for(String node : neighboursNames)
{
if(!node.equals(getMyLocation()))
{
ArrayList<String> route = new ArrayList<String>();
route.add(getMyLocation());
route.add(node);
if(!containsRoute(result, route))
{
if(!containsRoute(queue, route))
{
queue.add(route);
}
result.add(route);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Where I want the recursion to happen is after I have gone through all neighbours of the Entry node [ A ], I want to move to the next [ B ] and do the same for that, i.e. go through each of its neighbours (ignoring A, cause it is already present in Result list) and add them to the Queue and Result lists.
I hope the problem is clear, if not please let me know, and I'll try to clarify anything is not clear.
Breadth-first search is typically done without recursion as it is based on a queue (of partial path in your case). Depth-first search on the other hand is based on a stack, wich can be implemented quite naturally using the call-stack of a recursive function.
Essentially, what you want is to implement Dijkstra's algorithm or something similar to find the shortest path between a source and destination in a graph.
You can find such implementation in Java here. Just set all the weights at 1.
Related
I have the own data structure for the graph, and I need the implementation method:
List<Edge<T>> getPath(T start, T finish)
Performance not important, I search the simplest and most readable way. But my data structure should support the directed and undirected graph types and I stuck with it.
public class Graph<T> {
private boolean isDirected = false;
private Map<Vertex<T>, List<Edge<T>>> graph = new HashMap<>();
public Graph() {
}
public Graph(boolean isDirected) {
this.isDirected = isDirected;
}
public List<Edge<T>> getPath(T start, T finish) {
if (start.equals(finish)) {
return new ArrayList<>();
}
// TODO here is the method I'm stuck with.
if (isDirected) {
// Call search for directed graph
} else {
// Call search for undirected graph
}
}
public void addEdge(T first, T second) {
final Vertex<T> master = new Vertex<>(first);
final Vertex<T> slave = new Vertex<>(second);
final Set<Vertex<T>> vertices = graph.keySet();
if (!vertices.contains(master) || !vertices.contains(slave)) {
throw new IllegalArgumentException();
}
graph.get(master).add(new Edge<>(master, slave));
if (!isDirected) {
graph.get(slave).add(new Edge<>(slave, master));
}
}
public void addVertex(T value) {
final List<Edge<T>> result = graph.putIfAbsent(new Vertex<>(value), new ArrayList<>());
if (result != null) {
throw new IllegalArgumentException();
}
}
}
This Vertex and Edge class:
#Data
#AllArgsConstructor
#EqualsAndHashCode
public class Vertex<T> {
private T value;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Edge<T> {
private Vertex<T> first;
private Vertex<T> second;
}
I will be very grateful for Your help.
It is not totally clear what kind of path you want to find. The shortest path, any path,...?
If you want to find the shortest path, A* is a really simple algorithm to implement. The pseudo code can be found here. A* is a best-first search algorithm for a weighted graph (E.g. the distance of an edge or another kind of cost to travel on the edge like time). The algorithm uses a heuristic function to select the next node/vertex to evaluate. A* basically repeats the following steps:
Select a next node/vertex which has not already been visited. The selection is made using the heuristic function
If this new node equals the goal position, return the shortest path found
Evaluate all paths currently known and select the one with the lowest cost
I could also provide a Java code snippet (based on the pseudo code) if it's necessary. Be aware that the pseudo code in the end constructs the shortest path backwards (from goal to start).
You are also using a generic for your graph. Both your Vertext and Edge class use this generic. Let's assume that this generic T is a double. In your code this means that your Vertex is only a one-dimensional double. This does not make sense when you want to represent a graph of 2D or 3D points.
Is it even really necessary to use this generic? Wouldn't it be sufficient to simply support vertices which consists of floats, doubles or integers? Using a generic type or more abstract class (like Number) might give some problems when you for example want to compute the distance between vertices.
I'm having a bit of trouble in my head trying to solve this:
I'm working on a "rankList", an arrayList made of "Score". Score it's the object that has the following atributes: name,wins,loses,draws. My class Ranking has an ArrayList of Score objects. To create a new Score object I just use the name (and set the rest to 0 since it's new). However I'm trying to check if the player's name it's already in rankList I don't have to create new but sum a win or lose or draw.
I have been reading arround that I have to override equals then others say I have to override contains... It's getting a big mess in my head. My fastest solution would be to write an "for" that goes arround the arrayList and use the getName().equals("name"); however this is getting too messi in my code. I have checkPlayer (if the palyer is in the list):
public boolean checkPlayer(String playerName) {
for (int i = 0; i < this.rankList.size(); i++) {
if (this.rankList.get(i).getName().equals(playerName)) {
return true;
}
}
return false;
}
then if I want to incrase the wins i have this :
public void incraseWins(String playerName) {
if (checkPlayer(playerName)) {
for (int i = 0; i < this.rankList.size(); i++) {
if (this.rankList.get(i).getName().equals(playerName)) {
this.rankList.get(i).setWins(this.rankList.get(i).getWins() + 1);
break;
}
}
} else {
createPlayer(playerName);
//more for to get to the player i'm looking for...
for (int i = 0; i < this.rankList.size(); i++) {
if (this.rankList.get(i).getName().equals(playerName)) {
this.rankList.get(i).setWins(this.rankList.get(i).getWins() + 1);
break;
}
}
}
So i guess there is a better way to do this... :/
ArrayList is not the right data structure here. To check if an element exists in the array you are searching the entire arraylist. Which means it's O(N).
To keep an array list is sorted order and do a binary search on it would definitely be faster as suggested in the comments. But that wouldn't solve all your problems either because insert into the middle would be slow. Please see this Q&A: When to use LinkedList over ArrayList?
One suggestion is to use a Map. You would then be storing player name, player object pairs. This would give you very quick look ups. Worst case is O(log N) i believe.
It's also worth mentioning that you would probably need to make a permanent record of these scores eventually. If so an indexed RDBMS would give you much better performance and make your code a lot simpler.
Try using a hashtable with a key, it would be much more efficient!
e..Why not using map<>.
a binary search is good idea if you must use List,code like this
List<Method> a= new ArrayList<>();
//some method data add...
int index = Collections.binarySearch(a, m);
Method f = a.get(index);
and class method is impl of Comparable,then override compareTo() method
public class Method implements Comparable<Method>{
........
#Override
public int compareTo(Method o) {
return this.methodName.compareTo(o.getMethodName());
}
if you don't want use binsearch,CollectionUtils in commons can help you
CollectionUtils.find(a, new Predicate() {
#Override
public boolean evaluate(Object object) {
return ((Method)object).getMethodName().equals("aaa");
}
});
in fact CollectionUtils.find is also a 'for'
for (Iterator iter = collection.iterator(); iter.hasNext();) {
Object item = iter.next();
if (predicate.evaluate(item)) {
return item;
}
}
I have a Stack<String> defined in Java that I use for navigation through a workflow.
What I would like to do is ensure that all values in the stack are unique: when a transition to a "previous" state occurs, I want to remove everything off the stack AFTER the first occurrence of the previous state in the stack.
Is there an easy way to do this?
Edit: More information was requested. Here's an example of the contents of a stack:
[state2, state3, state2, state1, startState]
I need the ability to accept a String, check the stack and see if there are multiple occurrences of it, then pop elements until the "bottommost" occurrence of that String. "Truncate" was probably a bad description of what I wanted to do... "pop until I hit an arbitrary index" is probably closer to what I need.
Consider using deque. Below is the link which explains why should you use it over stack.
Why should I use Deque over Stack?
Stack implements List (among various other interfaces). Get a ListIterator for the last element, and move it backwards until you find an occurrence of the new state, counting how many elements along the way, and then pop that many elements. (If you don't find the new state, then of course you don't pop anything, and you push the new state onto the stack instead).
This might not be particularly efficient, but it will certainly work. If you also want it to be efficient, you probably need to use another data structure (either instead of or as well as the stack). One possibility is to use a Map (in addition to the stack) to keep track of which states are on the stack together with the index at which they occur, or at least a Set to keep track of which states are on the stack (you can then just pop states until you find the one you are looking for). You would maintain the stack and the map or set in parallel.
Or if you were serious about:
"pop until I hit an arbitrary index" is probably closer to what I need.
... then surely this would suffice:
int numberToPop = stack.size() - arbitraryIndex - 1;
while (numberToPop-- > 0) {
stack.pop();
}
Would it simplify your overall code if you created your own container that is basically a set that's a stack? Something like (not a complete implementation):
public class StackSet<T> {
private final Set<T> set;
private final Deque<T> queue;
public StackSet(){
set = new HashSet<>();
queue = new ArrayDeque<>();
}
public void push(T value){
if(set.add(value)){
queue.push(value);
}
}
public T pop(){
return queue.pop();
}
}
That should guarantee no duplicates.
Here's an example of a truncate method with Deque instead of Stack.
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Collection;
import java.util.Deque;
public class ExampleDeque
{
public static void main(String[] args)
{
Deque<String> deque = new ArrayDeque<String>();
deque.offer("startState");
deque.offer("state1");
deque.offer("state2");
deque.offer("state3");
deque.offer("state2");
System.out.println("Before");
print(deque);
deque = truncate(deque, "state2");
System.out.println("After");
print(deque);
}
static Deque<String> truncate (Deque<String> deque, String value)
{
if(!deque.contains(value)) return deque;
String[] array = deque.toArray(new String[deque.size()]);
for(int i = 0; i < array.length; i++)
{
if(array[i] == value)
{
String[] truncated = Arrays.copyOfRange(array, 0, i + 1);
Collection<String> collection = Arrays.asList(truncated);
return new ArrayDeque<>(collection);
}
}
return null;
}
static void print(Deque<String> deque)
{
for(String s : deque)
System.out.println(s);
System.out.println();
}
}
I'm currently learning linked list and I have discovered the basics of coding it and I fully understand them. However, I have a certain amount of nodes preset, so the user would not be able to add more. How would one implement a while loop to keep cycling through and asking the user if they want to add another piece of data.
Here is the code that I already have so far:
public class List {
public int x;
public List ptr = null;
}
Above is the object class for List. List contains a data type of x and a pointer.
import javax.swing.JOptionPane;
public class Main {
public static void main(String[] args) {
List front = new List();
front.x = Integer.parseInt(JOptionPane.showInputDialog("Enter a value"));
List l1 = new List();
l1.x = Integer.parseInt(JOptionPane.showInputDialog("Enter a value"));
List l2 = new List();
l2.x = Integer.parseInt(JOptionPane.showInputDialog("Enter a value"));
front.ptr = l1;
l1.ptr = l2;
printNodes(front);
}
public static void printNodes(List p) {
while (p != null) {
System.out.print(p.x + " ");
p = p.ptr;
}
}
}
As you can see, I have 3 Nodes created, but you cannot add anymore. I'd like to have something along the lines of:
boolean goAgain = true;
while (goAgain) {
//create a new node
String again = JOptionPane.showInputDialog("Add another node?");
if (!again.equalsIgnoreCase("yes")) {
goAgain = false;
}
}
Thank you!
P.S - I am a sophomore in high school, please use vocabulary that I will be able to understand. I wouldn't say I'm a java noob, but I'm no expert either.
Well I'd clean up your while loop to include everything in one statement. But that's just because I'm lazy. ;)
while (!JOptionPane.showInputDialog("Add another node?").equalsIgnoreCase("yes"))
{
//make a new node
}
As for the code to make a new node I would suggest implementing your List class from the List interface that Java has. You can read about it here. If you're just starting out in Java though it might be a little hard to understand. As a comment on your existing code here:
List front = new List();
front.x = Integer.parseInt(JOptionPane.showInputDialog("Enter a value"));
You're not exactly making a new node per-say, you're just creating new instances of your List class. In this case the object is labeled 'front' My suggestion is to read up more on how a List is meant to function. Here is a good example.
I am working on a project for school that requires us to find the shortest path between two points. Basically I use a breadth first search to traverse the graph and then use a map to keep track of each cities predecessor. My idea is then that when I reach the end I will then use the edges map to find out how a city was gotten to and essentially work backwards. However when I attempt to pull values from the map all I get is null, even though when I print out the contents it shows that there is something there. If somebody could help me track down the problem I would appreciate it.
Contents of input file with each city and its neighbor:
basic
Bismark Fargo
Minneapolis Chicago
StPaul Chicago
Minneapolis StPaul
Minneapolis Fargo
Fargo GrandForks
The code (corrected version, so this code won't exhibit the described problem any more):
import java.util.*;
import java.io.*;
public class BFSBasics {
public static void main(String[] args) throws FileNotFoundException {
Map<String, List<String>> graph = new HashMap<>();
openFile(graph, args[0]);
String start = args[1];
String end = args[2];
BFS(graph, start, end);
}
public static void openFile(Map<String,List<String>> graph,
String file)
throws FileNotFoundException{
Map<String,List<String>> aGraph = new HashMap<>();
try (Scanner scan = new Scanner(new File(file))){
if(!scan.next().equals("basic")){
System.err.println("File cannot be read.");
System.exit(1);
}else{
while(scan.hasNext()){
String city1 = scan.next();
String city2 = scan.next();
addEdge(graph, city1, city2);
addEdge(graph, city2, city1);
}
}
}
}
private static void addEdge(Map<String, List<String>> graph, String city1,
String city2){
List<String> adjacent = graph.get(city1);
if(adjacent == null){
adjacent = new ArrayList<>();
graph.put(city1, adjacent);
}
adjacent.add(city2);
}
public static void BFS(Map<String, List<String>> graph, String start,
String end) {
boolean done = false;
//cities that still need to be worked on
Queue<String> work = new ArrayDeque<>();
//cities that have already been seen
Set<String> seen = new HashSet<>();
//cities predecessor i.e. how it was gotten to
Map<String, String> edges = new HashMap<>();
LinkedList<String> path = new LinkedList<>();
String city = start;
work.add(start);
while (!done && !work.isEmpty()) {
city = work.remove();
for (String s : graph.get(city)) {
if (!seen.contains(s)) {
edges.put(s, city);
work.add(s);
seen.add(s);
if (s.equals(end)) {
done = true;
}
}
}
}
//Work backwards through the edges map and push onto the path stack
path.push(end);
String temp = edges.get(end);
while(!temp.equals(start)){
path.push(temp);
temp = edges.get(path.peek()};
}
path.push(start);
//print out the path
while(!path.isEmpty()){
System.out.println(path.pop());
}
}
}
There is something wrong with your path building code:
path.push(end); // push node (n - 1)
String temp = edges.get(end); // temp = node (n - 2)
while(!temp.equals(start)){
path.push(edges.get(temp)); // push node (n - 3) down to and including node 0
temp = path.peek(); // temp = node (n - 3) down to and including node 0
}
path.push(start); // push node 0
So the node (n - 2) will never be pushed to the path, whereas the node 0 will be pushed twice.
But except for this, the program works for me. So perheaps you really have an unreachable target, as Hbcdev suggests. You should check whether or not you actually reached the end node. Note that your graph datra structure models a directed graph, so if you want to interpret your input as undirected edges, you'll have to insert two directed edges for each line of input.
Also note that you don't mark the initial node as seen, whereas all other nodes will get marked as seen when you add them to the queue. You should mark the first as well.
Edit:
After you pasted your (almost) complete code, I fixed it in the following ways:
added two wildcard imports, for java.util.* and java.io.*. Wildcard imports are quick and dirty.
Added a closing } at the very end to close the class definition.
Added a line with the word basic to your input data. You really should System.exit(1) in case of that keyword missing, instead of continuing with inconsistent state.
With those modifications, I tested all possible combinations of two cities, always in both orders, and including paths form a city to itself. No evidence of null values anywhere, neither in output nor as a cause of printed exceptions.
I see a couple of possible problems here. The immediate cause may be this: Your logic implicitly assumes that there is only one way to reach any given node. While this may be true, I doubt it. If there are two ways to reach the same node, you overwrite the first with the second in your map.
For example, suppose the input was:
A->B
C->B
B->E
D->E
You want to get from A to E. It could be done by going A, B, E. But when you build your map, you'll create an edge entry for B, A. Then you'll write B, C, overwriting B, A. Then you write E, B. Fine. Then you write E, D, overwriting E, B. So when you're done, all that's in the edge map is (B, C) and (E, D). You then try to walk backwards from E. You find E, D. But this is wrong: you wanted E, B, but that got overwritten. When you try to find an entry for D, there is none, so it's impossible to get back to A.
A second problem is that you said that the goal was to find the SHORTEST path from start to end. But you're doing nothing to find the shortest path: you stop looking once you find any path. You really need to, in principle, find all possible paths and then select the shortest from that list. (Or hopefully, eliminate longer paths as you go, one way or another.)