Need help to find the least amount of semesters - java

My homework problem presents some courses and how many depend on each other. For an instance, the first test (courses,depedent on): (1,3) (2,3) (4,1) (4,2) and we identify that there are 5 courses and 4 dependent on each other (Thats why 5 is not on the list, its just 0)
I know from a topological search, that the following is a valid solution:
1 3 2 4 0
I then need to print the number of semesters it takes to take these courses and I know it is 3 semester, due to the relations between them. We first have to take course 1 and 2 to take 3 and since we already have 1 2, we can take course 4.
So I need help figuring some code out that does this. That's where I need you guys help
I've tried to simply count the courses that are connected, but failed. I've tried to think of something that I can do but literally nothing pops up as a solution.
The graph class:
public class Graph {
int V;
LinkedList<Integer> adjList[];
public Graph(int vertex) {
this.V = vertex;
//We then define the num of vertexes in the adjlist
adjList = new LinkedList[vertex];
//Then create a new list for each vertex so we can create a link between the vertexes
for (int i = 0; i < vertex; i++) {
adjList[i] = new LinkedList<>();
}
}
//Directed graph
public void addEdge(int source, int destination) {
//Add the edge from the source node to the destination node
adjList[source].add(destination);
adjList[destination].add(source);
}
//Method to print the graph if necessary
public void printGraph(Graph graph) {
for (int i = 0; i < graph.V; i++) {
System.out.println("Adjacency list over vertex: " + i);
System.out.print("head");
for (Integer treeCrawler : graph.adjList[i]) {
System.out.print("-> " + treeCrawler);
}
System.out.println("\n");
}
}
public LinkedList<Integer>[] getAdjList() {
return adjList;
}
}
and the topological sort class, the algorithm we are using for the problem
public class TopologicalSort {
int vertex;
//This function helps the topological function recursively by marking the vertices and pushing them onto the stack
public void topologicalHelper(int vertex, boolean marked[], Stack nodes, Graph graph) {
List<Integer>[] list = graph.getAdjList();
marked[vertex] = true;
Iterator<Integer> iterator = list[vertex].iterator();
while (iterator.hasNext()) {
int temp = iterator.next();
if (!marked[temp] && list[temp].size() != 0) {
topologicalHelper(temp, marked, nodes, graph);
}
}
nodes.add(vertex);
}
public TopologicalSort(Graph graph, int vertecies) {
vertex = vertecies;
Stack nodes = new Stack();
boolean[] marked = new boolean[vertex];
for (int i = 0; i < vertex; i++) {
if (marked[i] == false) {
topologicalHelper(i, marked, nodes, graph);
}
}
while(!nodes.empty()) {
System.out.print(nodes.pop() + " ");
}
}
}
The result should be 3, but I haven't produced that number in all my solution ideas, I need some help or hints.
Oh and the following is the console output
Adjacency list over vertex: 0
head
Adjacency list over vertex: 1
head-> 3-> 4
Adjacency list over vertex: 2
head-> 3-> 4
Adjacency list over vertex: 3
head-> 1-> 2
Adjacency list over vertex: 4
head-> 1-> 2
1 3 2 4 0

Dependency is a directed property so you should be using a directed graph. After populating the graph u will end up with a disconnected graph which has one or more trees in it. Find out which nodes are roots of each tree and use DFS to get the max depth of each tree. Assuming there is no limit on no of courses for each semester the max depth of all trees is the solution.
public class Graph {
int V;
ArrayList<Integer> adjList[];
boolean[] notRoot;
public Graph(int vertex) {
this.V = vertex;
adjList = new ArrayList[vertex];
notRoot = new boolean[vertex];
for (int i = 0; i < vertex; i++) {
adjList[i] = new ArrayList<Integer>();
}
}
public void addEdge(int a, int b) {
//asuming b is dependent on a
adjList[b].add(a);
notRoot[a]=true;
}
int maxDepthDfs(int root){
int depth=1;
for(int i=0;i<adjList[root].size();i++){
int child=adjList[root].get(i);
depth=Math.max(maxDepthDfs(child)+1,depth);
}
return depth;
}
public int getSolution(){
int ans=0;
for(int i=0;i<V;i++){
if(!notRoot[i])
ans=Math.max(ans,maxDepthDfs(i));
}
return ans;
}
}
A topological sort is simply DFS with adding nodes into a stack,(all children of a node are added first and then root is added). In Kahn's algorithm first the root elements(nodes without parent) are found and the method is called only or those nodes.
int maxDepthDfs(int root){
//since we are only calling this function for root nodes we need not check if nodes were previously visited
int depth=1;
for(int i=0;i<adjList[root].size();i++){
int child=adjList[root].get(i);
depth=Math.max(maxDepthDfs(child)+1,depth);
}
s.push(root);
return depth;
}
public int getSolution(){
s=new Stack<Integer>();
int ans=0;
for(int i=0;i<V;i++){
if(!notRoot[i])
ans=Math.max(ans,maxDepthDfs(i));
}
//stack s contains result of topological sort;
return ans;
}

Related

MEX from root node to every other node in a tree

