Given a Directed Acyclic Graph, G and two vertices u and v, I need to find the longest u-v path in G. DFS calls explore function to store visited vertices in the visited[] boolean array (if vertex is visited the value in array is set true, otherwise it's false). Vertices u and v are never marked as visited. Variable MAX stores the max path; when STOP vertex is reached in explore() function MAX is set to max of current path length and MAX value. The code doesn't work right.
import java.util.Iterator;
import java.util.LinkedList;
public class DAG2 {
int vertex;
LinkedList<Integer> list[];
int START, STOP;
int length = 0;
int MAX = 0;
public DAG2(int vertex) {
this.vertex = vertex;
list = new LinkedList[vertex];
for (int i = 0; i < vertex; i++) {
list[i] = new LinkedList<>();
}
}
public void addEdge(int source, int destination) {
// add edge
list[source].addFirst(destination);
}
void DFS(int u, int v) {
boolean[] visited = new boolean[this.vertex];
START = u;
STOP = v;
explore(v, visited);
}
private void explore(int v, boolean[] visited) {
// TODO Auto-generated method stub
visited[v] = true;
visited[START] = false;
visited[STOP] = false;
Iterator<Integer> i = list[v].listIterator();
while (i.hasNext()) {
int n = i.next();
length++;
if (n == STOP) {
MAX = Math.max(MAX, length);
length = 0;
}
if (!visited[n])
explore(n, visited);
}
}
public static void main(String args[]) {
DAG2 g = new DAG2(8);
g.addEdge(1, 2);
g.addEdge(1, 3);
g.addEdge(2, 4);
g.addEdge(2, 5);
g.addEdge(3, 6);
g.addEdge(4, 7);
g.addEdge(5, 7);
g.addEdge(6, 5);
g.addEdge(6, 7);
// new
g.addEdge(2, 3);
g.addEdge(3, 5);
g.addEdge(5, 4);
}
}
The first thing I notice about the "visited" array is that if you're looking for more than one path, you might visit a node more than once (because more than one math might lead to it, e.g. 1 -> 3 -> 4 and 1 -> 2 -> 3 -> 4 will both visit 3).
My first instinct for a depth first search is to use a recursive search. I put together one like that looks like this:
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class DAG {
private Map<Integer, List<Integer>> graph = new HashMap<>();
public void addEdge(final int src, final int dst) {
if (!graph.containsKey(src)) {
graph.put(src, new LinkedList<Integer>());
}
graph.get(src).add(dst);
}
public List<Integer> findMaxPath(final int start, final int end) {
if (start == end) {
// The path is just this element, so return a list with just the
// start (or end).
final List<Integer> path = new LinkedList<>();
path.add(start);
return path;
}
if (!graph.containsKey(start)) {
// There is no path forward.
return null;
}
List<Integer> longestPath = null;
for (Integer next : graph.get(start)) {
final List<Integer> newPath = findMaxPath(next, end);
if (null != newPath) {
// Found a new path
if ( (null == longestPath)
|| (newPath.size() > longestPath.size()) )
{
// It was longer than the previous longest,
// it is new longest.
longestPath = newPath;
}
}
}
if (null != longestPath) {
// A path was found, include this node as the start of the path.
longestPath.add(0, start);
}
return longestPath;
}
public static void main(final String[] args) {
final DAG g = new DAG();
g.addEdge(1, 2);
g.addEdge(1, 3);
g.addEdge(1, 6);
g.addEdge(2, 4);
g.addEdge(3, 5);
g.addEdge(6, 7);
g.addEdge(7, 4);
g.addEdge(2, 6);
printPath(g.findMaxPath(1, 5));
g.addEdge(4, 5); // Make a longer path.
printPath(g.findMaxPath(1, 5));
}
private static void printPath(final List<Integer> path) {
System.out.println("Path:");
if (null != path) {
for (Integer p : path) {
System.out.println(" " + p);
}
} else {
System.out.println(" null");
}
}
}
To convert it into a non-recursive method, you can use a List as a Stack. Where findMaxPath() calls itself, instead you would push() the current node on the stack and use the next one, when that's done pop() the node and continue. Put all that in a loop, and it should work.
Related
I have an undirected graph which gets loaded as an adjacency matrix. I have a method to detect a cycle in a graph using BFS algorithm. What I am trying to achieve is to print all the edges in a way that they indicate a cycle which has been found.
I am able to print all the edges in a graph, but I am unable to print only those edges which create a cycle. How do I make it work?
Here is the graph implementation:
Edge:
public class Edge {
int source, dest;
public Edge(int source, int dest) {
this.source = source;
this.dest = dest;
}
}
Graph:
public class Graph {
// A List of Lists to represent an adjacency list
// Each insideList contains pointers to the next vertex
// list with an index of 1 (vertex 1) contains elements 2 and 3 (where 2, 3 are vertices connected to 1)
List<List<Integer>> adjList = null;
// Constructor
public Graph(List<Edge> edges, int N) {
adjList = new ArrayList<>(N);
for (int i = 0; i < N; i++) {
adjList.add(i, new ArrayList<>());
}
// add edges to the undirected graph
for (Edge edge : edges) {
int src = edge.source;
int dest = edge.dest;
adjList.get(src).add(dest);
adjList.get(dest).add(src);
}
}
}
Node:
public class Node {
int v, parent;
public Node(int v, int parent) {
this.v = v;
this.parent = parent;
}
}
Algorithm and test:
public class GraphTest {
// Perform BFS on graph starting from vertex src and
// returns true if cycle is found in the graph
// while traversing the graph, it should display the edges which create a cycle, but I am unable to do it (the result is wrong)
public static boolean BFS(Graph graph, int src, int N) {
// stores booleans if a vertex is discovered or not
boolean[] discovered = new boolean[N];
// mark source vertex as discovered
discovered[src] = true;
// create a queue used to do BFS and
// push source vertex into the queue
Queue<Node> q = new ArrayDeque<>();
q.add(new Node(src, -1));
// run till queue is not empty
while (!q.isEmpty()) {
// pop front node from queue and print it
Node node = q.poll();
// do for every edge (v -> u)
for (int u : graph.adjList.get(node.v)) {
if (!discovered[u]) {
// mark it as discovered
discovered[u] = true;
// construct the queue node containing info
// about vertex and push it into the queue
System.out.println(node.v + " -- " + u);
q.add(new Node(u, node.v));
}
// u is discovered and u is not a parent
else if (u != node.parent) {
// we found a cross-edge ie. cycle is found
return true;
}
}
}
// No cross-edges found in the graph
return false;
}
// Check if an undirected graph contains cycle or not
public static void main(String[] args) {
// In my case I load an adjacency matrix from file and then perform an action to create Edges.
// 0 1 1 0
// 1 0 1 0
// 1 1 0 1
// 0 0 1 0
// Edge(1, 2), Edge(2, 3), Edge(3, 1), Edge(3, 4)
// Edge(3, 1) introduces a cycle in the graph
List<Edge> edges = new ArrayList<Edge>();
ArrayList<ArrayList<Integer>> matrixList = loadFromFile(filePath);
System.out.println("Graph: (Adjacency Matrix)");
for (int i = 0; i < matrixList.size(); i++) {
for (int j = 0; j < matrixList.size(); j++) {
System.out.print(matrixList.get(i).get(j) + " ");
}
System.out.println();
}
System.out.println("All the edges: ");
for (int i = 0; i < matrixList.size(); i++) {
// ' + 1' is added so as to start vertices from 1 instead of 0
int temp = i + 1;
for (int j = 0; j < matrixList.size(); j++) {
if (matrixList.get(i).get(j) == 1) {
System.out.println(temp + "--" + (j + 1) + " ");
// each edge is added one-way only since it is an undirected graph
// if Edge(1,3) is already present, Edge(3,1) is not added
boolean isFound = false;
for (Edge e : edges) {
if (e.dest == temp && e.source == (j + 1)) {
isFound = true;
}
}
if (!isFound)
edges.add(new Edge(temp, j + 1));
}
}
System.out.println();
}
// sets number of vertices in the graph
final int N = 5;
// creates a graph from edges
Graph graph = new Graph(edges, N);
boolean[] discovered = new boolean[N];
// do BFS traversal in connected components of graph
System.out.println("Detect a cycle: ");
if (BFS(graph, 1, N))
System.out.println("Graph contains cycle");
else
System.out.println("Graph doesn't contain any cycle");
}
Input: an adjacency matrix (or a prebuilt list of edges)
Current wrong output: displays some edges, but not all the edges of a cycle
Expected output: to print all the edges which create a cycle, as shown in an example above,
I would like to display: 1--2, 2--3, 3--1
The ending vertex of one edge is a starting vertex of another edge in a cycle.
I'm not claiming this is the best way to achieve the result, but it's one of the ways.
First of all, I'd change the definition of your Node:
public class Node {
int v;
Node parent;
public Node(int v, Node parent) {
this.v = v;
this.parent = parent;
}
}
Then in your method BFS, I'd change the boolean array discovered to Node array, so you know, which path leads to this Node.
// stores booleans if a vertex is discovered or not
Node[] discovered = new Node[N];
Your BFS method would work then like this:
public static boolean BFS(Graph graph, int src, int N) {
// stores booleans if a vertex is discovered or not
Node[] discovered = new Node[N];
// mark source vertex as discovered
Node start = new Node(src, null);
discovered[src] = start;
// create a queue used to do BFS and
// push source vertex into the queue
Queue<Node> q = new LinkedList<>();
q.add(start);
// run till queue is not empty
while (!q.isEmpty()) {
// pop front node from queue and print it
Node node = q.poll();
// do for every edge (v -> u)
for (int u : graph.adjList.get(node.v)) {
if (discovered[u] == null) {
// mark it as discovered
Node newNode = new Node(u, node);
discovered[u] = newNode;
// construct the queue node containing info
// about vertex and push it into the queue
q.add(newNode);
}
// u is discovered and u is not a parent
else if (u != node.parent.v) {
Node newNode = new Node(u, node);
int commonParent = findCommonParent(discovered[u], newNode);
String result = "";
Node current;
current = discovered[u];
while(current.v != commonParent) {
result = current.parent.v + "--" + current.v + ", " + result;
current = current.parent;
}
current = newNode;
while(current.v != commonParent) {
result = result + current.v + "--" + current.parent.v + ", ";
current = current.parent;
}
result = result.substring(0, result.length() - 2);
System.out.println(result);
// we found a cross-edge ie. cycle is found
return true;
}
}
}
// No cross-edges found in the graph
return false;
}
The method findCommonParent can be implemented for example like this:
private static int findCommonParent(Node n1, Node n2) {
Set<Integer> n1Parents = new HashSet<Integer>();
Node temp = n1.parent;
while(temp != null) {
n1Parents.add(temp.v);
temp = temp.parent;
}
temp = n2.parent;
while(temp != null) {
if(n1Parents.contains(temp.v)) {
break;
}
temp = temp.parent;
}
return temp.v;
}
Given Parent Array Such that parent[i]=j where j is the parent and Value array . Need to Find Best possible sum.
Root node will have -1 as parent.
Best Possible sum is maximum sum in one of the tree paths.
Ex)
Integer[] parent = new Integer[] { -1, 0, 0, 2, 3 };
Integer[] values = new Integer[] { 0, 4, 6, -11, 3 };
(0/0)----(1/4)
|
|
(2/6)
|
|
(3/-11)
|
|
(4/3)
Maximum sum here would be 6+0+4=10 for path 2-->0-->1.
I have tried solving it the dfs way. But not sure if it works for all cases. Below is my code. It gives all possible sum. we can take out max from that.
package com.programs.algo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class BestPossibleSum {
static class Node<T> {
T label;
T data;
List<Node<T>> nodes;
}
public static void main(String[] args) {
Integer[] parent = new Integer[] { -1, 0, 0, 1, 1, 3, 5 };
Integer[] values = new Integer[] { 0, 4, 6, -11, 3, 10, 11 };
List<Integer> list1 = new ArrayList<>(Arrays.asList(parent));
List<Integer> list2 = new ArrayList<>(Arrays.asList(values));
bestPossibleSum(list1, list2);
}
static List<Node<Integer>> tree = new ArrayList<>();
private static void bestPossibleSum(List<Integer> list1, List<Integer> list2) {
int adj[][] = new int[list1.size()][list1.size()];
createTree(list1, list2, adj);
List<Integer> traversedNodes = new ArrayList<>();
List<Integer> sumOfraversedNodes = new ArrayList<>();
for (int i = 0; i < adj.length; i++) {
dfs(tree.get(i), traversedNodes, sumOfraversedNodes);
traversedNodes.clear();
}
System.out.println(sumOfraversedNodes);
}
private static void dfs(Node<Integer> tree, List<Integer> traversedNodes, List<Integer> sums) {
if (!traversedNodes.contains(tree.label)) {
traversedNodes.add(tree.label);
sums.add(getSum(traversedNodes));
for (Node<Integer> child : tree.nodes) {
dfs(child, traversedNodes, sums);
}
}
}
private static Integer getSum(List<Integer> traversedNodes) {
System.out.println(traversedNodes);
return traversedNodes.stream().reduce(0, Integer::sum);
}
private static void createTree(List<Integer> parent, List<Integer> values, int[][] adj) {
for (int i = 0; i < parent.size(); i++) {
Node<Integer> node = new Node<>();
node.label = i;
node.data = values.get(i);
node.nodes = new ArrayList<>();
tree.add(i, node);
}
for (int i = 0; i < parent.size(); i++) {
if (parent.get(i) != -1) {
adj[parent.get(i)][i] = 1;
adj[i][parent.get(i)] = 1;
tree.get(parent.get(i)).nodes.add(tree.get(i));
}
}
tree.forEach(t -> {
System.out.println(t.label);
System.out.println(t.nodes.stream().map(m -> m.label).collect(Collectors.toList()));
});
// System.out.println(Arrays.deepToString(adj));
}
}
I would divide your question to 2 different issues:
Build tree from your data
Find the max sum
I wrote the code in PHP but you can convert it to any language you need (my JAVA skill are bit rusty...)
Build the Tree:
$parent = array( -1, 0, 0, 2, 3 );
$values = array(0, 4, 6, -11, 3 );
function getNode($id, $data) {
return array("id" => $id, "data" => $data, "childs" => array());
}
function addToTree($node, &$root, $parentsId) {
if ($parentsId == -1)
$root = $node;
else if ( $root["id"] == $parentsId)
$root["childs"][] = $node;
else
foreach($root["childs"] as &$child)
addToTree($node, $child, $parentsId);
}
$root = null;
for($i = 0; $i < count($parent); $i++) {
addToTree(getNode($i, $values[$i]), $root, $parent[$i]);
}
Now root if contain you "tree-like" data. Notice this code works only if the nodes given at the right order and it cannot support multi root (assume tree and not forest)
Find max path:
function maxPath($node) {
$sum = $node["data"];
foreach($node["childs"] as $child) {
$s = maxPath($child);
if ($s > 0) // if its not positive then don't take it
$sum += $s;
}
return $sum;
}
This recursive function will get your max-sum-path. Notice this will allow multi-child per node and also the path can have star-shape.
Posting Java code considering it as tree with left and right nodes.
https://www.geeksforgeeks.org/construct-a-binary-tree-from-parent-array-representation/
https://www.geeksforgeeks.org/find-maximum-path-sum-in-a-binary-tree/
private static int maxSum(Node<Integer> btree, Result result) {
if (btree == null)
return 0;
int l = maxSum(btree.left, result);
int r = maxSum(btree.right, result);
System.out.println(l + " " + r + " " + btree.data);
int maxSingle = Math.max(Math.max(l, r) + btree.label, btree.label);
int maxTop = Math.max(l + r + btree.label, maxSingle);
result.val = Math.max(maxTop, result.val);
return maxSingle;
}
private static Node<Integer> createBinaryTree(Integer[] parent, Node<Integer> root) {
Map<Integer, Node<Integer>> map = new HashMap<>();
for (int i = 0; i < parent.length; i++) {
map.put(i, new Node<>(i));
}
for (int i = 0; i < parent.length; i++) {
if (parent[i] == -1) {
root = map.get(i);
} else {
Node<Integer> par = map.get(parent[i]);
Node<Integer> child = map.get(i);
if (par.left == null) {
par.left = child;
} else {
par.right = child;
}
}
}
return root;
}
1 . convert the given parent array into graph with the following steps :
unordered_map<int,vector<pair<int,int>>> graph;
for(int i=0;i<n;i++){
if(parents[i]!=-1){
graph[parents[i]].push_back({i,values[i]});
graph[i].push_back({parents[i],values[parents[i]]});
}
}
2.apply DFS on each node and check the maximum Path Sum
vector<bool> vis(n,false);
int res=0;
for(int i=0;i<n;i++){
vis.clear();
dfs(i,vis,mp,values,res);
}
DFS function
void dfs(int src,vector&vis,unordered_map<int,
vector<pair<int,int>>>&graph,vector<int>&values,int res){
res+=values[src];
ans=max(ans,res);
vis[src]=true;
for(int i=0;i<graph[src].size();i++){
if(!vis[graph[src][i].first]){
dfs(graph[src][i].first,vis,graph,values,res);
}
}
vis[src]=false;
}
C++ code :
#include<bits/stdc++.h>
using namespace std;
int ans=INT_MIN;
void dfs(int src,vector<bool>&vis,unordered_map<int,
vector<pair<int,int>>>&graph,vector<int>&values,int res){
res+=values[src];
ans=max(ans,res);
vis[src]=true;
for(int i=0;i<graph[src].size();i++){
if(!vis[graph[src][i].first]){
dfs(graph[src][i].first,vis,graph,values,res);
}
}
vis[src]=false;
}
int maxPathSum(vector<int>&parents,vector<int>&values){
int n=parents.size();
unordered_map<int,vector<pair<int,int>>> mp;
for(int i=0;i<n;i++){
if(parents[i]!=-1){
mp[parents[i]].push_back({i,values[i]});
mp[i].push_back({parents[i],values[parents[i]]});
}
}
vector<bool> vis(n,false);
int res=0;
for(int i=0;i<n;i++){
vis.clear();
dfs(i,vis,mp,values,res);
}
return ans;
}
int main(){
vector<int> parent = {-1,0,0,2,3}; //{-1,0,1,2,0};
vector<int> values = {0,4,6,-11,3}; //{-2,10,10,-3,10};
cout<<maxPathSum(parent,values)<<endl;
return 0;
}
Today I got this problem in One of the company's hackerrank test.
Here is my solution. All test cases have been passed successfully
import java.io.*;
import java.math.*;
import java.security.*;
import java.text.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.function.*;
import java.util.regex.*;
import java.util.stream.*;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
class Result {
/*
* Complete the 'bestSumDownwardTreePath' function below.
*
* The function is expected to return an INTEGER.
* The function accepts following parameters:
* 1. INTEGER_ARRAY parent
* 2. INTEGER_ARRAY values
*/
static int bestPath = Integer.MIN_VALUE;
public static int bestSumDownwardTreePath(List<Integer> parent, List<Integer> values) {
if(parent.size() == 1) return values.get(0);
Map<Integer, List<Integer>> tree = new HashMap<>();
for(int i = 1; i < parent.size(); i++) {
List<Integer> temp = tree.getOrDefault(parent.get(i), null);
if(temp == null) {
temp = new ArrayList<>();
temp.add(i);
tree.put(parent.get(i), temp);
}
else {
temp.add(i);
}
}
findBestSum(parent, values, tree, 0, 0);
return bestPath;
}
public static void findBestSum(List<Integer> parent, List<Integer> values,
Map<Integer, List<Integer>> tree, int root, int sum) {
sum = sum + values.get(root);
bestPath = Math.max(bestPath, sum);
sum = Math.max(0, sum);
if(tree.get(root) == null) return;
for(Integer child: tree.get(root)) {
findBestSum(parent, values, tree, child, sum);
}
}
}
public class Solution {
public static void main(String[] args) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(System.getenv("OUTPUT_PATH")));
int parentCount = Integer.parseInt(bufferedReader.readLine().trim());
List<Integer> parent = IntStream.range(0, parentCount).mapToObj(i -> {
try {
return bufferedReader.readLine().replaceAll("\\s+$", "");
} catch (IOException ex) {
throw new RuntimeException(ex);
}
})
.map(String::trim)
.map(Integer::parseInt)
.collect(toList());
int valuesCount = Integer.parseInt(bufferedReader.readLine().trim());
List<Integer> values = IntStream.range(0, valuesCount).mapToObj(i -> {
try {
return bufferedReader.readLine().replaceAll("\\s+$", "");
} catch (IOException ex) {
throw new RuntimeException(ex);
}
})
.map(String::trim)
.map(Integer::parseInt)
.collect(toList());
int result = Result.bestSumDownwardTreePath(parent, values);
bufferedWriter.write(String.valueOf(result));
bufferedWriter.newLine();
bufferedReader.close();
bufferedWriter.close();
}
}
I watched this Princeton University connected components tutorial and tried to run the code given on my computer (skip to 13 mins for code). The code should figure out all the different connected components of the graph and assign each vertex an 'id' which identifies which component it belongs to. I made a sample graph to test it, see here for:
![visual representation][1]
When I run the code below, it prints out the ids to be 0,0,1,2,3 but they should be 0,0,0,1,1. Any ideas what I'm doing wrong?
public class ConnectedComponents {
public boolean[] marked;
public int[] id;
public int count;
public ConnectedComponents() {
//Make a Graph with 5 vertices, and 4 edges
Graph g = new Graph(5, false, false);
g.addEdge(0, 1); g.addEdge(0, 2);g.addEdge(1, 2);
g.addEdge(3, 4);
int numVertices = g.getNumberOfVertices();
marked = new boolean[numVertices];
id = new int[numVertices];
for(int v = 0; v < numVertices; v++) {
if(!marked[v]) {
dfs(g, v);
count++;
}
}
}
public void dfs(Graph g, int v) {
marked[v] = true;
id[v] = count;
// loops through each vertex that's connected to v
for(int w: g.getEdgeMatrix()[v]) {
if(!marked[w]) {
dfs(g, w);
}
}
}
public int id(int v) {
return id[v];
}
public static void main(String [] args){
ConnectedComponents cc = new ConnectedComponents();
for(int i = 0; i < cc.id.length; i++) {
System.out.println(cc.id[i]);
}
}
}
https://i.stack.imgur.com/vhTxD.jpg
Because of the implementation of addEdge, your DFS is effectively operating on a directed graph. Change it to add the reverse edges.
I'm not sure where to start, but this is messy. Basically I need to write an Insertion Sort method for singly linked list - which causes enough problems, because usually for Insertion Sort - you're supposed to go through array/list elements backwards - which implementing into a singly linked list seems pointless, because the point of it - is that you're only capable of going forwards in the list and in addition to that -> I need to execute "swap" operations externally, which I do not completely understand how to perform that while using list structure.
This is my ArrayClass and Swap method that I used:
class MyFileArray : DataArray
{
public MyFileArray(string filename, int n, int seed)
{
double[] data = new double[n];
length = n;
Random rand = new Random(seed);
for (int i = 0; i < length; i++)
{
data[i] = rand.NextDouble();
}
if (File.Exists(filename)) File.Delete(filename);
try
{
using (BinaryWriter writer = new BinaryWriter(File.Open(filename,
FileMode.Create)))
{
for (int j = 0; j < length; j++)
writer.Write(data[j]);
}
}
catch (IOException ex)
{
Console.WriteLine(ex.ToString());
}
}
public FileStream fs { get; set; }
public override double this[int index]
{
get
{
Byte[] data = new Byte[8];
fs.Seek(8 * index, SeekOrigin.Begin);
fs.Read(data, 0, 8);
double result = BitConverter.ToDouble(data, 0);
return result;
}
}
public override void Swap(int j, double a)
{
Byte[] data = new Byte[16];
BitConverter.GetBytes(a).CopyTo(data, 0);
fs.Seek(8 * (j + 1), SeekOrigin.Begin);
fs.Write(data, 0, 8);
}
}
And this is my Insertion Sort for array:
public static void InsertionSort(DataArray items)
{
double key;
int j;
for (int i = 1; i < items.Length; i++)
{
key = items[i];
j = i - 1;
while (j >= 0 && items[j] > key)
{
items.Swap(j, items[j]);
j = j - 1;
}
items.Swap(j, key);
}
}
Now I somehow have to do the same exact thing - however using Singly Linked List, I'm given this kind of class to work with (allowed to make changes):
class MyFileList : DataList
{
int prevNode;
int currentNode;
int nextNode;
public MyFileList(string filename, int n, int seed)
{
length = n;
Random rand = new Random(seed);
if (File.Exists(filename)) File.Delete(filename);
try
{
using (BinaryWriter writer = new BinaryWriter(File.Open(filename,
FileMode.Create)))
{
writer.Write(4);
for (int j = 0; j < length; j++)
{
writer.Write(rand.NextDouble());
writer.Write((j + 1) * 12 + 4);
}
}
}
catch (IOException ex)
{
Console.WriteLine(ex.ToString());
}
}
public FileStream fs { get; set; }
public override double Head()
{
Byte[] data = new Byte[12];
fs.Seek(0, SeekOrigin.Begin);
fs.Read(data, 0, 4);
currentNode = BitConverter.ToInt32(data, 0);
prevNode = -1;
fs.Seek(currentNode, SeekOrigin.Begin);
fs.Read(data, 0, 12);
double result = BitConverter.ToDouble(data, 0);
nextNode = BitConverter.ToInt32(data, 8);
return result;
}
public override double Next()
{
Byte[] data = new Byte[12];
fs.Seek(nextNode, SeekOrigin.Begin);
fs.Read(data, 0, 12);
prevNode = currentNode;
currentNode = nextNode;
double result = BitConverter.ToDouble(data, 0);
nextNode = BitConverter.ToInt32(data, 8);
return result;
}
To be completely honest - I'm not sure neither how I'm supposed to implement Insertion Sort nor How then translate it into an external sort. I've used this code for not external sorting previously:
public override void InsertionSort()
{
sorted = null;
MyLinkedListNode current = headNode;
while (current != null)
{
MyLinkedListNode next = current.nextNode;
sortedInsert(current);
current = next;
}
headNode = sorted;
}
void sortedInsert(MyLinkedListNode newnode)
{
if (sorted == null || sorted.data >= newnode.data)
{
newnode.nextNode = sorted;
sorted = newnode;
}
else
{
MyLinkedListNode current = sorted;
while (current.nextNode != null && current.nextNode.data < newnode.data)
{
current = current.nextNode;
}
newnode.nextNode = current.nextNode;
current.nextNode = newnode;
}
}
So if someone could maybe give some kind of tips/explanations - or maybe if you have ever tried this - code examples how to solve this kind of problem, would be appreciated!
I actually have solved this fairly recently.
Here's the code sample that you can play around with, it should work out of the box.
public class SortLinkedList {
public static class LinkListNode {
private Integer value;
LinkListNode nextNode;
public LinkListNode(Integer value, LinkListNode nextNode) {
this.value = value;
this.nextNode = nextNode;
}
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
public LinkListNode getNextNode() {
return nextNode;
}
public void setNextNode(LinkListNode nextNode) {
this.nextNode = nextNode;
}
#Override
public String toString() {
return this.value.toString();
}
}
public static void main(String...args) {
LinkListNode f = new LinkListNode(12, null);
LinkListNode e = new LinkListNode(11, f);
LinkListNode c = new LinkListNode(13, e);
LinkListNode b = new LinkListNode(1, c);
LinkListNode a = new LinkListNode(5, b);
print(sort(a));
}
public static void print(LinkListNode aList) {
LinkListNode iterator = aList;
while (iterator != null) {
System.out.println(iterator.getValue());
iterator = iterator.getNextNode();
}
}
public static LinkListNode sort(LinkListNode aList){
LinkListNode head = new LinkListNode(null, aList);
LinkListNode fringePtr = aList.getNextNode();
LinkListNode ptrBeforeFringe = aList;
LinkListNode findPtr;
LinkListNode prev;
while(fringePtr != null) {
Integer valueToInsert = fringePtr.getValue();
findPtr = head.getNextNode();
prev = head;
while(findPtr != fringePtr) {
System.out.println("fringe=" + fringePtr);
System.out.println(findPtr);
if (valueToInsert <= findPtr.getValue()) {
LinkListNode tmpNode = fringePtr.getNextNode();
fringePtr.setNextNode(findPtr);
prev.setNextNode(fringePtr);
ptrBeforeFringe.setNextNode(tmpNode);
fringePtr = ptrBeforeFringe;
break;
}
findPtr = findPtr.getNextNode();
prev = prev.getNextNode();
}
fringePtr = fringePtr.getNextNode();
if (ptrBeforeFringe.getNextNode() != fringePtr) {
ptrBeforeFringe = ptrBeforeFringe.getNextNode();
}
}
return head.getNextNode();
}
}
From a high level, what you are doing is you are keeping track of a fringe ptr, and you are inserting a node s.t. the it is in the correct spot in the corresponding sublist.
For instance, suppose I have this LL.
3->2->5->4
The first iteration, I have fringePtr at 2, and I want to insert 2 somewhere in the sublist that's before the fringe ptr, so I basically traverse starting from head going to the fringe ptr until the value is less than the current value. I also have a previous keeping track of the previous ptr (to account for null, I have a sentinel node at the start of my traversal so I can insert it at the head).
Then, when I see that it's less than the current, I know I need to insert it next to the previous, so I have to:
use a temporary ptr to keep track of my previous's current next.
bind previuos's next to my toInsert node.
bind my toInsert node's next to my temp node.
Then, to continue, you just advance your fringe ptr and try again, basically building up a sublist that is sorted as you move along until fringe hits the end.
i.e. the iterations will look like
1. 3->2->5->4
^
2. 2->3->5->4
^
3. 2->3->5->4
^
4. 2->3->4->5 FIN.
Like the title says, This A* search algorithm never stops searching. I'm trying to create a working A* search algorithm for point-click walking in a 2D tile-based game, some tiles are walk-able and some tiles are solid.
PathFinder.java:
public class PathFinder {
public static List<Node> findPath(Map map, int sx, int sy, int dx, int dy) {
if(map.getTile(dx, dy).isSolid()) return null;
Node startNode = new Node(new Vector2i(sx, sy), null, 0, 0);
Vector2i goal = new Vector2i(dx, dy);
List<Node> open = new ArrayList<>();
HashSet<Node> closed = new HashSet<>();
open.add(startNode);
while(open.size() > 0) {
Node currentNode = open.get(0);
for(int i = 1; i < open.size(); i++) {
if(open.get(i).fCost < currentNode.fCost ||
open.get(i).fCost == currentNode.fCost && open.get(i).hCost < currentNode.hCost) {
currentNode = open.get(i);
}
}
open.remove(currentNode);
closed.add(currentNode);
if(currentNode.tile == goal){
System.out.println("returning path!");
return retracePath(startNode, currentNode);
}
for(Tile tile : map.getNeighbors(currentNode)) {
Vector2i neighbor = new Vector2i(tile.getTileX(), tile.getTileY());
if(tile.isSolid() || getNodeInHashSetForPosition(neighbor, closed) != null) {
continue;
}
double gCost = currentNode.gCost + getNodeDistance(currentNode.tile, neighbor);
if(currentNode.gCost < gCost || !vecInList(neighbor, open)) {
double hCost = getNodeDistance(neighbor, goal);
Node node = new Node(neighbor, currentNode, gCost, hCost);
if(!open.contains(node)) {
open.add(node);
}
}
}
}
return null;
}
private static List<Node> retracePath(Node startNode, Node endNode) {
List<Node> path = new ArrayList<>();
Node currentNode = endNode;
while(currentNode != startNode) {
path.add(currentNode);
currentNode = currentNode.parent;
}
List<Node> finalPath = new ArrayList<>();
for(int i = path.size() - 1; i > 0; i--) {
finalPath.add(path.get(i));
}
return finalPath;
}
private static boolean vecInList(Vector2i vec, List<Node> list) {
for(Node n : list) {
if(n.tile.equals(vec)) return true;
}
return false;
}
private static boolean vecInList(Vector2i vec, HashSet<Node> list) {
for(Node n : list) {
if(n.tile.equals(vec)) return true;
}
return false;
}
private static Node getNodeInHashSetForPosition(Vector2i position, HashSet<Node> hashSet) {
for(Node n : hashSet) {
if(n.tile.equals(position)) return n;
}
return null;
}
private static double getNodeDistance(Vector2i nodeA, Vector2i nodeB) {
int dstX = Math.abs(nodeA.x - nodeB.x);
int dstY = Math.abs(nodeA.y - nodeB.y);
if(dstX > dstY) return 14 * dstY + 10 * (dstX - dstY);
return (14 * dstX) + (10 * (dstY - dstX));
}
}
Node.java
public class Node {
public Vector2i tile;
public Node parent;
public double fCost, gCost, hCost; //a cost is like the distance it takes to get to that point. these are used to find the lowest cost way to get from start point A to end point B.
//gCost is the sum of all of our node to node, or tile to tile, distances.
//hCost is the direct distance from the start node to the end node.
//fCost is the total cost for all the ways we calculate to get to the end node/tile.
public Node(Vector2i tile, Node parent, double gCost, double hCost) { //NODE CONSTRUCTOR STARt
this.tile = tile;
this.parent = parent;
this.gCost = gCost;
this.hCost = hCost;
this.fCost = this.gCost + this.hCost;
}//NODE CONSTRUCTOR END
}
change the following:
if(!open.contains(node)) {
to:
if(!veckInList(neighbor, open) {