I am trying to code dijkstra's algorithm, starting at any vertex I need to show the distance and print the path of nodes. It works for vertex 2,4, and 5, but for 1 and 3 it gets messed up. It's probably something stupidly small, but I can't see it.
public static void main(String[] args)
{
int INF = Integer.MAX_VALUE;
int verticies = 5;
int W[][] = {{INF,7,4,6,1},
{0,INF,0,0,0},
{0,2,INF,4,0},
{0,0,0,INF,0},
{0,0,0,1,INF}};
int startNode = 1;
dijkstra(W,verticies,startNode-1);
}
public static void dijkstra(int G[][],int n,int startnode)
{
int INF = Integer.MAX_VALUE, nINF = Integer.MIN_VALUE;
//int cost[MAX][MAX],distance[MAX],pred[MAX];
//int visited[MAX],count,mindistance,nextnode,i,j;
int cost[][] = new int[n][n];
int distance[] = new int[n];
int pred[] = new int[n];
boolean visited[] = new boolean[n];
int count=0, mindistance=0, nextnode=0,i,j;
//pred[] stores the predecessor of each node
//count gives the number of nodes seen so far
//create the cost matrix
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(G[i][j]==0)
cost[i][j]=INF;
else
cost[i][j]=G[i][j];
//initialize pred[],distance[] and visited[]
for(i=0;i<n;i++)
{
distance[i]=cost[startnode][i];
pred[i]=startnode;
visited[i]=false;
}
distance[startnode]=0;
visited[startnode]=true;
count=1;
while(count<n-1)
{
mindistance=INF;
//nextnode gives the node at minimum distance
for(i=0;i<n;i++)
if(distance[i]<mindistance&&!visited[i])
{
mindistance=distance[i];
nextnode=i;
}
//check if a better path exists through nextnode
visited[nextnode]=true;
for(i=0;i<n;i++)
if(!visited[i])
if(mindistance+cost[nextnode][i]<distance[i])
{
distance[i]=mindistance+cost[nextnode][i];
pred[i]=nextnode;
}
count++;
}
//print the path and distance of each node
for(i=0;i<n;i++)
if(i!=startnode)
{
if(distance[i] == INF || distance[i] < 0){
System.out.print("\nNo edge exists between node "+(startnode+1)+" and node "+(i+1));
} else {
System.out.format("\nDistance of node %d = %d", (i + 1), distance[i]);
System.out.format("\nPath = %d", (i + 1));
j = i;
do {
j = pred[j];
System.out.format("<-%d", (j + 1));
} while (j != startnode);
}
}
}
I don’t know exactly how, but you are somehow getting INF into your calculations. My suspicion goes to the line distance[i]=mindistance+cost[nextnode][i];, but it may not be the only culprit, I have not checked. When mindistance is 1 (or greater) and cost is Integer.MAX_VALUE, you get an arithmetic overflow and the result gets negative. Further behaviour, I have not predicted, but it’s not as expected.
When in the two places you define INF I change the value to 1,000,000, I get the following output from your program:
Distance of node 2 = 6
Path = 2<-3<-1
Distance of node 3 = 4
Path = 3<-1
Distance of node 4 = 2
Path = 4<-5<-1
Distance of node 5 = 1
Path = 5<-1
I believe this is correct.
The way I found out? I stuck this statement into the middle of your outer while loop:
System.out.println("count " + count + " nextnode " + nextnode + " mindistance " + mindistance);
When it printed a large negative number, I started suspecting an arithmetic overflow. Until you learn to use a debugger, System.out.println() is your friend for debugging.
Related
I recently got interviewed and was asked the following question.
Given an n-ary tree, find the maximum path from root to leaf such that maximum path does not contain values from any two adjacent nodes.
(Another edit: The nodes would only have positive values.)
(Edit from comments: An adjacent node means node that share a direct edge. Because its a tree, it means parent-child. So if I include parent, I can not include child and vice versa.)
For example:
5
/ \
8 10
/ \ / \
1 3 7 9
In the above example, the maximum path without two adjacent would be 14 along the path 5->10->9. I include 5 and 9 in the final sum but not 10 because it would violate the no two adjacent nodes condition.
I suggested the following algorithm. While I was fairly sure about it, my interviewer did not seem confident about it. Hence, I wanted to double check if my algorithm was correct or not. It seemed to work on various test cases I could think of:
For each node X, let F(X) be the maximum sum from root to X without two adjacent values in the maximum sum.
The formula for calculating F(X) = Max(F(parent(X)), val(X) + F(grandParent(X)));
Solution would have been
Solution = Max(F(Leaf Nodes))
This was roughly the code I came up with:
class Node
{
int coins;
List<Node> edges;
public Node(int coins, List<Node> edges)
{
this.coins = coins;
this.edges = edges;
}
}
class Tree
{
int maxPath = Integer.MIN_VALUE;
private boolean isLeafNode(Node node)
{
int size = node.edges.size();
for(int i = 0; i < size; i++)
{
if(node.edges.get(i) != null)
return false;
}
return true;
}
// previous[0] = max value obtained from parent
// previous[1] = max value obtained from grandparent
private void helper(Node node, int[] previous)
{
int max = Math.max(previous[0], max.val + previous[1]);
//leaf node
if(isLeafNode(node))
{
maxPath = Math.max(maxPath, max);
return;
}
int[] temp= new int[2];
temp[0] = max;
temp[1] = prev[0];
for(int i = 0; i < node.edges.size(); i++)
{
if(node.edges.get(i) != null)
{
helper(node.edges.get(i), temp);
}
}
}
public int findMax(Node node)
{
int[] prev = new int[2];
prev[0] = 0;
prev[1] = 0;
if(node == null) return 0;
helper(node, prev);
return maxPath;
}
}
Edit: Forgot to mention that my primary purpose in asking this question is to know if my algorithm was correct rather than ask for a new algorithm.
Edit: I have a reason to believe that my algorithm should also have worked.
I was scouring the internet for similar questions and came across this question:
https://leetcode.com/problems/house-robber/?tab=Description
It is pretty similar to the problem above except that it is now an array instead of the tree.
The formal F(X) = Max(F(X-1), a[x] + F(X-2)) works in this case.
Here is my accepted code:
public class Solution {
public int rob(int[] nums) {
int[] dp = new int[nums.length];
if(nums.length < 1) return 0;
dp[0] = nums[0];
if(nums.length < 2) return nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for(int i = 2; i < nums.length; i++)
{
dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]);
}
return dp[nums.length-1];
}
}
The natural solution would be to compute for each node X two values: max path from X to leaf including X and max path from X to leaf, excluding X, let's call them MaxPath(X) and MaxExcluded(X).
For leaf L MaxPath(L) is Value(L) and MaxExcluded(L) is 0.
For internal node X:
MaxPath(X) = Value(X) + Max over child Y of: MaxExcluded(Y)
MaxExcluded(X) = Max over child Y of : Max(MaxExcluded(Y), MaxPath(Y))
The first line means that if you include X, you have to exclude its children. The second means that if you exclude X, you are free to either include or exclude its children.
It's a simple recursive function on nodes which can be computed going leaves-to-parents in O(size of the tree).
Edit: The recursive relation does also work top-down, and in this case you can indeed eliminate storing two values by the observation that MaxExcluded(Y) is actually MaxPath(Parent(Y)), which gives the solution given in the question.
Implementation of what #RafałDowgird explained.
/* 5
* 8 10
* 1 3 7 9
* 4 5 6 11 13 14 3 4
*
*
*/
public class app1 {
public static void main(String[] args) {
Node root = new Node(5);
root.left = new Node(8);root.right = new Node(10);
root.left.left = new Node(1);root.left.right = new Node(3);
root.right.left = new Node(7);root.right.right = new Node(9);
root.left.left.left = new Node(4);root.left.left.right = new Node(5);
root.left.right.left = new Node(6);root.left.right.right = new Node(11);
root.right.left.left = new Node(13);root.right.left.right = new Node(14);
root.right.right.right = new Node(4);
System.out.println(findMaxPath(root));
}
private static int findMaxPath(Node root) {
if (root == null) return 0;
int maxInclude = root.data + findMaxPathExcluded(root);
int maxExcludeLeft = Math.max(findMaxPath(root.left), findMaxPathExcluded(root.left));
int maxExcludeRight = Math.max(findMaxPath(root.right), findMaxPathExcluded(root.right));
return Math.max(maxInclude, Math.max(maxExcludeLeft, maxExcludeRight));
}
private static int findMaxPathExcluded(Node root) {
if(root == null) return 0;
int left1 = root.left!=null ? findMaxPath(root.left.left) : 0;
int right1 = root.left!=null ? findMaxPath(root.left.right) : 0;
int left2 = root.right!=null ? findMaxPath(root.right.left) : 0;
int right2 = root.right!=null ? findMaxPath(root.right.right) : 0;
return Math.max(left1, Math.max(right1, Math.max(left2, right2)));
}
}
class Node{
int data;
Node left;
Node right;
Node(int data){
this.data=data;
}
}
The Problem
Given a set of integers, find a subset of those integers which sum to 100,000,000.
Solution
I am attempting to build a tree containing all the combinations of the given set along with the sum. For example, if the given set looked like 0,1,2, I would build the following tree, checking the sum at each node:
{}
{} {0}
{} {1} {0} {0,1}
{} {2} {1} {1,2} {0} {2} {0,1} {0,1,2}
Since I keep both the array of integers at each node and the sum, I should only need the bottom (current) level of the tree in memory.
Issues
My current implementation will maintain the entire tree in memory and therefore uses way too much heap space.
How can I change my current implementation so that the GC will take care of my upper tree levels?
(At the moment I am just throwing a RuntimeException when I have found the target sum but this is obviously just for playing around)
public class RecursiveSolver {
static final int target = 100000000;
static final int[] set = new int[]{98374328, 234234123, 2341234, 123412344, etc...};
Tree initTree() {
return nextLevel(new Tree(null), 0);
}
Tree nextLevel(Tree currentLocation, int current) {
if (current == set.length) { return null; }
else if (currentLocation.sum == target) throw new RuntimeException(currentLocation.getText());
else {
currentLocation.left = nextLevel(currentLocation.copy(), current + 1);
Tree right = currentLocation.copy();
right.value = add(currentLocation.value, set[current]);
right.sum = currentLocation.sum + set[current];
currentLocation.right = nextLevel(right, current + 1);
return currentLocation;
}
}
int[] add(int[] array, int digit) {
if (array == null) {
return new int[]{digit};
}
int[] newValue = new int[array.length + 1];
for (int i = 0; i < array.length; i++) {
newValue[i] = array[i];
}
newValue[array.length] = digit;
return newValue;
}
public static void main(String[] args) {
RecursiveSolver rs = new RecursiveSolver();
Tree subsetTree = rs.initTree();
}
}
class Tree {
Tree left;
Tree right;
int[] value;
int sum;
Tree(int[] value) {
left = null;
right = null;
sum = 0;
this.value = value;
if (value != null) {
for (int i = 0; i < value.length; i++) sum += value[i];
}
}
Tree copy() {
return new Tree(this.value);
}
}
The time and space you need for building the tree here is absolutely nothing at all.
The reason is because, if you're given
A node of the tree
The depth of the node
The ordered array of input elements
you can simply compute its parent, left, and right children nodes using O(1) operations. And you have access to each of those things while you're traversing the tree, so you don't need anything else.
The problem is NP-complete.
If you really want to improve performance, then you have to forget about your tree implementation. You either have to just generate all the subsets and sum them up or to use dynamic programming.
The choice depends on the number of elements to sum and the sum you want to achieve. You know the sum it is 100,000,000, bruteforce exponential algorithm runs in O(2^n * n) time, so for number below 22 it makes sense.
In python you can achieve this with a simple:
def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
You can significantly improve this complexity (sacrificing the memory) by using meet in the middle technique (read the wiki article). This will decrease it to O(2^(n/2)), which means that it will perform better than DP solution for n <~ 53
After thinking more about erip's comments, I realized he is correct - I shouldn't be using a tree to implement this algorithm.
Brute force usually is O(n*2^n) because there are n additions for 2^n subsets. Because I only do one addition per node, the solution I came up with is O(2^n) where n is the size of the given set. Also, this algorithm is only O(n) space complexity. Since the number of elements in the original set in my particular problem is small (around 25) O(2^n) complexity is not too much of a problem.
The dynamic solution to this problem is O(t*n) where t is the target sum and n is the number of elements. Because t is very large in my problem, the dynamic solution ends up with a very long runtime and a high memory usage.
This completes my particular solution in around 311 ms on my machine, which is a tremendous improvement over the dynamic programming solutions I have seen for this particular class of problem.
public class TailRecursiveSolver {
public static void main(String[] args) {
final long starttime = System.currentTimeMillis();
try {
step(new Subset(null, 0), 0);
}
catch (RuntimeException ex) {
System.out.println(ex.getMessage());
final long endtime = System.currentTimeMillis();
System.out.println(endtime - starttime);
}
}
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
static void step(Subset current, int counter) {
if (current.sum == target) throw new RuntimeException(current.getText());
else if (counter == set.length) {}
else {
step(new Subset(add(current.subset, set[counter]), current.sum + set[counter]), counter + 1);
step(current, counter + 1);
}
}
static int[] add(int[] array, int digit) {
if (array == null) {
return new int[]{digit};
}
int[] newValue = new int[array.length + 1];
for (int i = 0; i < array.length; i++) {
newValue[i] = array[i];
}
newValue[array.length] = digit;
return newValue;
}
}
class Subset {
int[] subset;
int sum;
Subset(int[] subset, int sum) {
this.subset = subset;
this.sum = sum;
}
public String getText() {
String ret = "";
for (int i = 0; i < (subset == null ? 0 : subset.length); i++) {
ret += " + " + subset[i];
}
if (ret.startsWith(" ")) {
ret = ret.substring(3);
ret = ret + " = " + sum;
} else ret = "null";
return ret;
}
}
EDIT -
The above code still runs in O(n*2^n) time - since the add method runs in O(n) time. This following code will run in true O(2^n) time, and is MUCH more performant, completing in around 20 ms on my machine.
It is limited to sets less than 64 elements due to storing the current subset as the bits in a long.
public class SubsetSumSolver {
static boolean found = false;
static final int target = 100000000;
static final int[] set = new int[]{ . . . };
public static void main(String[] args) {
step(0,0,0);
}
static void step(long subset, int sum, int counter) {
if (sum == target) {
found = true;
System.out.println(getText(subset, sum));
}
else if (!found && counter != set.length) {
step(subset + (1 << counter), sum + set[counter], counter + 1);
step(subset, sum, counter + 1);
}
}
static String getText(long subset, int sum) {
String ret = "";
for (int i = 0; i < 64; i++) if((1 & (subset >> i)) == 1) ret += " + " + set[i];
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + sum;
else ret = "null";
return ret;
}
}
EDIT 2 -
Here is another version uses a meet in the middle attack, along with a little bit shifting in order to reduce the complexity from O(2^n) to O(2^(n/2)).
If you want to use this for sets with between 32 and 64 elements, you should change the int which represents the current subset in the step function to a long although performance will obviously drastically decrease as the set size increases. If you want to use this for a set with odd number of elements, you should add a 0 to the set to make it even numbered.
import java.util.ArrayList;
import java.util.List;
public class SubsetSumMiddleAttack {
static final int target = 100000000;
static final int[] set = new int[]{ ... };
static List<Subset> evens = new ArrayList<>();
static List<Subset> odds = new ArrayList<>();
static int[][] split(int[] superSet) {
int[][] ret = new int[2][superSet.length / 2];
for (int i = 0; i < superSet.length; i++) ret[i % 2][i / 2] = superSet[i];
return ret;
}
static void step(int[] superSet, List<Subset> accumulator, int subset, int sum, int counter) {
accumulator.add(new Subset(subset, sum));
if (counter != superSet.length) {
step(superSet, accumulator, subset + (1 << counter), sum + superSet[counter], counter + 1);
step(superSet, accumulator, subset, sum, counter + 1);
}
}
static void printSubset(Subset e, Subset o) {
String ret = "";
for (int i = 0; i < 32; i++) {
if (i % 2 == 0) {
if ((1 & (e.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
else {
if ((1 & (o.subset >> (i / 2))) == 1) ret += " + " + set[i];
}
}
if (ret.startsWith(" ")) ret = ret.substring(3) + " = " + (e.sum + o.sum);
System.out.println(ret);
}
public static void main(String[] args) {
int[][] superSets = split(set);
step(superSets[0], evens, 0,0,0);
step(superSets[1], odds, 0,0,0);
for (Subset e : evens) {
for (Subset o : odds) {
if (e.sum + o.sum == target) printSubset(e, o);
}
}
}
}
class Subset {
int subset;
int sum;
Subset(int subset, int sum) {
this.subset = subset;
this.sum = sum;
}
}
Here's the problem.
http://usaco.org/index.php?page=viewproblem2&cpid=487
Here's the solution.
http://usaco.org/current/data/sol_marathon_bronze.html
I already solved that problem but my code is too slow to pass all the testcases until 100, 000 points, so I read the official solution, but I could not understand the solution, the math part that says:
int largestSkip = 0;
for(int i = 1; i < n-1; i++) {
int noSkipDistance = Math.abs(x[i+1] - x[i]) + Math.abs(x[i] - x[i-1]) + Math.abs(y[i+1] - y[i]) + Math.abs(y[i] - y[i-1]);
int skipDistance = Math.abs(x[i+1] - x[i-1]) + Math.abs(y[i+1] - y[i-1]);
largestSkip = Math.max(largestSkip, noSkipDistance - skipDistance);
}
How do they get those formulas?, if I understood well the variable skipDistance goes from 0 - skip 1- 2, 1- skip 2 -3, and so on, the other variables noskipDistance goes from 0 -2, 1 -3 (no skipping) and so on, are there other similar problems? or please if anyone could help me to understand I will be really grateful
The best solution that comes to my mind is, but It is too slow
import java.io.*;
import java.util.*;
public class marathon {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("marathon.in"));
PrintWriter pw = new PrintWriter(new File("marathon.out"));
int n = Integer.parseInt(br.readLine());
int[][] arr = new int[n][2];
boolean[] flag = new boolean[n];
for(int i = 0; i < n; i++){
String s = br.readLine();
int n1 = Integer.parseInt(s.substring(0 , s.indexOf(" ")));
int n2 = Integer.parseInt(s.substring(s.indexOf(" ") +1, s.length()));
arr[i][0] = n1;
arr[i][1] = n2;
}
Arrays.fill(flag, true);
long min = Long.MAX_VALUE;
for(int i = 1; i < (n -1); i++){
flag[i] = false;
min = Math.min(min, solve(arr, flag, n));
flag[i] = true;
}
pw.println(min);
pw.close();
}
public static long solve(int[][]arr, boolean[] flag, int n){
long min = Long.MAX_VALUE;
long sum = 0;
List<Integer> listX = new ArrayList<>();
List<Integer> listY = new ArrayList<>();
listX.add(arr[0][0]);
listY.add(arr[0][1]);
for(int i = 1; i < n -1; i++){
if(flag[i]){
listX.add(arr[i][0]);
listY.add(arr[i][1]);
}
}
listX.add(arr[n-1][0]);
listY.add(arr[n-1][1]);
for(int i = 1; i < listX.size(); i++){
sum = Math.abs(listX.get(i -1) - listX.get(i)) + Math.abs(listY.get(i -1) - listY.get(i)) + sum;
}
return sum;
}
}
What happening in this solution is what is written in their editorial. At each point it is calculating the effective length it would go if it chose that point as skipping point.
For eq:
If there are four points (a, b, c, d) and you are at point 'a'. You can either go to 'b' or skip 'b'.
If you go to 'b', distance traveled is: Manhattan distance a -> b + b -> c; (Lets call it X)
If you skip 'b', distance traveled is: Manhattan distance a ->c; (Call it Y)
Gain in distance by choosing this choice = X - Y.
You objective is to find the maximum gain and choose the point which gives it.
In the end, ans is: sum of all Manhattan distances - the maximum gain.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Here the predecessor vertices get displayed for all iterations. I want the final predecessor to be displayed for the vertices
import java.io.*;
import java.util.*;
public class BellmanFord {
LinkedList<Edge> edges;
int d[];
int n,e,s;
final int INFINITY=999;
private static class Edge {
int u,v,w;
public Edge(int a, int b, int c){
u=a;
v=b;
w=c;
}
BellmanFord() throws IOException{
int item;
edges=new LinkedList<Edge>();
BufferedReader inp = new BufferedReader (new InputStreamReader(System.in));
System.out.print("Enter number of vertices ");
n=Integer.parseInt(inp.readLine());
System.out.println("Cost Matrix");
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
item=Integer.parseInt(inp.readLine());
if(item!=0)
edges.add(new Edge(i,j,item));
}
e=edges.size();
d=new int[n];
System.out.print("Enter the source vertex ");
s=Integer.parseInt(inp.readLine());
}
void relax() {
int i,j;
for(i=0;i<n;++i)
d[i]=INFINITY;;
d[s] = 0;
for (i = 0; i < n - 1; ++i)
for (j = 0; j < e; ++j)
if (d[edges.get(j).u] + edges.get(j).w < d[edges.get(j).v])
{
d[edges.get(j).v] = d[edges.get(j).u] + edges.get(j).w;
/*Gives me the predecessor nodes of all iterations How can i get the final predecessornodes*/ System.out.println(edges.get(j).v+" Has predecessor " + edges.get(j).u);
}
}
boolean cycle() {
int j;
for (j = 0; j < e; ++j)
if (d[edges.get(j).u] + edges.get(j).w < d[edges.get(j).v])
return false;
return true;
}
public static void main(String args[]) throws IOException {
BellmanFord r=new BellmanFord();
r.relax();
if(r.cycle())
for(int i=0;i<r.n;i++)
System.out.println(r.s+"to"+i+" ==> "+r.d[i]);
else
System.out.println(" There is a nagative edge cycle ");
}
}
The erronous output is as follows. I am trying to print out the predecessor for every iterations:
**OUTPUT:**
Enter number of vertices
Cost Matrix
0
-1
4
0
0
0
0
3
2
2
0
0
0
0
0
0
1
5
0
0
0
0
0
-3
0
Enter the source vertex
1 Has predecessor 0
2 Has predecessor 0
2 Has predecessor 1
3 Has predecessor 1
4 Has predecessor 1
3 Has predecessor 4
0to0 ==> 0
0to1 ==> -1
0to2 ==> 2
0to3 ==> -2
0to4 ==> 1
Seems that your last lines are very less understandable and so I give you my program which I made a few days back.. you can look for the mistake your program makes since its difficult to understand 100 lines of code and find out errors :Also I advice you to focus more on writing neat and commented codes rather than straightaway focussing on time optimizations. Only check the logic and try to implement it in your code ,that's why I didn't post a Java code so that you could get everything easily :)
// A C / C++ program for Bellman-Ford's single source shortest path algorithm.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
// a structure to represent a weighted edge in graph
struct Edge
{
int src, dest, weight;
};
// a structure to represent a connected, directed and weighted graph
struct Graph
{
// V-> Number of vertices, E-> Number of edges
int V, E;
// graph is represented as an array of edges.
struct Edge* edge;
};
// Creates a graph with V vertices and E edges
struct Graph* createGraph(int V, int E)
{
struct Graph* graph = (struct Graph*) malloc( sizeof(struct Graph) );
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*) malloc( graph->E * sizeof( struct Edge ) );
return graph;
}
// A utility function used to print the solution
void printArr(int dist[], int n)
{
printf("Vertex Distance from Source\n");
for (int i = 0; i < n; ++i)
printf("%d \t\t %d\n", i, dist[i]);
}
// The main function that finds shortest distances from src to all other
// vertices using Bellman-Ford algorithm. The function also detects negative
// weight cycle
void BellmanFord(struct Graph* graph, int src)
{
int V = graph->V;
int E = graph->E;
int dist[V];
// Step 1: Initialize distances from src to all other vertices as INFINITE
for (int i = 0; i < V; i++)
dist[i] = INT_MAX;
dist[src] = 0;
// Step 2: Relax all edges |V| - 1 times. A simple shortest path from src
// to any other vertex can have at-most |V| - 1 edges
for (int i = 1; i <= V-1; i++)
{
for (int j = 0; j < E; j++)
{
int u = graph->edge[j].src;
int v = graph->edge[j].dest;
int weight = graph->edge[j].weight;
if (dist[u] + weight < dist[v])
dist[v] = dist[u] + weight;
}
}
// Step 3: check for negative-weight cycles. The above step guarantees
// shortest distances if graph doesn't contain negative weight cycle.
// If we get a shorter path, then there is a cycle.
for (int i = 0; i < E; i++)
{
int u = graph->edge[i].src;
int v = graph->edge[i].dest;
int weight = graph->edge[i].weight;
if (dist[u] + weight < dist[v])
printf("Graph contains negative weight cycle");
}
printArr(dist, V);
return;
}
// Driver program to test above functions
int main()
{
int V = 5; // Number of vertices in graph
int E = 8; // Number of edges in graph
struct Graph* graph = createGraph(V, E);
// add edge 0-1
graph->edge[0].src = 0;
graph->edge[0].dest = 1;
graph->edge[0].weight = -1;
// add edge 0-2
graph->edge[1].src = 0;
graph->edge[1].dest = 2;
graph->edge[1].weight = 4;
// add edge 1-2
graph->edge[2].src = 1;
graph->edge[2].dest = 2;
graph->edge[2].weight = 3;
// add edge 1-3
graph->edge[3].src = 1;
graph->edge[3].dest = 3;
graph->edge[3].weight = 2;
// add edge 1-4
graph->edge[4].src = 1;
graph->edge[4].dest = 4;
graph->edge[4].weight = 2;
// add edge 3-2
graph->edge[5].src = 3;
graph->edge[5].dest = 2;
graph->edge[5].weight = 5;
// add edge 3-1
graph->edge[6].src = 3;
graph->edge[6].dest = 1;
graph->edge[6].weight = 1;
// add edge 4-3
graph->edge[7].src = 4;
graph->edge[7].dest = 3;
graph->edge[7].weight = -3;
BellmanFord(graph, 0);
return 0;
}
There is a "Weighted Quick-Union with Path Compression" algorithm.
The code:
public class WeightedQU
{
private int[] id;
private int[] iz;
public WeightedQU(int N)
{
id = new int[N];
iz = new int[N];
for(int i = 0; i < id.length; i++)
{
iz[i] = i;
id[i] = i;
}
}
public int root(int i)
{
while(i != id[i])
{
id[i] = id[id[i]]; // this line represents "path compression"
i = id[i];
}
return i;
}
public boolean connected(int p, int q)
{
return root(p) == root(q);
}
public void union(int p, int q) // here iz[] is used to "weighting"
{
int i = root(p);
int j = root(q);
if(iz[i] < iz[j])
{
id[i] = j;
iz[j] += iz[i];
}
else
{
id[j] = i;
iz[i] += iz[j];
}
}
}
Questions:
How does the path compression work? id[i] = id[id[i]] means that we reach only the second ancester of our node, not the root.
iz[] contains integers from 0 to N-1. How does iz[] help us know the number of elements in the set?
Can someone clarify this for me?
First understand that id is a forest. id[i] is the parent of i. If id[i] == i it means that i is a root.
For some root i (where id[i] == i) then iz[i] is the number of elements in the tree rooted at i.
public int root(int i)
{
while(i != id[i])
{
id[i] = id[id[i]]; // this line represents "path compression"
i = id[i];
}
return i;
}
How does the path compression work? id[i] = id[id[i]] means that we reach only the second ancester of our node, not the root.
As we are ascending the tree to find the root we move nodes from their parents to their grandparents. This partially flattens the tree. Notice that this operation doesn't change which tree the node is a member of, this is all we are interested in. This is the path compression technique.
(You did notice the loop right? while(i == id[i]) terminates once i is a root node)
iz[] contains integers from 0 to N-1. How does iz[] help us know the number of elements in the set?
There is a transcription error in the code:
for(int i = 0; i < id.length; i++)
{
iz[i] = i; // WRONG
id[i] = i;
}
This is the correct version:
for(int i = 0; i < id.length; i++)
{
iz[i] = 1; // RIGHT
id[i] = i;
}
iz[i] is the number of elements for a tree rooted at i (or if i is not a root then iz[i] is undefined). So it should be initialized to 1, not i. Initially each element is a seperate "singleton" tree of size 1.
id[i] = id[id[i]]; // this line represents "path compression"
The above code is "Simpler one-pass variant" as mentioned in the slide of Union Find (Algorithms, Part I by Kevin Wayne and Robert Sedgewick). Therefore your guess for question 1 is correct. Each examined node points to its grandparent.
To make each examined node points to the root we will need two-pass implementation:
/**
* Returns the component identifier for the component containing site <tt>p</tt>.
* #param p the integer representing one site
* #return the component identifier for the component containing site <tt>p</tt>
* #throws java.lang.IndexOutOfBoundsException unless 0 <= p < N
*/
public int find(int p) {
int root = p;
while (root != id[root])
root = id[root];
while (p != root) {
int newp = id[p];
id[p] = root;
p = newp;
}
return root;
}
Reference:
http://algs4.cs.princeton.edu/15uf/WeightedQuickUnionPathCompressionUF.java.html
One more thing to be noted here:
While finding the root when we are making id[i]=id[id[i]] i.e; making i under its grand parent
-then size of id[i] will decrease by size of i i,e; iz[id[i]]-=iz[i]
Now this makes code perfectly correct.
I am not sure about this but intuitively i feel,
Its absence does not cause problems because we are always comparing size of the roots.
Question 1.
It is not right to say that the line id[i] = id[id[i]]; only reaches the second ancestor of the root.You will realize that while loop while(i != id[i]) stops only when the node i is pointing at the root i.e when i == id[i].By this time we shall have pointed the node to the root using the line id[i] = id[id[i]]; where the inner id[i] is the root.
Question 2.
You are wrong to initialize iz[i] = i; actually it should be iz[i] = 1; meaning, each and every node size is initialized by 1 at the beginning since they are of size 1.
In the union function you realize that we have the lines iz[j] += iz[i]; and iz[i] += iz[j]; which updates the size of the root node to be the sum of the sizes of the two components joined together. This efficiently updates the nodes sizes.