Given a rooted tree having N nodes. Root node is node 1. Each ith node has some value , val[i] associated with it.
For each node i (1<=i<=N) we want to know MEX of the path values from root node to node i.
MEX of an array is smallest positive integer not present in the array, for instance MEX of {1,2,4} is 3
Example : Say we are given tree with 4 nodes. Value of nodes are [1,3,2,8] and we also have parent of each node i (other than node 1 as it is the root node). Parent array is defined as [1,2,2] for this example. It means parent of node 2 is node 1, parent of node 3 is node 2 and parent of node 4 is also node 2.
Node 1 : MEX(1) = 2
Node 2 : MEX(1,3) = 2
Node 3 : MEX(1,3,2) = 4
Node 4 : MEX(1,3,8) = 2
Hence answer is [2,2,4,2]
In worst case total number of Nodes can be upto 10^6 and value of each node can go upto 10^9.
Attempt :
Approach 1 : As we know MEX of N elements will be always be between 1 to N+1. I was trying to use this understanding with this tree problem, but then in this case N will keep on changing dynamically as one proceed towards leaf nodes.
Approach 2 : Another thought was to create an array with N+1 empty values and then try to fill them as we go along from root node. But then challenge I faced was on to keep track of first non filled value in this array.
public class TestClass {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter wr = new PrintWriter(System.out);
int T = Integer.parseInt(br.readLine().trim());
for(int t_i = 0; t_i < T; t_i++)
{
int N = Integer.parseInt(br.readLine().trim());
String[] arr_val = br.readLine().split(" ");
int[] val = new int[N];
for(int i_val = 0; i_val < arr_val.length; i_val++)
{
val[i_val] = Integer.parseInt(arr_val[i_val]);
}
String[] arr_parent = br.readLine().split(" ");
int[] parent = new int[N-1];
for(int i_parent = 0; i_parent < arr_parent.length; i_parent++)
{
parent[i_parent] = Integer.parseInt(arr_parent[i_parent]);
}
int[] out_ = solve(N, val, parent);
System.out.print(out_[0]);
for(int i_out_ = 1; i_out_ < out_.length; i_out_++)
{
System.out.print(" " + out_[i_out_]);
}
System.out.println();
}
wr.close();
br.close();
}
static int[] solve(int N, int[] val, int[] parent){
// Write your code here
int[] result = new int[val.length];
ArrayList<ArrayList<Integer>> temp = new ArrayList<>();
ArrayList<Integer> curr = new ArrayList<>();
if(val[0]==1)
curr.add(2);
else{
curr.add(1);
curr.add(val[0]);
}
result[0]=curr.get(0);
temp.add(new ArrayList<>(curr));
for(int i=1;i<val.length;i++){
int parentIndex = parent[i-1]-1;
curr = new ArrayList<>(temp.get(parentIndex));
int nodeValue = val[i];
boolean enter = false;
while(curr.size()>0 && nodeValue == curr.get(0)){
curr.remove(0);
nodeValue++;
enter=true;
}
if(curr.isEmpty())
curr.add(nodeValue);
else if(!curr.isEmpty() && curr.contains(nodeValue) ==false && (enter|| curr.get(0)<nodeValue))
curr.add(nodeValue);
Collections.sort(curr);
temp.add(new ArrayList<>(curr));
result[i]=curr.get(0);
}
return result;
}
}
This can be done in time O(n log n) using augmented BSTs.
Imagine you have a data structure that supports the following operations:
insert(x), which adds a copy of the number x.
remove(x), which removes a copy of the number x.
mex(), which returns the MEX of the collection.
With something like this available, you can easily solve the problem by doing a recursive tree walk, inserting items when you start visiting a node and removing those items when you leave a node. That will make n calls to each of these functions, so the goal will be to minimize their costs.
We can do this using augmented BSTs. For now, imagine that all the numbers in the original tree are distinct; we’ll address the case when there are duplicates later. Start off with Your BST of Choice and augment it by having each node store the number of nodes in its left subtree. This can be done without changing the asymptotic cost of an insertion or deletion (if you haven’t seen this before, check out the order statistic tree data structure). You can then find the MEX as follows. Starting at the root, look at its value and the number of nodes in its left subtree. One of the following will happen:
The node’s value k is exactly one plus the number of nodes in the left subtree. That means that all the values 1, 2, 3, …, k are in the tree, so the MEX will be the smallest value missing from the right subtree. Recursively find the MEX of the right subtree. As you do, remember that you’ve already seen the values from 1 to k by subtracting k off of all the values you find there as you encounter them.
The node’s value k is at least two more than the number of nodes in the left subtree. That means that the there’s a gap somewhere in the node’s in the left subtree plus the root. Recursively find the MEX of the left subtree.
Once you step off the tree, you can look at the last node where you went right and add one to it to get the MEX. (If you never went right, the MEX is 1).
This is a top-down pass on a balanced tree that does O(1) work per node, so it takes a total of O(log n) work.
The only complication is what happens if a value in the original tree (not the augmented BST) is duplicated on a path. But that’s easy to fix: just add a count field to each BST node tracking how many times it’s there, incrementing it when an insert happens and decrementing it when a remove happens. Then, only remove the node from the BST in the case where the frequency drops to zero.
Overall, each operation on such a tree takes time O(log n), so this gives an O(n log n)-time algorithm for your original problem.
public class PathMex {
static void dfs(int node, int mexVal, int[] res, int[] values, ArrayList<ArrayList<Integer>> adj, HashMap<Integer, Integer> map) {
if (!map.containsKey(values[node])) {
map.put(values[node], 1);
}
else {
map.put(values[node], map.get(values[node]) + 1);
}
while(map.containsKey(mexVal)) mexVal++;
res[node] = mexVal;
ArrayList<Integer> children = adj.get(node);
for (Integer child : children) {
dfs(child, mexVal, res, values, adj, map);
}
if (map.containsKey(values[node])) {
if (map.get(values[node]) == 1) {
map.remove(values[node]);
}
else {
map.put(values[node], map.get(values[node]) - 1);
}
}
}
static int[] findPathMex(int nodes, int[] values, int[] parent) {
ArrayList<ArrayList<Integer>> adj = new ArrayList<>(nodes);
HashMap<Integer, Integer> map = new HashMap<>();
int[] res = new int[nodes];
for (int i = 0; i < nodes; i++) {
adj.add(new ArrayList<Integer>());
}
for (int i = 0; i < nodes - 1; i++) {
adj.get(parent[i] - 1).add(i + 1);
}
dfs(0, 1, res, values, adj, map);
return res;
}
public static void main(String args[]) {
Scanner sc = new Scanner(System.in);
int nodes = sc.nextInt();
int[] values = new int[nodes];
int[] parent = new int[nodes - 1];
for (int i = 0; i < nodes; i++) {
values[i] = sc.nextInt();
}
for (int i = 0; i < nodes - 1; i++) {
parent[i] = sc.nextInt();
}
int[] res = findPathMex(nodes, values, parent);
for (int i = 0; i < nodes; i++) {
System.out.print(res[i] + " ");
}
}
}

