Implementing a BFS-search method for a graph - java

I'm implementing my own graph class. My undirected graph is represented by a map which maps every node to a list storing the edges it has.
private Map<T, List<Edge<T>>> graphRep = new HashMap<>();
private static class Edge<T> {
int cost;
T node;
public Edge(T n, int w) {
node = n;
cost = w;
}
I have already created a recursive depth-first traversal method for my graph which utilizes a map to store the path between the start node to the search node. It does by mapping every node the next node on the path between the start node to end node.
#Override
public List<T> depthFirstSearch(T start, T end) {
Set<T> visited = new HashSet<T>();
Map<T,T> path = new HashMap<>();
recursiveDFS(start, end, visited,path);
List<T> myList = new ArrayList<T>();
T current = end;
myList.add(current);
while (current != start) {
myList.add(path.get(current));
current = path.get(current);
}
System.out.println(path);
System.out.println(myList);
Collections.reverse(myList);
return myList;
}
private void recursiveDFS (T node, T end, Set<T> visited, Map<T, T> path) {
// uppdatera path och visited
visited.add(node);
for (Edge<T> e : graphRep.get(node)) {
if (e.node == end) {
path.put(e.node, node);
return;
}
if (!visited.contains(e.node)){
path.put(e.node, node);
recursiveDFS(e.node, end, visited, path);
}
}
}
I believe I can utilize essentially the same code for the breadth-first search as with the depth-first search, only that the instead of traversing the nodes by depth I traverse them by breadth, and that's where I'm stuck. I'm completely lost on how to do that.
#Override
public List<T> breadthFirstSearch(T start, T end) {
Set<T> visited = new HashSet<T>();
Map<T,T> path = new HashMap<>();
recursiveBFS(start, end, visited,path);
List<T> myList = new ArrayList<T>();
T current = end;
myList.add(current);
while (current != start) {
myList.add(path.get(current));
current = path.get(current);
}
System.out.println(path);
System.out.println(myList);
Collections.reverse(myList);
return myList;
}
public void recursiveBFS (T node, T end, Set<T> visited, Map<T, T> path) {
visited.add(node);
for (Edge<T> e : graphRep.get(node)) {
if (e.node == end) {
path.put(e.node, node);
return;
}
if (!visited.contains(node)) {
//Here's where I'm stuck. I have no idea how to traverse the graph by breadth
}
}
}
How do I complete my breadth-first traversal method?

BFS requires a container that will allow to retrieve nodes in the order they were visited. It can't be achieved with a Map. You need a Queue for that purpose (take a look carefully at the description of this algorithm).
Note that although BFS could be implemented recursively, the iterative approach is way better for this task.
Firstly, you need to create a queue and add a starting node into it. Then the queue will be passed as an argument to the recursiveBFS().
At each call of the recursiveBFS() a node at the beginning of the queue will be removed. If the queue is empty that will mean that the start-node and end-node are not connected.
That is how recursive implementation might look like:
public List<T> breadthFirstSearch(T start, T end) {
Map<T, T> paths = new HashMap<>();
Queue<T> queue = new ArrayDeque<>();
queue.add(start);
recursiveBFS(end, new HashSet<>(), queue, paths);
return getPath(start, end, paths);
}
public void recursiveBFS(T end, Set<T> visited, Queue<T> queue, Map<T, T> paths) {
if (queue.isEmpty()) { // start-node and end-node are not connected
return;
}
T parentNode = queue.remove();
visited.add(parentNode);
for (Edge<T> edge : graphRep.get(parentNode)) { // avoid one-letter variables like "e" instead of edge
if (visited.contains(parentNode)) {
continue;
}
paths.put(edge.node, parentNode);
// end node was found
if (edge.node.equals(end)) { // don't compare object with "=="
return;
}
recursiveBFS(end, visited, queue, paths); // this line was missing
}
}
In order to make this solution adhere to the Single responsibility principle I extracted the logic for restoring the path from the start-node to end-node from the breadthFirstSearch() into the separate method.
public List<T> getPath(T start, T end, Map<T, T> paths) {
List<T> path = new ArrayList<T>();
T current = end;
path.add(current);
while (current != start && current != null) { // if there's no path from start to end current eventually will become null
path.add(paths.get(current));
current = paths.get(current);
}
System.out.println(path);
Collections.reverse(path);
return current != null ? path : Collections.emptyList();
}
Recommendations:
The most important I want to point out is the overall design of your graph. While traversing the graph you heavily rely on the Map<T, List<Edge<T>>> graphRep, edges are helpless without it. You might consider refining your graph so that its elements will be more self-contained. Firstly, in my opinion, the edge of a graph has to have two references because by definition it is meant to represent a connection between two vertices of the graph. And if you add a Vertex class to your graph then will contain reference a collection of edges then you can implement graph traversal algorithms using only edges and vertexes without a need to fall back on graphRep.
don't compare object with ==, use equals() method instead.
avoid one-letter variables like e.
don't name like myList, but try to come up with the name that explains the purpose of this variable (like path).
Update
Below is an iterative implementation of BFS:
public List<T> breadthFirstSearch(T start, T end) {
Map<T, T> paths = new HashMap<>();
Set<T> visited = new HashSet<>();
Queue<T> queue = new ArrayDeque<>();
queue.add(start);
visited.add(start);
boolean isFound = false;
while (!isFound && !queue.isEmpty()) {
T parentNode = queue.remove();
for (Edge<T> edge : graphRep.get(parentNode)) {
if (!visited.add(edge.node)) {
continue;
}
paths.put(edge.node, parentNode);
// end node was found
if (edge.node.equals(end)) {
isFound = true;
break;
}
}
}
return getPath(start, end, paths);
}
An iterative solution would be cleaner if you take into account recommendation above. And since for BFS as well as for DFS we don't need any information specific to edges (because vertex (node) can store data about adjusent vertexes) these algorithms could be implemented using vertecies only.

Related

Stream Spliterator error after concat Java

When attempting to create an iterator from a Stream that was concatenated from two previous streams, I get a NoSuchElementException, as the iterator doesn't recognise that the Stream has elements due to some problems with the Spliterator. The concatenated Stream seems to have two spliterators, despite the previous streams being of the same type. I also get this error when I tried to convert the concatenated stream into an array to try get round the problem.
Stream<Node> nodeStream = Stream.of(firstNode);
while (!goal) {
Iterator<Node> iterator = nodeStream.iterator();
Node head = iterator.next();
Stream<Node> tail = Stream.generate(iterator::next).filter(n -> n != head);
Node[] newNodes = head.expand(end);
if (newNodes.length == 1) {
goal = true;
endNode = newNodes[0];
}
nodeStream = Stream.concat(Arrays.stream(newNodes), tail);
nodeStream = nodeStream.sorted(Comparator.comparing(n -> n.routeCost(end)));
}
The error is as follows:
Exception in thread "main" java.util.NoSuchElementException
at java.base/java.util.Spliterators$1Adapter.next(Spliterators.java:688)
at java.base/java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1358)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:292)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:206)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:161)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:298)
at java.base/java.util.stream.Streams$ConcatSpliterator.tryAdvance(Streams.java:723)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.lambda$initPartialTraversalState$0(StreamSpliterators.java:292)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.fillBuffer(StreamSpliterators.java:206)
at java.base/java.util.stream.StreamSpliterators$AbstractWrappingSpliterator.doAdvance(StreamSpliterators.java:161)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.tryAdvance(StreamSpliterators.java:298)
at java.base/java.util.Spliterators$1Adapter.hasNext(Spliterators.java:681)
at java.base/java.util.Spliterators$1Adapter.next(Spliterators.java:687)
at inf.ed.ac.uk.Route.generateRoute(Route.java:35)
I am trying to expand the first node (returns 16 new nodes) add them to the stream, sort it, and repeat, this is part of an implementation of the A* algorithm on gps coordinates
Code for Node is
public class Node {
boolean goal = false;
Node parent;
final LngLat coords;
LngLat.Compass direction;
double cost;
private Route route;
public Node[] expand(LngLat end) {
ArrayList<Node> nodes = new ArrayList<>();
for (LngLat.Compass direction: LngLat.Compass.values()) {
Node node = new Node(coords.nextPosition(direction), this, direction);
if (noFlyClear(node)) {
if (!route.contains(node)) {
if (node.coords.closeTo(end)) {
node.goal = true;
return new Node[]{node};
}
nodes.add(node);
route.visited.add(node);
}
}
}
return nodes.toArray(Node[]::new);
}
But that's since I changed to ArrayLists
I'm still unsure what the issue was originally

Breadth-first search from one node to another

I'm implementing my own graph class, and I'm currently making my own BFS-search method. Right now it traverses all vertices from one root vertex.
public List<T> breadthFirstSearch(T start, T end) {
List<T> visited = new ArrayList<T>();
Queue<T> path = new LinkedList<T>();
visited.add(start);
path.add(start);
while (!path.isEmpty()){
T currentNode = path.poll();
for (Edge<T> edge: graphRep.get(currentNode)) {
if (!visited.contains(edge.node)) {
visited.add(edge.node);
path.add(edge.node);
}
}
}
System.out.println(visited);
return visited;
}
What I want to do is to find the path from vertex start to vertex end, but right now it finds the path between the start to all nodes. How do I change my code so that it only finds the path between the start to end?
There are several mistakes in your solution:
you not checking whether you find the target node;
case, when it is not possible to reach the end from the start node, is not covered in your solution;
the list visited will contain all a sequence of visited nodes, but not a path from the start node to end node;
method contains() costs O(n) for a list, you definitely have to use a HashSet for that purpose;
ArrayDeque will perform better than LinkedList (technically it's not a mistake but rather a strong recommendation).
So to fix your code you need to add a check whether the node to which points the current edge is equal to the end node and a boolean flag to break out from the loop (there's no need to do farther iterations).
In the code below HashMap paths is used for two purposes:
to track the parent node for each visited node in order to restore the path from start to end;
to check whether a new node is already visited.
Method getPath() will either return list nodes that represents a direct path from start to end an empty list if the path doesn't exist.
public List<T> breadthFirstSearch(T start, T end) {
Map<T, T> paths = new HashMap<>();
Queue<T> queue = new ArrayDeque<>();
queue.add(start);
paths.put(start, null);
boolean isFound = false;
while (!isFound && !queue.isEmpty()) {
T currentNode = queue.remove();
for (Edge<T> edge : graphRep.get(currentNode)) {
if (paths.containsKey(edge.node)) {
continue;
}
paths.put(edge.node, currentNode);
// end node was found
if (edge.node.equals(end)) {
isFound = true;
break;
}
}
}
return getPath(start, end, paths);
}
public List<T> getPath(T start, T end, Map<T, T> paths) {
List<T> path = new ArrayList<T>();
T current = end;
path.add(current);
while (current != start && current != null) { // if there's no path from start to end current eventually will become null
path.add(paths.get(current));
current = paths.get(current);
}
System.out.println(path);
Collections.reverse(path);
return current != null ? path : Collections.emptyList();
}

Data structures get maximum value at each level of N-ary tree

Lets say I have a n-ary tree something like below I need to find maximum value at each level and return like :
[8,7,32] .
8
4 3 7
1 4 3 3 5 6 7 12 32 3 1
My Node will look something like below :
public class Node {
public int val;
public List<Node> children;
public Node() {
}
public Node(int _val,List<Node> _children) {
val=_val;
children=_children;
}
I tried through recursion at each level get the elements and find the maximum but unable to do so.
We can get the level-maximum by a level order traversal / Breadth-first search. The idea is that we have a list/queue of nodes on one level. For all nodes in this list the algorithm does two things:
It calculates the maximum value on this level.
It iterates over all nodes of the list/queue, gets all children of those nodes and put them in a new list/queue, which it can then process in the next iteration.
The algorithm starts with a list/queue holding the root of the (sub)-tree and ends when the list/queue is empty.
This can be expressed nicely with Stream operations:
public static List<Integer> getMaxValuePerLevel(Node node) {
final ArrayList<Integer> maxPerLevel = new ArrayList();
maxPerLevel.add(node.getValue());
List<Node> children = node.getChildren();
while (!children.isEmpty()) {
maxPerLevel.add(children.stream()
.mapToInt(Node::getValue)
.max()
.getAsInt());
children = children.stream()
.map(Node::getChildren)
.flatMap(List::stream)
.collect(Collectors.toList());
}
return maxPerLevel;
}
Ideone demo
This implementation has two nice properties:
It is iterative, not recursive, i.e. the algorithm is not subject to a StackOverflowError
It has linear time- and memory complexity
With a little bit of effort, we are even able to make the algorithm work with generic Node<T extends Comparable<T>>:
public static <T extends Comparable<T>> List<T> getMaxValuePerLevel(Node<T> node) {
final ArrayList<T> maxPerLevel = new ArrayList<>();
maxPerLevel.add(node.getValue());
List<Node<T>> children = node.getChildren();
while (!children.isEmpty()) {
final Node<T> defaultNode = children.get(0);
maxPerLevel.add(children.stream()
.map(Node::getValue)
.max(Comparator.naturalOrder())
.orElseGet(defaultNode::getValue));
children = children.stream()
.map(Node::getChildren)
.flatMap(List::stream)
.collect(Collectors.toList());
}
return maxPerLevel;
}
Ideone demo
The root node is going to be the highest of its level. For the subsequent levels, call Collections.sort() (or any other comparison that will order your list) on the list of children nodes and take the last element (or whichever has the highest value according to the sorting method you used). Then iterate through the list of children nodes that you just sorted and for each node, apply the same treatment to its list of children.
A recursive solution is surprisingly simple. First create a list to hold the result. Then iterate through all the nodes: at each node you compare the node's value with the value in the list at the same level. If the node's value is greater, you replace the value in the list.
class Node {
public int val;
public List<Node> children;
public Node(int _val, List<Node> _children) {
val = _val;
children = _children;
}
public List<Integer> getMaxPerLevel() {
List<Integer> levels = new ArrayList<>();
getMaxPerLevel(0, levels);
return levels;
}
private void getMaxPerLevel(int level, List<Integer> levels) {
if (level >= levels.size()) {
levels.add(level, val);
} else {
levels.set(level, Math.max(val, levels.get(level)));
}
for (Node child : children) {
child.getMaxPerLevel(level + 1, levels);
}
}
}
Thanks everyone I did using below solution:
public List<Integer> levelOrder(Node node){
List<Integer> result = new ArrayList<>();
Queue<Node> queue = new LinkedList<Node>();
queue.add(node);
while(!queue.isEmpty()) {
int size = queue.size();
List<Integer> currentLevel = new ArrayList<Integer>();
for(int i=0;i<size;i++) {
Node current = queue.remove();
currentLevel.add(current.val);
for(Integer inte:currentLevel) {
System.out.println(inte);
}
if(current.children !=null) {
for(Node node1:current.children)
queue.add(node1);
}
}
result.add(Collections.max(currentLevel));
}
return result;
}

Given K sorted lists of up to N elements in each list, return a sorted iterator over all the items

Example: List 1: [1, 4, 5, 8, 9]
List 2: [3, 4, 4, 6]
List 3: [0, 2, 8]
Would yield the following result:
Iterator -> [0, 1, 2, 3, 4, 4, 4, 5, 6, 8, 8, 9]
I am reluctant to create a "merge" method that accepts the k lists and merges the contents of the List to another List in the spirit of space complexity. Is this a k-way merge problem that can be implemented using "min Heap". Any pointers would be very helpful.
public class CustomListIterator<E> implements Iterator<E>{
private boolean canAddIterators = true;
private boolean balanceTreeIteratorFlag = false;
private E f_element;
private E s_element;
private Iterator<E> left;
private Iterator<E> right;
private final Comparator<E> comparator;
public CustomListIterator(Comparator<E> comparator){
this.comparator = comparator;
}
public CustomListIterator(Iterator<E> left, Iterator<E> right, Comparator<E> comparator){
this.left = left;
this.right = right;
this.comparator = comparator;
}
public void addIterator(Iterator<E> iterator){
if (!canAddIterators)
throw new ConcurrentModificationException();
if (right == null){
right = iterator;
return;
}else if (left == null){
left = iterator;
return;
}
if (!balanceTreeIteratorFlag){
right = balanceTreeOfIterators(iterator, right);
}else{
left = balanceTreeOfIterators(iterator, left);
}
balanceTreeIteratorFlag = !balanceTreeIteratorFlag;
}
private Iterator<E> balanceTreeOfIterators(Iterator<E> iterator_1, Iterator<E> iterator_2){
if (iterator_2 instanceof CustomListIterator){
((CustomListIterator<E>)iterator_2).addIterator(iterator_1);
} else{
iterator_2 = new CustomListIterator<E>(iterator_1, iterator_2, comparator);
}
return iterator_2;
}
public boolean hasNext() {
if (canAddIterators){
if (left != null && left.hasNext()){
f_element = left.next();
}
if (right != null && right.hasNext()){
s_element = right.next();
}
}
canAddIterators = false;
return f_element != null || s_element != null;
}
public E next() {
E next;
if (canAddIterators){
if (left.hasNext()){
f_element = left.next();
}
if (right.hasNext()){
s_element = right.next();
}
}
canAddIterators = false;
if (s_element == null && f_element == null){
throw new NoSuchElementException();
}
if (f_element == null){
next = s_element;
s_element = right.hasNext() ? right.next() : null;
return next;
}
if (s_element == null){
next = f_element;
f_element = left.hasNext() ? left.next() : null;
return next;
}
return findNext();
}
public void remove() {
}
private E findNext(){
E next;
if (comparator.compare(f_element, s_element) < 0){
next = f_element;
f_element = left.hasNext() ? left.next() : null;
return next;
}
next = s_element;
s_element = right.hasNext() ? right.next() : null;
return next;
}
}
I don't this is the most optimal way of doing it (using a tree). Any suggestions on how this can be implemented only by overriding next() hasNext() and remove()?
There are basically three different ways to merge multiple sorted lists:
Successive two-way merges
Divide and conquer
Priority queue based
In the discussion below, n refers to the total number of items in all lists combined. k refers to the number of lists.
Case 1 is the easiest to envision, but also the least efficient. Imagine you're given four lists, A, B, C, and D. With this method, you merge A and B to create AB. Then you merge AB and C to create ABC. Finally, you merge ABC with D to create ABCD. The complexity of this algorithm approaches O(n*k). You iterate over A and B three times, C two times, and D one time.
The divide and conquer solution is to merge A and B to create AB. Then merge C and D to create CD. Then merge AB and CD to create ABCD. In the best case, which occurs when the lists have similar numbers of items, this method is O(n * log(k)). But if the lists' lengths vary widely, this algorithm's running time can approach O(n*k).
For more information about these two algorithms, see my blog entry, A closer look at pairwise merging. For more details about the divide and conquer approach specifically, see A different way to merge multiple lists.
The priority queue based merge works as follows:
Create a priority queue to hold the iterator for each list
while the priority queue is not empty
Remove the iterator that references the smallest current number
Output the referenced value
If not at end of iterator
Add the iterator back to the queue
This algorithm is proven to be O(n * log(k)) in the worst case. You can see that every item in every list is added to the priority queue exactly once, and removed from the priority queue exactly once. But the queue only contains k items at any time. So the memory requirements are very small.
The implementation of iterators in Java makes the priority queue implementation slightly inconvenient, but it's easily fixed with some helper classes. Most importantly, we need an iterator that lets us peek at the next item without consuming it. I call this a PeekableIterator, which looks like this:
// PeekableIterator is an iterator that lets us peek at the next item
// without consuming it.
public class PeekableIterator<E> implements Iterator<E> {
private final Iterator<E> iterator;
private E current;
private boolean hasCurrent;
public PeekableIterator(Iterator<E> iterator) {
this.iterator = iterator;
if (iterator.hasNext()) {
current = iterator.next();
hasCurrent = true;
}
else {
hasCurrent = false;
}
}
public E getCurrent() {
// TODO: Check for current item
return current;
}
public boolean hasNext() {
return hasCurrent;
}
public E next() {
// TODO: Error check to see if there is a current
E rslt = current;
if (iterator.hasNext()) {
current = iterator.next();
}
else {
hasCurrent = false;
}
return rslt;
}
public void remove() {
iterator.remove();
}
Then, since the priority queue will hold iterators rather than individual items, we need a comparator that will compare the current items of two PeekableIterator interfaces. That's easy enough to create:
// IteratorComparator lets us compare the next items for two PeekableIterator instances.
public class IteratorComparator<E> implements Comparator<PeekableIterator<E>> {
private final Comparator<E> comparator;
public IteratorComparator(Comparator<E> comparator) {
this.comparator = comparator;
}
public int compare(PeekableIterator<E> t1, PeekableIterator<E> t2) {
int rslt = comparator.compare(t1.getCurrent(), t2.getCurrent());
return rslt;
}
}
Those two classes are more formal implementations of the code you wrote to get and compare the next items for individual iterators.
Finally, the MergeIterator initializes a PriorityQueue<PeekableIterator> so that you can call the hasNext and next methods to iterate over the merged lists:
// MergeIterator merges items from multiple sorted iterators
// to produce a single sorted sequence.
public class MergeIterator<E> implements Iterator<E> {
private final IteratorComparator<E> comparator;
private final PriorityQueue<PeekableIterator<E>> pqueue;
// call with an array or list of sequences to merge
public MergeIterator(List<Iterator<E>> iterators, Comparator<E> comparator) {
this.comparator = new IteratorComparator<E>(comparator);
// initial capacity set to 11 because that's the default,
// and there's no constructor that lets me supply a comparator without the capacity.
pqueue = new PriorityQueue<PeekableIterator<E>>(11, this.comparator);
// add iterators to the priority queue
for (Iterator<E> iterator : iterators) {
// but only if the iterator actually has items
if (iterator.hasNext())
{
pqueue.offer(new PeekableIterator(iterator));
}
}
}
public boolean hasNext() {
return pqueue.size() > 0;
}
public E next() {
PeekableIterator<E> iterator = pqueue.poll();
E rslt = iterator.next();
if (iterator.hasNext()) {
pqueue.offer(iterator);
}
return rslt;
}
public void remove() {
// TODO: Throw UnsupportedOperationException
}
}
I've created a little test program to demonstrate how this works:
private void DoIt() {
String[] a1 = new String[] {"apple", "cherry", "grape", "peach", "strawberry"};
String[] a2 = new String[] {"banana", "fig", "orange"};
String[] a3 = new String[] {"cherry", "kumquat", "pear", "pineapple"};
// create an ArrayList of iterators that we can pass to the
// MergeIterator constructor.
ArrayList<Iterator<String>> iterators = new ArrayList<Iterator<String>> (
Arrays.asList(
Arrays.asList(a1).iterator(),
Arrays.asList(a2).iterator(),
Arrays.asList(a3).iterator())
);
// String.CASE_INSENSITIVE_ORDER is a Java 8 way to get
// a String comparator. If there's a better way to do this,
// I don't know what it is.
MergeIterator<String> merger = new MergeIterator(iterators, String.CASE_INSENSITIVE_ORDER);
while (merger.hasNext())
{
String s = merger.next();
System.out.println(s);
}
}
My performance comparisons of the divide-and-conquer and priority queue merges shows that the divide-and-conquer approach can be faster than using the priority queue, depending on the cost of comparisons. When comparisons are cheap (primitive types, for example), the pairwise merge is faster even though it does more work. As key comparisons become more expensive (like comparing strings), the priority queue merge has the advantage because it performs fewer comparisons.
More importantly, the pairwise merge requires twice the memory of the priority queue approach. My implementation used a FIFO queue, but even if I built a tree the pairwise merge would require more memory. Also, as your code shows, you still need the PeekableIterator and IteratorComparator classes (or something similar) if you want to implement the pairwise merge.
See Testing merge performance for more details about the relative performance of these two methods.
For the reasons I detailed above, I conclude that the priority queue merge is the best way to go.

Java tree structure with multiple children (sorted) at each level

I'm working with a flat List of objects, which nevertheless are associated with each other in parent-child relationships. An object may have any number of children, or none at all. I need to display these objects as a tree, showing those relationships. Each level of the tree should be sorted (the objects are compatible with Collections.sort() ).
The question is two-part:
Does Java have a good out-of-the-box data structure for holding such a tree, or do I need to write one from scratch? (not a huge task, but there's no sense in reinventing the wheel) I know about DefaultTreeModel in Swing... but this application is running on the server-side, and use of the Swing package will get frowned upon in code review.
What would be the best pattern for loading a flat List into such a data-structure? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc. However, for the requirement of sorting the peers at each level in the tree... I'm not sure if it makes more sense to worry about this when I'm building the tree, or worry about it later when I'm parsing the tree for display.
Here is a quick-and-dirty Tree implementation that uses TreeSets on all levels (you can supply a comparator, or natural ordering will be used):
public class Tree<T> {
private final Node<T> rootElement;
public void visitNodes(final NodeVisitor<T> visitor){
doVisit(rootElement, visitor);
}
private static <T> boolean doVisit(final Node<T> node,
final NodeVisitor<T> visitor){
boolean result = visitor.visit(node);
if(result){
for(final Node<T> subNode : node.children){
if(!doVisit(subNode, visitor)){
result = false;
break;
}
}
}
return result;
}
public interface NodeVisitor<T> {
boolean visit(Node<T> node);
}
public Node<T> getRootElement(){
return rootElement;
}
private static final class NodeComparator<T> implements Comparator<Node<T>>{
private final Comparator<T> wrapped;
#Override
public int compare(final Node<T> o1, final Node<T> o2){
return wrapped.compare(o1.value, o2.value);
}
public NodeComparator(final Comparator<T> wrappedComparator){
this.wrapped = wrappedComparator;
}
}
public static class Node<T> {
private final SortedSet<Node<T>> children;
private final Node<T> parent;
private T value;
private final Comparator<?> comparator;
#SuppressWarnings("unchecked")
Node(final T value, final Node<T> parent, final Comparator<?> comparator){
this.value = value;
this.parent = parent;
this.comparator = comparator;
children =
new TreeSet<Node<T>>(new NodeComparator<T>((Comparator<T>) comparator));
}
public List<Node<T>> getChildren(){
return new ArrayList<Node<T>>(children);
}
public Node<T> getParent(){
return parent;
}
public T getValue(){
return value;
}
public void setValue(final T value){
this.value = value;
}
public Node<T> addChild(final T value){
final Node<T> node = new Node<T>(value, this, comparator);
return children.add(node) ? node : null;
}
}
#SuppressWarnings("rawtypes")
private static final Comparator NATURAL_ORDER = new Comparator(){
#SuppressWarnings("unchecked")
#Override
public int compare(final Object o1, final Object o2){
return ((Comparable) o1).compareTo(o2);
}
};
private final Comparator<?> comparator;
public Tree(){
this(null, null);
}
public Tree(final Comparator<? super T> comparator){
this(comparator, null);
}
public Tree(final Comparator<? super T> comparator, final T rootValue){
this.comparator = comparator == null ? NATURAL_ORDER : comparator;
this.rootElement = new Node<T>(rootValue, null, this.comparator);
}
public Tree(final T rootValue){
this(null, rootValue);
}
}
Here is some sample code against it:
final Tree<Integer> tree = new Tree<Integer>();
final Node<Integer> rootNode = tree.getRootElement();
rootNode.setValue(1);
final Node<Integer> childNode = rootNode.addChild(2);
final Node<Integer> newChildNode = rootNode.addChild(3);
newChildNode.addChild(4);
tree.visitNodes(new NodeVisitor<Integer>(){
#Override
public boolean visit(final Node<Integer> node){
final StringBuilder sb = new StringBuilder();
Node<Integer> curr = node;
do{
if(sb.length() > 0){
sb.insert(0, " > ");
}
sb.insert(0, String.valueOf(curr.getValue()));
curr = curr.getParent();
} while(curr != null);
System.out.println(sb);
return true;
}
});
Output:
1
1 > 2
1 > 3
1 > 3 > 4
What would be the best pattern for loading a flat List into such a data-structure? My first thought is to identify the root-level objects, and then use a recursive method to traverse down through their children, grandchildren, etc.
If I understand correctly, you only have a flat list, without any concrete associations between its elements, and you can detect somehow whether a particular element is the child of another.
In this case, you could
sort the list
(identify the root node, if it is not known yet)
put the root into a queue
take the first node from the queue
starting from the first element of the list, check each element whether it is a child of the current node; if so, add it to the current level of the tree and put it into the queue
repeat from step 4.
If detecting parent-child relationship is costly, you could improve performance by storing a flag for / nulling out each node whose location within the tree is already identified, so that you can jump over them when traversing the list. Alternatively, you may copy the whole sorted list into a linked list so that it is trivial to remove processed elements from it.
There are no tree structures in Java, but there are sorted ones: TreeSet and TreeMap. See for some hints java data-structure to simulate a data tree
The approach you came up with is what I would do.
How to go about building the tree really depends on what information you have in the initial List.
If each node contains a reference to its parent and a collection of its children, you don't need to build anything other than the root set.
If each node only has a reference to its parent, you do need to build a tree; but you can do it in a single pass over the data using a HashMap to map each node to a list (which you build) of its children.
If the nodes don't even contain a reference to their parents, you'll have to do what Péter suggests.
In any case, I wouldn't bother sorting the whole List first. Sorting a large List will be slower than sorting lots of little ones with the same total length. (This follows from sorting being O(n log n).)

Categories

Resources