I am running breadth first search on the above graph to find the shortest path from Node 0 to Node 6.
My code
public List<Integer> shortestPathBFS(int startNode, int nodeToBeFound){
boolean shortestPathFound = false;
Queue<Integer> queue = new LinkedList<Integer>();
Set<Integer> visitedNodes = new HashSet<Integer>();
List<Integer> shortestPath = new ArrayList<Integer>();
queue.add(startNode);
shortestPath.add(startNode);
while (!queue.isEmpty()) {
int nextNode = queue.peek();
shortestPathFound = (nextNode == nodeToBeFound) ? true : false;
if(shortestPathFound)break;
visitedNodes.add(nextNode);
System.out.println(queue);
Integer unvisitedNode = this.getUnvisitedNode(nextNode, visitedNodes);
if (unvisitedNode != null) {
queue.add(unvisitedNode);
visitedNodes.add(unvisitedNode);
shortestPath.add(nextNode); //Adding the previous node of the visited node
shortestPathFound = (unvisitedNode == nodeToBeFound) ? true : false;
if(shortestPathFound)break;
} else {
queue.poll();
}
}
return shortestPath;
}
I need to track down the nodes through which the BFS algo. traversed to reach node 6, like [0,3,2,5,6]. For that I have created a List named shortestPath & trying to store the previous nodes of the visited nodes, to get the list of nodes. Referred
But it doesn't seem to work. The shortest path is [0,3,2,5,6]
In the list what I get is Shortest path: [0, 0, 0, 0, 1, 3, 3, 2, 5]
It's partially correct but gives the extra 1 .
If I again start from the first element 0 of the shortestPath list & start traversing & backtracking. Like 1 doesn't has an edge to 3, so I backtrack & move from 0 to 3 to 5, I will get the answer but not sure if that's the correct way.
What is the ideal way to getting the nodes for the shortest path?
Storing all the visited nodes in a single list is not helpful for finding the shortest path because in the end you have no way of knowing which nodes were the ones that led to the target node, and which ones were dead ends.
What you need to do is for every node to store the previous node in the path from the starting node.
So, create a map Map<Integer, Integer> parentNodes, and instead of this:
shortestPath.add(nextNode);
do this:
parentNodes.put(unvisitedNode, nextNode);
After you reach the target node, you can traverse that map to find the path back to the starting node:
if(shortestPathFound) {
List<Integer> shortestPath = new ArrayList<>();
Integer node = nodeToBeFound;
while(node != null) {
shortestPath.add(node)
node = parentNodes.get(node);
}
Collections.reverse(shortestPath);
}
As you can see in acheron55 answer:
"It has the extremely useful property that if all of the edges in a graph are unweighted (or the same weight) then the first time a node is visited is the shortest path to that node from the source node"
So all you have to do, is to keep track of the path through which the target has been reached.
A simple way to do it, is to push into the Queue the whole path used to reach a node, rather than the node itself.
The benefit of doing so is that when the target has been reached the queue holds the path used to reach it.
Here is a simple implementation :
/**
* unlike common bfs implementation queue does not hold a nodes, but rather collections
* of nodes. each collection represents the path through which a certain node has
* been reached, the node being the last element in that collection
*/
private Queue<List<Node>> queue;
//a collection of visited nodes
private Set<Node> visited;
public boolean bfs(Node node) {
if(node == null){ return false; }
queue = new LinkedList<>(); //initialize queue
visited = new HashSet<>(); //initialize visited log
//a collection to hold the path through which a node has been reached
//the node it self is the last element in that collection
List<Node> pathToNode = new ArrayList<>();
pathToNode.add(node);
queue.add(pathToNode);
while (! queue.isEmpty()) {
pathToNode = queue.poll();
//get node (last element) from queue
node = pathToNode.get(pathToNode.size()-1);
if(isSolved(node)) {
//print path
System.out.println(pathToNode);
return true;
}
//loop over neighbors
for(Node nextNode : getNeighbors(node)){
if(! isVisited(nextNode)) {
//create a new collection representing the path to nextNode
List<Node> pathToNextNode = new ArrayList<>(pathToNode);
pathToNextNode.add(nextNode);
queue.add(pathToNextNode); //add collection to the queue
}
}
}
return false;
}
private List<Node> getNeighbors(Node node) {/* TODO implement*/ return null;}
private boolean isSolved(Node node) {/* TODO implement*/ return false;}
private boolean isVisited(Node node) {
if(visited.contains(node)) { return true;}
visited.add(node);
return false;
}
This is also applicable to cyclic graphs, where a node can have more than one parent.
In addition to the already given answer by user3290797.
It looks like You are dealing with an unweighted graph. We interpret this as every edge has a weight of 1. In this case, once You have associated a distance to the root node with every node of the graph (the breadth-first traversal), it becomes trivial to reconstruct the shortest path from any node, and even detect if there are multiple ones.
All You need to do is a breadth- (in case You want every shortest path) or depth-first traversal of the same graph starting from the target node and only considering neighbours with a depth's value of exactly 1 less.
So we need to jump from distance 4 (node 6) to 3, 2, 1, 0, and there is only one way (in this case) to do so.
In case we are interested in the shortest path to node 4 the result would be distances 2-1-0 or nodes 4-3-0 or 4-8-0.
BTW, this approach can easily be modified to work with weighted graphs (with non-negative weights) too: valid neighbours are those with distance equals to current minus the weight of the edge -- this involves some actual calculations and directly storing previous nodes along the shortest path might be better.
Related
I implemented public BinarySearchTree<Node,T> chop(T x)
that chops my tree into two parts at element x. The SSet this will contain elements < x, and the returned SSet is a SSet that contains elements >= x. This should work for all elements regardless of whether they are in this.
For example, suppose s={2,4,6,8}. Then s.chop(3) returns {4,6,8} and s becomes {2}. We would get the same result for s.chop(4).
The slowChop method is implemented, but it has a time complexity of O(n), but I need to reduce it to at least O(h) when the tree is balanced (where h is the height of the tree).
public BinarySearchTree<Node,T> slowChop(T x) {
Node sample = super.newNode();
BinarySearchTree<Node,T> other = new
BinarySearchTree<Node, T>(sample);
// Iterate through the n nodes in-order.
// When see value >=x, add to new BST in O(height) time, and
// remove it from this BST (on next iteration) in O(height) time.
Iterator<T> it = iterator();
T prev = null;
while( it.hasNext() ) {
T curr = (T)(it.next());
if( c.compare(curr, x) >= 0 ) { // we have our first >= x
other.add(curr);
if( prev != null ) {
this.remove(prev); // safe to remove now
}
prev = curr;
}
}
if( prev != null ) {
this.remove(prev); // edge case, get that last one!
}
return other;
}
public BinarySearchTree<Node,T> chop(T x) {
Node sample = super.newNode();
BinarySearchTree<Node,T> other = new
BinarySearchTree<Node, T>(sample);
// TODO: Implement this method. It should match slowChop in
// behaviour, but should be faster :-)
return other;
}
Indeed, your algorithm is not making use of the efficiency that you can get from the fact you are dealing with a binary search tree. So iterating through the tree with an in-order traversal is not the way to go.
Instead, perform a binary search and cut the edges that link two nodes which should end up in different trees. While cutting you'll also need to reattach nodes to where a previous cut was performed. The complexity is the same as a binary search towards the bottom of the tree, and so it is O(logn).
Here is an implementation that assumes you have the regular getters and setters:
on the Node class: getLeft, setLeft, getRight, setRight, getValue, and
on the BinarySearchTree class: getRoot, setRoot
public BinarySearchTree<Node,T> chop(T x) {
// Create two temporary dummy (sentinel) nodes to ease the process.
Node rightRootParent = super.newNode();
Node leftRootParent = super.newNode();
// Set "cursors" at both sides
Node rightParent = rightRootParent;
Node leftParent = leftRootParent;
// Start the binary search for x, cutting edges as we walk down
Node node = this.getRoot();
while (node != null) {
// Decide for each node in the binary search path at which side it should go
if (c.compare(node.getValue(), x) >= 0) {
// Node should belong to the right-side tree
rightParent.setLeft(node); // Establish edge
rightParent = node;
node = node.getLeft(); // Move down
rightParent.setLeft(null); // Cut next edge for now (it might get restored)
} else { // Symmetric case
leftParent.setRight(node);
leftParent = node;
node = node.getRight();
leftParent.setRight(null);
}
}
// Set the roots of both trees
this.setRoot(leftRootParent.getRight());
return BinarySearchTree<Node, T>(rightRootParent.getLeft());
}
I have got a graph, and I would like to find a path between two nodes (number 3 and 5).
I read about finding paths in graph, and I tried to write DFS and BFS. Both are implemented and works well. However, I would like to get a list of each node visited directly from 3 to 5.
Both algorithms work as they supposed to so, when running bsf I will visit nodes in such order: 2,6,1,4,5.
Using dfs 2,1,4,5.
But what I would like to do achieve is 6,5 (in first case) and 2,4,5 in second.
In other words, I want to save only nodes that are on the way from 3 to 5 (Not all visited during dfs/bfs), as a List of nodes.
I have been racking my brain for a long time, how to change my code to achieve it, or maybe should i change my approach? I should should store nodes in the correct path, or use different algorithm? I simply do not have idea how to do it.
My bfs
public List<Node> bfs(Node root, Node nodeWeSearchFor)
{ Queue<Node> queue = new LinkedList<Node>();
List<Node> route = new ArrayList<Node>();
if(root == null || nodeWeSearchFor == null) return route;
//State is just an enum Visited or unvisited
root.state = State.Visited;
//Adds to end of queue
queue.add(root);
while(!queue.isEmpty())
{
//removes from front of queue
Node r = queue.remove();
//Visit child first before grandchild
for(Node n: r.getConnectedNodes())
{
if(n.state == State.Unvisited)
{
queue.add(n);
route.add(n);
n.state = State.Visited;
//If we found node, return
if(n==nodeWeSearchFor){
return route;
}
}
}
}
return route;}
My dfs:
public List<Node> dfs(Node root, Node nodeWeSearchFor)
{
List<Node> route = new ArrayList<Node>();
//Avoid infinite loops
if(root == null) return route;
System.out.print(root.toString() + "\t");
root.state = State.Visited;
route.add(root);
if(root == nodeWeSearchFor) return route;
//for every child
for(Node n: root.getConnectedNodes())
{
//if childs state is not visited then recurse
if(n.state == State.Unvisited)
{
//recursive call for dfs (We are passing route)
dfs(n,nodeWeSearchFor,route);
}
}
return route;
}
public List<Node> dfs(Node root, Node nodeWeSearchFor,List<Node> _route)
{
List<Node> route = _route;
//Avoid infinite loops
if(root == null) return route;
System.out.print(root.toString() + "\t");
root.state = State.Visited;
route.add(root);
if(root == nodeWeSearchFor) return route;
//for every child
for(Node n: root.getConnectedNodes())
{
//if childs state is not visited then recurse
if(n.state == State.Unvisited)
{
dfs(n,nodeWeSearchFor,route);
}
}
return route;
}
It is quite easy, in DFS, when you reach the "end" (you cannot go forward), you have to "go back". So when you are going "back", you just remove that node at the "dead end" from your list of visited nodes.
In BFS, you have to create new list for each node visited, copy the already visited nodes of node that "opens him" and then add itself to that list.
so I have a list of basic nodes, for example nodes A B C.
each component can see what it is attached to for example:
a->b
b->c
c->a
I want a way that I can get a list of all the nodes in the graph. However, I'm running into trouble as my current system can't detect if it has already reached a point. EG in the above example it will go a->b->c->a->b etc. How can I detect this or how can I solve this problem.
My current "solution" getList() in the Node class:
ArrayList<Node> tempList = new ArrayList<Node>();
tempList.add(this);
for(int i = 0 ; i < nodesAttachedTo.size();i++){
tempList.addAll(nodesAttachedTo.get(i).getList());
}
return tempList;
You can use a HashSet. It will not allow one element to be added twice.
Here's an example code that first creates the graph similar to your example, then starts at some point in the graph and goes through it.
import java.util.HashSet;
public class Node
{
private HashSet<Node> nextNodes = new HashSet<Node>();
public Node()
{
}
public void addNextNode(Node node)
{
nextNodes.add(node);
}
public static void main(String[] args)
{
// this builds the graph of connected nodes
Node a = new Node();
Node b = new Node();
Node c = new Node();
a.addNextNode(b);
b.addNextNode(c);
c.addNextNode(a);
//this is the set that will lsit all nodes:
HashSet<Node> allNodes = new HashSet<Node>();
// this goes through the graph
a.listAllNodes(allNodes);
System.out.println(allNodes);
}
private void listAllNodes (HashSet<Node> listOfNodes)
{
// try to put all next nodes of the node into the list:
for(Node n : nextNodes)
{
if (listOfNodes.add(n)) // the set returns true if it did in fact add it.
n.listAllNodes(listOfNodes); // recursion
}
}
}
This goes from one node to all the nodes this node knows. (say that really fast three times)
Until it hits a dead end (= a node it already visited)
I chose to use a HashSet in the Node itself to store all the nodes it knows.
This could also be an ArrayList or whatever. But as I don't think there should be a connection twice, a HashSet seems to be a good choice in this situation, too.
I'm not familiar with your notation, but you could use two pointers to solve your issue. Start with two pointers that point to the same node. Increment one pointer until it returns to the start. Some pseudocode is below.
ArrayList<Node> tempList = new ArrayList<Node>();
Node head = nodesAttachedTo.get(0); //get the head of the list
tempList.add(head);
Node runner = head;
runner = runner.next;
while (!runner.equals(head)) {
tempList.add(runner);
runner = runner.next;
}
A hashmap is probably the way to go here. It allows constant time access (some overhead required, but I'm assuming you want a solution that scales well) to any element in the map.
HashMap<String, String> specificSolution = new HashMap<String, String>();
specificSolution.put("a", "b");
specificSolution.put("b", "c");
specificSolution.put("c", "a");
// To get all nodes in the graph
Set<String> nodes = specificSolution.keySet();
I implemented with String here because you don't provide a definition for the Node Class in your question, but it can be easily swapped out.
There are many different ways to represent a graph and each has their own limitations/advantages. Maybe another might be more appropriate but we would need more information about the problem.
I have a graph which stores it's edges using a HashMap as follows :
HashMap<Integer,LinkedList<Node>> adj;
where Node is defined ;
class Node
{
int number;
int weight;
}
eg
0 : <1,55> -> <2,54> //node 0 is connected to node 1 with edge weight 55 and node 2 with edge weight 54
1 : <0,43> -> <2,44> //node 1 is connected to node 0 with edge weight 43 and node 2 with
edge weight 44
I need to get a list of edges in sorted order by weight and I have no clue how to go about it. I am trying to implement Kruskal's MST.
Is it possible to sort the graph I have defined? If not please suggest a better way of storing it.
Let's start by creating an Edge class:
class Edge implements Comparable<Edge> { // Important: must implement Comparable. More on this later
public Node first; // first connected node
public Node second; // second connected node
public int weight; // move edge weight to Edge class
#Override
public int compareTo(Edge e) {
if (weight < e.weight) {
return -1;
} else if (weight > e.weight) {
return 1;
} else {
return 0;
}
}
}
Because the weight variable is in the Edge class, it isn't needed in Node, so you can remove it:
class Node {
public int number;
// add more variables later is you need here
}
Now, for your program (if there isn't a requirement against it), I would define your list like this:
HashMap<Node, List<Edge>> adj; // use any list implementation you want
This will represent the graph like this inside your program (copied from your example):
Node 0: Edge(Node 0, Node 1, 55), Edge(Node 0, Node 2, 54)
Node 1: Edge(Node 1, Node 0, 43), Edge(Node 1, Node 2, 44)
To answer your question, lets find the edges sorted by edge weight:
ArrayList<Edge> sortedEdges = new ArrayList<Edge>();
for (List<Edge> connectedEdges : adj.values()) {
sortedEdges.addAll(connectedEdges);
}
Collections.sort(sortedEdges);
This simply takes all the Edges in adj and puts them all in one list, and then sorts them according to their weight (because we made Edge extend Comparable<Edge>). As per the Javadoc on Collections.sort(), the sort() method uses merge sort, which runs in O(nlog(n)) time:
Implementation note: This implementation is a stable, adaptive, iterative mergesort that requires far fewer than n lg(n) comparisons when the input array is partially sorted, while offering the performance of a traditional mergesort when the input array is randomly ordered.
Getting the list of all Edges by adj.values takes O(n) time (see this), so the total time complexity of getting the list of edges sorted by weight will be O(n) + O(nlog(n)) = O(nlog(n)).
So there you go. I hope this helped :)
If you have the freedom to change the way nodes are represented, I would like to suggest changing it. Currently the Node class really represents an edge (and a node is represented by Integer, i.e., keys to the adj variable.
For example, the following seems more natural:
Set<Node> nodes = new HashSet<>(); // The enclosing class keeps track of all nodes.
// Represents each node.
class Node {
int nodeId = /* ... */;
// The Node class keeps track of its neighbors, sorted by their weights.
SortedMap<Integer,Node> edges = new TreeMap<>(Collections.reverseOrder());
}
Then, whenever you need to do things in the descending order of the weight, you could do something like:
void method(Node node) {
Iterator<Integer> iter = node.edges.keySet().iterator(); // Iterates in descending order.
while(iter.hasNext()) {
int weight = iter.next();
Node neighbor = node.edges.get(weight);
doSomething( /* ... */ );
}
}
I'm trying build a method which returns the shortest path from one node to another in an unweighted graph. I considered the use of Dijkstra's but this seems a bit overkill since I only want one pair. Instead I have implemented a breadth-first search, but the trouble is that my returning list contains some of the nodes that I don't want - how can I modify my code to achieve my goal?
public List<Node> getDirections(Node start, Node finish){
List<Node> directions = new LinkedList<Node>();
Queue<Node> q = new LinkedList<Node>();
Node current = start;
q.add(current);
while(!q.isEmpty()){
current = q.remove();
directions.add(current);
if (current.equals(finish)){
break;
}else{
for(Node node : current.getOutNodes()){
if(!q.contains(node)){
q.add(node);
}
}
}
}
if (!current.equals(finish)){
System.out.println("can't reach destination");
}
return directions;
}
Actually your code will not finish in cyclic graphs, consider graph 1 -> 2 -> 1. You must have some array where you can flag which node's you've visited already. And also for each node you can save previous nodes, from which you came. So here is correct code:
private Map<Node, Boolean>> vis = new HashMap<Node, Boolean>();
private Map<Node, Node> prev = new HashMap<Node, Node>();
public List getDirections(Node start, Node finish){
List directions = new LinkedList();
Queue q = new LinkedList();
Node current = start;
q.add(current);
vis.put(current, true);
while(!q.isEmpty()){
current = q.remove();
if (current.equals(finish)){
break;
}else{
for(Node node : current.getOutNodes()){
if(!vis.contains(node)){
q.add(node);
vis.put(node, true);
prev.put(node, current);
}
}
}
}
if (!current.equals(finish)){
System.out.println("can't reach destination");
}
for(Node node = finish; node != null; node = prev.get(node)) {
directions.add(node);
}
directions.reverse();
return directions;
}
Thank you Giolekva!
I rewrote it, refactoring some:
The collection of visited nodes doesn't have to be a map.
For path reconstruction, the next node could be looked up, instead of the previous node, eliminating the need for reversing the directions.
public List<Node> getDirections(Node sourceNode, Node destinationNode) {
//Initialization.
Map<Node, Node> nextNodeMap = new HashMap<Node, Node>();
Node currentNode = sourceNode;
//Queue
Queue<Node> queue = new LinkedList<Node>();
queue.add(currentNode);
/*
* The set of visited nodes doesn't have to be a Map, and, since order
* is not important, an ordered collection is not needed. HashSet is
* fast for add and lookup, if configured properly.
*/
Set<Node> visitedNodes = new HashSet<Node>();
visitedNodes.add(currentNode);
//Search.
while (!queue.isEmpty()) {
currentNode = queue.remove();
if (currentNode.equals(destinationNode)) {
break;
} else {
for (Node nextNode : getChildNodes(currentNode)) {
if (!visitedNodes.contains(nextNode)) {
queue.add(nextNode);
visitedNodes.add(nextNode);
//Look up of next node instead of previous.
nextNodeMap.put(currentNode, nextNode);
}
}
}
}
//If all nodes are explored and the destination node hasn't been found.
if (!currentNode.equals(destinationNode)) {
throw new RuntimeException("No feasible path.");
}
//Reconstruct path. No need to reverse.
List<Node> directions = new LinkedList<Node>();
for (Node node = sourceNode; node != null; node = nextNodeMap.get(node)) {
directions.add(node);
}
return directions;
}
You must include the parent node to each node when you put them on your queue. Then you can just recursively read the path from that list.
Say you want to find the shortest path from A to D in this Graph:
/B------C------D
/ |
A /
\ /
\E---------
Each time you enqueue a node, keep track of the way you got here.
So in step 1 B(A) E(A) is put on the queue. In step two B gets dequeued and C(B) is put on the queue etc. Its then easy to find your way back again, by just recursing "backwards".
Best way is probably to make an array as long as there are nodes and keep the links there, (which is whats usually done in ie. Dijkstra's).
Every time through your loop, you call
directions.Add(current);
Instead, you should move that to a place where you really know you want that entry.
It is really no simpler to get the answer for just one pair than for all the pairs. The usual way to calculate a shortest path is to start like you do, but make a note whenever you encounter a new node and record the previous node on the path. Then, when you reach the target node, you can follow the backlinks to the source and get the path. So, remove the directions.add(current) from the loop, and add code something like the following
Map<Node,Node> backlinks = new HashMap<Node,Node>();
in the beginning and then in the loop
if (!backlinks.containsKey(node)) {
backlinks.add(node, current);
q.add(node);
}
and then in the end, just construct the directions list in backwards using the backlinks map.