Input and Output help in topological sorting

public class Problem3 {
public static void main (String [] args){
Scanner sc= new Scanner (System.in);
System.out.println("Enter no. of Islands");
int n= sc.nextInt();
Graph g = new Graph (n);
System.out.println("Enter no. of one-way bridges");
int m= sc.nextInt();
System.out.println("Enter no. of island you want to be intially on");
int r= sc.nextInt();
try{ for (int i=0; i<m;i++){
System.out.println("This one-way bridge connects between");
int u = sc.nextInt();
int v = sc.nextInt();
if(u == v || u>n || v>n){ throw new Bounds("");}
else{ g.addEgde(u-1, v-1);}
}
g.topoSort();}
catch(IndexOutOfBoundsException e){
System.out.println("Please enter a valid input!");
}
catch(Bounds e){
System.out.println("Please enter a valid input!");
}
}
public static class Bounds extends Exception{
public Bounds (String message){
super(message);
}}
static class Graph {
int V;
LinkedList<Integer>[] adjList;
Graph(int V) {
this.V = V;
adjList = new LinkedList[V];
for (int i = 0; i < V; i++) {
adjList[i] = new LinkedList<>();
}
}
public void addEgde(int u, int v) {
adjList[u].addFirst(v);
}
public void topoSort() {
boolean[] visited = new boolean[V];
stack stack = new stack();
for (int i = 0; i < V; i++) {
if (!visited[i]) {
topoSortRE(i, visited, stack);
}
}
System.out.println("Topological Sort: ");
int size = stack.size();
for (int i = 0; i <size ; i++) {
System.out.print(stack.pop()+ 1 + " ");
}
}
public void topoSortRE(int s, boolean[] visited, stack stack) {
visited[s] = true;
for (int i = 0; i < adjList[s].size(); i++) {
int vertex = adjList[s].get(i);
if (!visited[vertex])
topoSortRE(vertex, visited, stack);
}
stack.push(s);
}}}
The following code is an attempt to solve the following problem:
There are many islands that are connected by one-way bridges, that is, if a bridge connects
islands a and b, then you can only use the bridge to go from a to b but you cannot travel back
by using the same. If you are on island a, then you select (uniformly and randomly) one of
the islands that are directly reachable from a through the one-way bridge and move to that
island. You are stuck on an island if you cannot move any further. It is guaranteed that if
there is a directed path from one island to the second there is no path that leads from the
second back to the first. In other words the formed graph is a Directed Acyclic Graph.
Find the island that you are most likely to get stuck on; that is the island that you can
possibly reach with the maximum number of paths from all other islands.
Input format:
First line: Three integers n (the number of islands), m (the number of one-way bridges), and r
(the index of the island you are initially on)
Next m lines: Two integers ui and vi representing a one-way bridge from island ui to vi.
Output format:
Print the index of the island that you are most likely to get stuck on. If there are multiple
islands, then print them in the increasing order of indices (space separated values in a single
line).
Sample input
5, 7, 1
(1, 2)
(1, 3)
(1, 4)
(1, 5)
(2, 4)
(2, 5)
(3, 4)
Sample output
4
I wrote the code to topologically sort the graph but I am having issues with how to make the input r the intial island and also how to make the output be the most probable island to be stuck on. I know that the island I'm most likely to be stuck on is the island that has the most indegrees and no outdegrees but don't know how to implement that.
For each node make a value (real number) representing probability that you will reach it from your starting island. At first, set this value for initial node to 1 and 0 for other nodes.
During the topological sort, when you're in node v, add its probability value divided by number of neighbors to each neighbor's value (in other words, since you know that the probability of getting to v is v.value, then the probability of reaching its neighbor should be increased by v.value * ppb of choosing this neighbor, that is 1 / #_of_neighbors). In this way, whenever you're in some node during topological sort, its value will be equal to the total probability of reaching it.
Your answer is an ending island (node with outdegree 0) with largest value.
Your topological sort seems wrong, you're doing something that looks like DFS. In topological sort you want to visit each vertex after visiting all vertices with an edge ending in it.
About implementation, I changed your DFS into topological sort and added those probabilities I've talked about. I left the part about choosing best ending vertex to you, I don't think doing all the work for someone is educational in any way. Also, I do not guarantee that my changes below do not contain any spelling mistakes, etc. I did my best, but I have not run this code.
static class Graph {
int V;
LinkedList<Integer>[] adjList;
Graph(int V) {
this.V = V;
adjList = new LinkedList[V];
probability = new double[V];
for (int i = 0; i < V; i++) {
adjList[i] = new LinkedList<>();
}
}
public void addEgde(int u, int v) {
adjList[u].addFirst(v);
}
public void topoSort(int start) {
double[] probability;
probability[start] = 1;
int[] indegree = new int[V];
stack stack = new stack();
for (int i = 0; i < V; i++) {
probability[i] = 0;
for (int j = 0; j < adjList[i].size(); ++j) {
indegree[adjList[i][j]] += 1;
}
}
probability[start] = 1;
for(int i = 0; i < V; ++i)
{
if(indegree[i] == 0)
stack.push(i);
}
while(stack.size())
{
int v = stack.pop();
for (int i = 0; i < adjList[v].size(); ++i)
{
indegree[adjList[v][i]] -= 1;
probability[adjList[v][i]] += probability[v] / (double)(adjList[v].size());
if(indegree[adjList[v][i]] == 0)
stack.push(adjList[v][i]);
}
}
//probability array now contains all probabilities to visit each node
}
}
}

BFS Queue is empty before solution is found

The goal of this BFS is to find a solution to a 3x2 puzzle game(0 is blank space and you can only move pieces to that space)
start:
1 4 2
5 3 0
Goal:
0 1 2
3 4 5
The problem is that my queue becomes empty before a solution is found, how is that possible? One of the paths in the search tree must return a solution here. Please let me know if I can clarify anything.
Node Class (represents a state of the game):
mport java.lang.reflect.Array;
import java.util.*;
public class Node {
public int[] state = new int[6];
private Node parent;
public Node(int[] initialState, Node parent){
this.parent = parent;
this.state = initialState;
}
public boolean isGoal(){
int[] goal = {0,1,2,3,4,5};
return Arrays.equals(state, goal);
}
public ArrayList<Node> getChildren(){
ArrayList<Node> children = new ArrayList<>();
Integer[] newInt = new Integer[getState().length];
for (int i = 0; i < getState().length; i++) {
newInt[i] = Integer.valueOf(getState()[i]);
}
int position = Arrays.asList(newInt).indexOf(0);
switch(position){
case 0:
children.add(right());
children.add(down());
break;
case 1:
children.add(down());
children.add(left());
children.add(right());
break;
case 2:
children.add(down());
children.add(left());
break;
case 3:
children.add(up());
children.add(right());
break;
case 4:
children.add(up());
children.add(left());
children.add(right());
break;
case 5:
children.add(up());
children.add(left());
break;
}
return children;
}
public int[] getState(){
return this.state;
}
public int getBlankIndex() {
for (int i = 0; i < state.length; i++)
if (state[i] == 0) return i;
return -1;
}
public Node up(){
int[] newer = state.clone();
int blankIndex = getBlankIndex();
int temp = newer[blankIndex - 3];
newer[blankIndex] = temp;
newer[blankIndex - 3] = 0;
return new Node(newer, this);
}
public Node down(){
int[] newer = state.clone();
int blankIndex = getBlankIndex();
int temp = newer[blankIndex + 3];
newer[blankIndex] = temp;
newer[blankIndex + 3] = 0;
return new Node(newer, this);
}
public Node left(){
int[] newer = state.clone();
int blankIndex = getBlankIndex();
int temp = newer[blankIndex - 1];
newer[blankIndex] = temp;
newer[blankIndex - 1] = 0;
return new Node(newer, this);
}
public Node right(){
int[] newer = state.clone();
int blankIndex = getBlankIndex();
int temp = newer[blankIndex + 1];
newer[blankIndex] = temp;
newer[blankIndex + 1] = 0;
return new Node(newer, this);
}
public void print(){
System.out.println("---------");
System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 0, 3)));
System.out.println(Arrays.toString(Arrays.copyOfRange(getState(), 3, 6)));
System.out.println("---------");
}
public void printTrace(){
Stack<Node> stack = new Stack<>();
Node current = this;
while (current.parent != null){
stack.push(current);
current = current.parent;
}
while (!stack.isEmpty()){
stack.pop().print();
}
}
#Override
public boolean equals(Object object){
if (object instanceof Node) {
Node node2 = (Node) object;
return (Arrays.equals(node2.getState(), this.getState()));
}
return false;
}
#Override
public int hashCode() {
return this.hashCode();
}
}
Driver Class:
import java.util.*;
public class Driver {
public static void main(String[] args){
Node test = new Node(new int[]{1, 4, 2, 5, 3, 0}, null);
BFS(test);
System.out.println("done");
}
public static void BFS(Node initial){
Queue<Node> queue = new LinkedList<>();
ArrayList<Node> explored = new ArrayList<>();
queue.add(initial);
Node current = initial;
while (!queue.isEmpty() && !current.isGoal()){
current = queue.remove();
for (Node child: current.getChildren()){
if (!explored.contains(child)) {
queue.add(child);
explored.add(current);
}
}
}
System.out.println("DONEDONEDONE");
current.printTrace();
}
}
This is a very surprising problem!
I haven't looked at the code yet, it seemed more or less ok.
I'll instead address the question:
The problem is that my queue becomes empty before a solution is found, how is that possible?
The code is not the problem.
The problem is that your puzzle is unsolvable.
The funny thing is that
parity(permutation) * (-1)^{manhattanMetric(positionOfZeroTile)}
is an invariant that is preserved during the entire game.
Let me briefly explain what it means.
(It's essentially the same argument as here: https://en.wikipedia.org/wiki/15_puzzle )
The parity of a permutation is (-1)^{numberOfTranspositions}.
The number of transpositions is essentially just the number of swaps that
the bubble-sort would need to sort the sequence.
The manhattan metric of the zero-tile position is x-coordinate of the zero-tile
added with the y-coordinate of the zero-tile.
Each time you swap a tile with zero, the parity of the permutation changes
the sign.
At the same time, the manhattan metric between the upper left corner and
the position of the zero-tile changes by +1 or -1. In both cases,
(-1)^{manhattanDist} also changes the sign.
Thus, the product of the parity and (-1)^{manhattanDist} is constant.
If you now look at the solved game
0 1 2
3 4 5
then the number of transpositions is 0, parity is 1, the manhattan distance is 0.
Thus, the invariant is (+1).
However, if you look at this:
1 4 2
5 3 0
then you can calculate that the number of transpositions is even (bubble-sort it!),
parity is (+1), and the manhattan distance is 2 + 1 = 3, and thus uneven.
Thus, the invariant is (+1) * (-1)^3 = (-1).
But (-1) is not (+1). Therefore, your game is unsolvable in principle, no matter
how good your BFS is.
Another (more intuitive but less rigorous) way to see quickly that your puzzle is "broken"
is to swap two non-zero tiles in the beginning.
1 4 2
3 5
This is almost immediately solvable:
1 4 2 1 2 1 2
3 5 3 4 5 3 4 5
So, if you don't want to waste any time searching for bugs that aren't there,
don't skip the Group Theory lectures next time ;)
An error I can find is that you only add current to the explored list if it doesn‘t contain child. Additionally, you do this within the loop through the children, so it might also be that you add it multiple times. (Though, this shouldn‘t affect your result)

Updating Heap in both direction (Prim's Algorithm)

In Prim's algorithm, it is recommended to maintain the invariant in the following way :
When a vertice v is added to the MST:
For each edge (v,w) in the unexplored tree:
1. Delete w from the min heap.
2. Recompute the key[w] (i.e. it's value from the unexplored tree
to the explored one).
3. Add the value back to the heap.
So, basically this involves deletion from the heap (and heapify which takes O(logn)) and then reinserting (again O(logn))
Instead, if I use the following approach:
For each edge (v,w) in the unexplored tree:
1. Get the position of the node in the heap(array) using HashMap -> O(1)
2. Update the value in place.
3. Bubble up or bubble down accordingly. -> O(logn)
Which gives better constants than the previous one.
The controversial part is the 3rd part where Im supposed to bubble up or down.
My implementation is as follows :
public int heapifyAt(int index){
// Bubble up
if(heap[index].edgeCost < heap[(int)Math.floor(index/2)].edgeCost){
while(heap[index].edgeCost < heap[(int)Math.floor(index/2)].edgeCost){
swap(index, (int)Math.floor(index/2));
index = (int)Math.floor(index/2);
}
}else{
// Bubble down
while(index*2 + 2 < size && (heap[index].edgeCost > heap[index*2 + 1].edgeCost|| heap[index].edgeCost > heap[index*2 + 2].edgeCost)){
if(heap[index*2 + 1].edgeCost < heap[index*2 + 2].edgeCost){
//swap with left child
swap(index, index*2 + 1);
index = index*2 + 1;
}else{
//swap with right child
swap(index, index*2 + 2);
index = index*2 + 2;
}
}
}
return index;
}
And I'am plucking from the heap this way :
public AdjNode pluck(){
AdjNode min = heap[0];
int minNodeNumber = heap[0].nodeNumber;
AdjNode toRet = new AdjNode(min.nodeNumber, min.edgeCost);
heap[0].edgeCost = INF; // set this to infinity, so it'll be at the bottom
// of the heap.
heapifyat(0);
visited.add(minNodeNumber);
updatevertices(minNodeNumber); // Update the adjacent vertices
return toRet;
}
And updating the plucked vertices this way :
public void updatevertices(int pluckedNode){
for(AdjNode adjacentNode : g.list[pluckedNode]){
if(!visited.contains(adjacentNode.nodeNumber)){ // Skip the nodes that are already visited
int positionInHeap = map.get(adjacentNode.nodeNumber); // Retrive the position from HashMap
if(adjacentNode.edgeCost < heap[positionInHeap].edgeCost){
heap[positionInHeap].edgeCost = adjacentNode.edgeCost; // Update if the cost is better
heapifyAt(positionInHeap); // Now this will go bottom or up, depending on the value
}
}
}
}
But when I execute it on large graph, the code fails, There are small values in the bottom of heap and large values at the top. But the heapifyAt() API seems to work fine. So I am unable to figure out is my approach wrong or my code?
Moreover, if I replace the heapifyAt() API by siftDown(), i.e. construct the heap, it works fine, but it doesnt make sense calling siftDown() that takes O(n) time for every updates which can be processed in logarithmic time.
In short : Is it possible to update the values in Heap both way, or the algorithm is wrong, since that's why it is recommended to first remove the element from Heap and reinsert it.
EDIT : Complete code:
public class Graph1{
public static final int INF = 9999999;
public static final int NEGINF = -9999999;
static class AdjNode{
int nodeNumber;
int edgeCost;
AdjNode next;
AdjNode(int nodeNumber, int edgeCost){
this.nodeNumber = nodeNumber;
this.edgeCost = edgeCost;
}
}
static class AdjList implements Iterable<AdjNode>{
AdjNode head;
AdjList(){
}
public void add(int to, int cost){
if(head==null){
head = new AdjNode(to, cost);
}else{
AdjNode temp = head;
while(temp.next!=null){
temp = temp.next;
}
temp.next = new AdjNode(to, cost);
}
}
public Iterator<AdjNode> iterator(){
return new Iterator<AdjNode>(){
AdjNode temp = head;
public boolean hasNext(){
if(head==null){
return false;
}
return temp != null;
}
public AdjNode next(){
AdjNode ttemp = temp;
temp = temp.next;
return ttemp;
}
public void remove(){
throw new UnsupportedOperationException();
}
};
}
public void printList(){
AdjNode temp = head;
if(head==null){
System.out.println("List Empty");
return;
}
while(temp.next!=null){
System.out.print(temp.nodeNumber + "|" + temp.edgeCost + "-> ");
temp = temp.next;
}
System.out.println(temp.nodeNumber + "|" + temp.edgeCost);
}
}
static class Heap{
int size;
AdjNode[] heap;
Graph g;
int pluckSize;
Set<Integer> visited = new HashSet<Integer>();
HashMap<Integer, Integer> map = new HashMap<>();
Heap(){
}
Heap(Graph g){
this.g = g;
this.size = g.numberOfVertices;
this.pluckSize = size - 1;
heap = new AdjNode[size];
copyElements();
constructHeap();
}
public void copyElements(){
AdjList first = g.list[0];
int k = 0;
heap[k++] = new AdjNode(0, NEGINF); //First entry
for(AdjNode nodes : first){
heap[nodes.nodeNumber] = nodes;
}
for(int i=0; i<size; i++){
if(heap[i]==null){
heap[i] = new AdjNode(i, INF);
}
}
}
public void printHashMap(){
System.out.println("Priniting HashMap");
for(int i=0; i<size; i++){
System.out.println(i + " Pos in heap :" + map.get(i));
}
line();
}
public void line(){
System.out.println("*******************************************");
}
public void printHeap(){
System.out.println("Printing Heap");
for(int i=0; i<size; i++){
System.out.println(heap[i].nodeNumber + " | " + heap[i].edgeCost);
}
line();
}
public void initializeMap(){
for(int i=0; i<size; i++){
map.put(heap[i].nodeNumber, i);
}
}
public void swap(int one, int two){
AdjNode first = heap[one];
AdjNode second = heap[two];
map.put(first.nodeNumber, two);
map.put(second.nodeNumber, one);
AdjNode temp = heap[one];
heap[one] = heap[two];
heap[two] = temp;
}
public void constructHeap(){
for(int i=size-1; i>=0; i--){
int temp = i;
while(heap[temp].edgeCost < heap[(int)Math.floor(temp/2)].edgeCost){
swap(temp, (int)Math.floor(temp/2));
temp = (int)Math.floor(temp/2);
}
}
initializeMap();
}
public void updatevertices(int pluckedNode){
for(AdjNode adjacentNode : g.list[pluckedNode]){
if(!visited.contains(adjacentNode.nodeNumber)){
int positionInHeap = map.get(adjacentNode.nodeNumber);
if(adjacentNode.edgeCost < heap[positionInHeap].edgeCost){
// //System.out.println(adjacentNode.nodeNumber + " not visited, Updating vertice " + heap[positionInHeap].nodeNumber + " from " + heap[positionInHeap].edgeCost + " to " + adjacentNode.edgeCost);
// heap[positionInHeap].edgeCost = INF;
// //heap[positionInHeap].edgeCost = adjacentNode.edgeCost;
// int heapifiedIndex = heapifyAt(positionInHeap); // This code follows my logic
// heap[heapifiedIndex].edgeCost = adjacentNode.edgeCost; // (which doesnt work)
// //heapifyAt(size - 1);
heap[positionInHeap].edgeCost = adjacentNode.edgeCost;
//heapifyAt(positionInHeap);
constructHeap(); // When replaced by SiftDown,
} // works as charm
}
}
}
public void printSet(){
Iterator<Integer> it = visited.iterator();
System.out.print("Printing set : [");
while(it.hasNext()){
System.out.print((int)it.next() + ", ");
}
System.out.println("]");
}
public AdjNode pluck(){
AdjNode min = heap[0];
int minNodeNumber = heap[0].nodeNumber;
AdjNode toRet = new AdjNode(min.nodeNumber, min.edgeCost);
heap[0].edgeCost = INF;
constructHeap();
visited.add(minNodeNumber);
updatevertices(minNodeNumber);
return toRet;
}
public int heapifyAt(int index){
if(heap[index].edgeCost < heap[(int)Math.floor(index/2)].edgeCost){
while(heap[index].edgeCost < heap[(int)Math.floor(index/2)].edgeCost){
swap(index, (int)Math.floor(index/2));
index = (int)Math.floor(index/2);
}
}else{
if(index*2 + 2 < size){
while(index*2 + 2 < size && (heap[index].edgeCost > heap[index*2 + 1].edgeCost|| heap[index].edgeCost > heap[index*2 + 2].edgeCost)){
if(heap[index*2 + 1].edgeCost < heap[index*2 + 2].edgeCost){
//swap with left child
swap(index, index*2 + 1);
index = index*2 + 1;
}else{
//swap with right child
swap(index, index*2 + 2);
index = index*2 + 2;
}
}
}
}
return index;
}
}
static class Graph{
int numberOfVertices;
AdjList[] list;
Graph(int numberOfVertices){
list = new AdjList[numberOfVertices];
for(int i=0; i<numberOfVertices; i++){
list[i] = new AdjList();
}
this.numberOfVertices = numberOfVertices;
}
public void addEdge(int from, int to, int cost){
this.list[from].add(to, cost);
this.list[to].add(from, cost);
}
public void printGraph(){
System.out.println("Printing Graph");
for(int i=0; i<numberOfVertices; i++){
System.out.print(i + " = ");
list[i].printList();
}
}
}
public static void prims(Graph graph, Heap heap){
int totalMin = INF;
int tempSize = graph.numberOfVertices;
while(tempSize>0){
AdjNode min = heap.pluck();
totalMin += min.edgeCost;
System.out.println("Added cost : " + min.edgeCost);
tempSize--;
}
System.out.println("Total min : " + totalMin);
}
public static void main(String[] args) throws Throwable {
Scanner in = new Scanner(new File("/home/mayur/Downloads/PrimsInput.txt"));
Graph graph = new Graph(in.nextInt());
in.nextInt();
while(in.hasNext()){
graph.addEdge(in.nextInt() - 1, in.nextInt() - 1, in.nextInt());
}
Heap heap = new Heap(graph);
prims(graph, heap);
}
}
With a proper implementation of heap, you should be able to bubble up and down. Heap preserves a group of elements using an order that applies to both directions and bubbling up and down are essentially the same, apart from the direction in which you move.
As to your implementation, I believe you are correct but one, seemingly minor issue: indexing.
If you look around for array implementations of heap, you will notice that in most cases the root is located at index 1, instead of 0. The reason for that being, in a 1-indexed array you preserve the following relation between parent p and children c1 and c2.
heap[i] = p
heap[2 * i] = c1
heap[2 * i + 1] = c2
It is trivial to draw an array on a piece of paper and see that this relation holds, if you have the root at heap[1]. The children of root, at index 1, are located at indices 2 and 3. Children of the node at index 2 are at indices 4 & 5, while children of the node at index 3 are at indices 6 & 7, and so on.
This relation helps you get to the children or the parent of any node at i, without having to keep track of where they are. (i.e. parent is at floor(i/2) and children are at 2i and 2i+1)
What you seem to have tried is a 0-indexed implementation of heap. Consequently you had to use a slightly different relation given below for parent p and children c1 and c2.
heap[i] = p
heap[2 * i + 1] = c1
heap[2 * i + 2] = c2
This seems to be ok when accessing the children. For example, the children of root, at index 0, are located at indices 1 and 2. Children of the node at index 1 are at indices 3 & 4, while children of the node at index 2 are at indices 5 & 6, and so on. However there is a pickle when accessing the parent of a node. If you consider node 3 and take floor(3/2), you do get index 1, which is the parent of 1. However, if you take the node at index 4, floor(4/2) gives you index 2, which is not the parent of the node at index 4.
Obviously, this adaptation of the index relationship between a parent and its children does not work for both children. Unlike the 1-indexed heap implementation, you can not treat both children the same while accessing their parents. Therefore, the problem lies specifically in your bubbling up part, without necessarily being related to bubbling up operation. As a matter of fact, though I haven't tested your code, the bubbling up portion of heapifyAt function seems to be correct.(i.e. except the indexing, of course)
Now, you may keep using a 0-indexed heap and adapt your code so that whenever you are looking for a node's parent, you implicitly check whether it is the right (i.e. not as in correct but as in the opposite of left) child of that parent and use floor((i-1)/2) if it is. Checking whether a node is the right child is trivial: just look if it is even or not. (i.e. as you index right children with 2i + 2, they will always be even)
However I recommend you take a different approach and instead use a 1-indexed array implementation of heap. The elegance of the array implementation of heap is that you can treat each node the same and you don't have to do anything different based on its index or location, with the root of the heap perhaps being the only possible exception to this.

Prim's MST algorithm implementation with Java

I'm trying to write a program that'll find the MST of a given undirected weighted graph with Kruskal's and Prim's algorithms. I've successfully implemented Kruskal's algorithm in the program, but I'm having trouble with Prim's. To be more precise, I can't figure out how to actually build the Prim function so that it'll iterate through all the vertices in the graph. I'm getting some IndexOutOfBoundsException errors during program execution. I'm not sure how much information is needed for others to get the idea of what I have done so far, but hopefully there won't be too much useless information.
This is what I have so far:
I have a Graph, Edge and a Vertex class.
Vertex class mostly just an information storage that contains the name (number) of the vertex.
Edge class can create a new Edge that has gets parameters (Vertex start, Vertex end, int edgeWeight). The class has methods to return the usual info like start vertex, end vertex and the weight.
Graph class reads data from a text file and adds new Edges to an ArrayList. The text file also tells us how many vertecis the graph has, and that gets stored too.
In the Graph class, I have a Prim() -method that's supposed to calculate the MST:
public ArrayList<Edge> Prim(Graph G) {
ArrayList<Edge> edges = G.graph; // Copies the ArrayList with all edges in it.
ArrayList<Edge> MST = new ArrayList<Edge>();
Random rnd = new Random();
Vertex startingVertex = edges.get(rnd.nextInt(G.returnVertexCount())).returnStartingVertex(); // This is just to randomize the starting vertex.
// This is supposed to be the main loop to find the MST, but this is probably horribly wrong..
while (MST.size() < returnVertexCount()) {
Edge e = findClosestNeighbour(startingVertex);
MST.add(e);
visited.add(e.returnStartingVertex());
visited.add(e.returnEndingVertex());
edges.remove(e);
}
return MST;
}
The method findClosesNeighbour() looks like this:
public Edge findClosestNeighbour(Vertex v) {
ArrayList<Edge> neighbours = new ArrayList<Edge>();
ArrayList<Edge> edges = graph;
for (int i = 0; i < edges.size() -1; ++i) {
if (edges.get(i).endPoint() == s.returnVertexID() && !visited(edges.get(i).returnEndingVertex())) {
neighbours.add(edges.get(i));
}
}
return neighbours.get(0); // This is the minimum weight edge in the list.
}
ArrayList<Vertex> visited and ArrayList<Edges> graph get constructed when creating a new graph.
Visited() -method is simply a boolean check to see if ArrayList visited contains the Vertex we're thinking about moving to. I tested the findClosestNeighbour() independantly and it seemed to be working but if someone finds something wrong with it then that feedback is welcome also.
Mainly though as I mentioned my problem is with actually building the main loop in the Prim() -method, and if there's any additional info needed I'm happy to provide it.
Thank you.
Edit: To clarify what my train of thought with the Prim() method is. What I want to do is first randomize the starting point in the graph. After that, I will find the closest neighbor to that starting point. Then we'll add the edge connecting those two points to the MST, and also add the vertices to the visited list for checking later, so that we won't form any loops in the graph.
Here's the error that gets thrown:
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.rangeCheck(Unknown Source)
at java.util.ArrayList.get(Unknown Source)
at Graph.findClosestNeighbour(graph.java:203)
at Graph.Prim(graph.java:179)
at MST.main(MST.java:49)
Line 203: return neighbour.get(0); in findClosestNeighbour()
Line 179: Edge e = findClosestNeighbour(startingVertex); in Prim()
Vertex startingVertex = edges.get(rnd.nextInt(G.returnVertexCount())).returnStartingVertex();
This uses the vertex count to index an edge list, mixing up vertices and edges.
// This is supposed to be the main loop to find the MST, but this is probably horribly wrong..
while (MST.size() < returnVertexCount()) {
Edge e = findClosestNeighbour(startingVertex);
MST.add(e);
visited.add(e.returnStartingVertex());
visited.add(e.returnEndingVertex());
edges.remove(e);
}
This shouldn't be passing the same startingVertex to findClosestNeighbour each time.
public Edge findClosestNeighbour(Vertex v) {
ArrayList<Edge> neighbours = new ArrayList<Edge>();
ArrayList<Edge> edges = graph;
for (int i = 0; i < edges.size() -1; ++i) {
if (edges.get(i).endPoint() == s.returnVertexID() && !visited(edges.get(i).returnEndingVertex())) {
neighbours.add(edges.get(i));
}
}
return neighbours.get(0); // This is the minimum weight edge in the list.
}
What is s here? This doesn't look like it's taking the edge weights into account. It's skipping the last edge, and it's only checking the ending vertex, when the edges are non-directional.
// Simple weighted graph representation
// Uses an Adjacency Linked Lists, suitable for sparse graphs /*undirected
9
A
B
C
D
E
F
G
H
I
A B 1
B C 2
C E 7
E G 1
G H 8
F H 3
F D 4
D E 5
I F 9
I A 3
A D 1
This is the graph i used saved as graph.txt
*/
import java.io.*;
import java.util.Scanner;
class Heap
{
private int[] h; // heap array
private int[] hPos; // hPos[h[k]] == k
private int[] dist; // dist[v] = priority of v
private int MAX;
private int N; // heap size
// The heap constructor gets passed from the Graph:
// 1. maximum heap size
// 2. reference to the dist[] array
// 3. reference to the hPos[] array
public Heap(int maxSize, int[] _dist, int[] _hPos)
{
N = 0;
MAX = maxSize;
h = new int[maxSize + 1];
dist = _dist;
hPos = _hPos;
}
public boolean isEmpty()
{
return N == 0;
}
public void siftUp( int k)
{
int v = h[k];
h[0] = 0;
dist[0] = Integer.MIN_VALUE;
//vertex using dist moved up heap
while(dist[v] < dist[h[k/2]]){
h[k] = h[k/2]; //parent vertex is assigned pos of child vertex
hPos[h[k]] = k;//hpos modified for siftup
k = k/2;// index of child assigned last parent to continue siftup
}
h[k] = v;//resting pos of vertex assigned to heap
hPos[v] = k;//index of resting pos of vertex updated in hpos
//display hpos array
/* System.out.println("\nThe following is the hpos array after siftup: \n");
for(int i = 0; i < MAX; i ++){
System.out.println("%d", hPos[i]);
}
System.out.println("\n Following is heap array after siftup: \n");
for (int i = 0; i < MAX; i ++ ){
System.out.println("%d" , h[i]);
}*/
}
//removing the vertex at top of heap
//passed the index of the smallest value in heap
//siftdown resizes and resorts heap
public void siftDown( int k)
{
int v, j;
v = h[k];
while(k <= N/2){
j = 2 * k;
if(j < N && dist[h[j]] > dist[h[j + 1]]) ++j; //if node is > left increment j child
if(dist[v] <= dist[h[j]]) break;//if sizeof parent vertex is less than child stop.
h[k] = h[j];//if parent is greater than child then child assigned parent pos
hPos[h[k]] = k;//update new pos of last child
k = j;//assign vertex new pos
}
h[k] = v;//assign rest place of vertex to heap
hPos[v] = k;//update pos of the vertex in hpos array
}
public void insert( int x)
{
h[++N] = x;//assign new vertex to end of heap
siftUp( N);//pass index at end of heap to siftup
}
public int remove()
{
int v = h[1];
hPos[v] = 0; // v is no longer in heap
h[N+1] = 0; // put null node into empty spot
h[1] = h[N--];//last node of heap moved to top
siftDown(1);//pass index at top to siftdown
return v;//return vertex at top of heap
}
}
class Graph {
class Node {
public int vert;
public int wgt;
public Node next;
}
// V = number of vertices
// E = number of edges
// adj[] is the adjacency lists array
private int V, E;
private Node[] adj;
private Node z;
private int[] mst;
// used for traversing graph
private int[] visited;
private int id;
// default constructor
public Graph(String graphFile) throws IOException
{
int u, v;
int e, wgt;
Node t;
FileReader fr = new FileReader(graphFile);
BufferedReader reader = new BufferedReader(fr);
String splits = " +"; // multiple whitespace as delimiter
String line = reader.readLine();
String[] parts = line.split(splits);
System.out.println("Parts[] = " + parts[0] + " " + parts[1]);
V = Integer.parseInt(parts[0]);
E = Integer.parseInt(parts[1]);
// create sentinel node
z = new Node();
z.next = z;
// create adjacency lists, initialised to sentinel node z
adj = new Node[V+1];
for(v = 1; v <= V; ++v)
adj[v] = z;
// read the edges
System.out.println("Reading edges from text file");
for(e = 1; e <= E; ++e)
{
line = reader.readLine();
parts = line.split(splits);
u = Integer.parseInt(parts[0]);
v = Integer.parseInt(parts[1]);
wgt = Integer.parseInt(parts[2]);
System.out.println("Edge " + toChar(u) + "--(" + wgt + ")--" + toChar(v));
// write code to put edge into adjacency matrix
t = new Node(); t.vert = v; t.wgt = wgt; t.next = adj[u]; adj[u] = t;
t = new Node(); t.vert = u; t.wgt = wgt; t.next = adj[v]; adj[v] = t;
}
}
// convert vertex into char for pretty printing
private char toChar(int u)
{
return (char)(u + 64);
}
// method to display the graph representation
public void display() {
int v;
Node n;
for(v=1; v<=V; ++v){
System.out.print("\nadj[" + toChar(v) + "] ->" );
for(n = adj[v]; n != z; n = n.next)
System.out.print(" |" + toChar(n.vert) + " | " + n.wgt + "| ->");
}
System.out.println("");
}
//use the breath first approach to add verts from the adj list to heap
//uses 3 arrays where array = # of verts in graph
//parent array to keep track of parent verts
// a dist matrix to keep track of dist between it and parent
//hpos array to track pos of vert in the heap
public void MST_Prim(int s)
{
int v, u;
int wgt, wgt_sum = 0;
int[] dist, parent, hPos;
Node t;
//declare 3 arrays
dist = new int[V + 1];
parent = new int[V + 1];
hPos = new int[V +1];
//initialise arrays
for(v = 0; v <= V; ++v){
dist[v] = Integer.MAX_VALUE;
parent[v] = 0;
hPos[v] = 0;
}
dist[s] = 0;
//d.dequeue is pq.remove
Heap pq = new Heap(V, dist, hPos);
pq.insert(s);
while (! pq.isEmpty())
{
// most of alg here
v = pq.remove();
wgt_sum += dist[v];//add the dist/wgt of vert removed to mean spanning tree
//System.out.println("\nAdding to MST edge {0} -- ({1}) -- {2}", toChar(parent[v]), dist[v], toChar[v]);
dist[v] = -dist[v];//mark it as done by making it negative
for(t = adj[v]; t != z; t = t.next){
u = t.vert;
wgt = t.wgt;
if(wgt < dist[u]){ //weight less than current value
dist[u] = wgt;
parent[u] = v;
if(hPos[u] == 0)// not in heap insert
pq.insert(u);
else
pq.siftUp(hPos[u]);//if already in heap siftup the modified heap node
}
}
}
System.out.print("\n\nWeight of MST = " + wgt_sum + "\n");
//display hPos array
/*System.out.println("\nhPos array after siftUp: \n");
for(int i = 0; i < V; i ++){
System.out.println("%d", hPos[i]);
}*/
mst = parent;
}
public void showMST()
{
System.out.print("\n\nMinimum Spanning tree parent array is:\n");
for(int v = 1; v <= V; ++v)
System.out.println(toChar(v) + " -> " + toChar(mst[v]));
System.out.println("");
}
}
public class PrimLists {
public static void main(String[] args) throws IOException
{
int s = 2;
String fname = "graph.txt";
Graph g = new Graph(fname);
g.display();
}
}

Categories

Resources