I have JXTreeTable which I can filter. When I filter a keyword a new model is set. It works ok for me.
Now I want to expand all the filtered results. For that I save the position where the matching node was found. (It cant be a leaf.)
For that positions I create a List of TreePaths with this method:
public TreePath getPath(TreeNode node) {
List<TreeNode> list = new ArrayList<TreeNode>();
while (node != null) {
list.add(node);
node = node.getParent();
}
Collections.reverse(list);
return new TreePath(list.toArray());
}
Now I iterate over this list and call expandPath (after the new model was set)
Where getTreePaths() is a list of TreePaths which I created with the method before.
for (TreePath elem : f.getTreePaths()) {
tree.expandPath(elem);
tree.scrollPathToVisible(elem);
}
But it has no effect, only the root is expanded and all the children are collapsed.
In the TreePath the last element is no leaf. In a System.out in this loop I get for all:
-1
true
true
true
System.out.println(tree.getRowForPath(elem)); System.out.println(f.isPathValid(elem,tree.getTreeTableModel()));
System.out.println(tree.isVisible(elem));
System.out.println(tree.isExpanded(elem));
The -1 for getRowForPath is maybe the problem?
isPathValid():
public boolean isPathValid(TreePath path,TreeTableModel model) {
if (path.getPathCount() == 0) {
return model.getRoot().equals(path.getPathComponent(0));
}
for (int x = 1; x < path.getPathCount(); x++) {
if (model.getIndexOfChild(path.getPathComponent(x - 1),
path.getPathComponent(x)) == -1) {
return false;
}
}
return true;
}
I know it is an old post, but I'll drop here my findings after spending an hour in a similar problem.
The method "isPathValid" only validates if the sequence is correct by checking if the child (x) belongs to the parent (x - 1), but it doesn't validate if the root is the same as the table model (unless the path count is zero).
When filtering, you usually trigger a new root to the table model, so if you captured the TreePaths "before" updating the model (and triggering a new root), the table won't be able to find the path, although the path itself is valid.
It should work with a few of changes:
public **List** getPath(TreeNode node) {
List<TreeNode> list = new ArrayList<TreeNode>();
**while (node != model.getRoot()) {**
list.add(node);
node = node.getParent();
}
Collections.reverse(list);
**return list;**
}
**for (List elem : f.getTreePaths()) {**
elem.add(0, model.getRoot());
tree.expandPath(new TreePath(elem.toArray());
tree.scrollPathToVisible(elem);
}
Related
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();
}
Suppose we have a simple graph like this:
It was easy to find a path from start node to end node with depth-first search, but I got stuck while trying to do the same thing with breadth-first search. My code is as follows:
public List<String> getPathBreadth(String name1, String name2) {
Node node1 = getNode(name1);
Node node2 = getNode(name2);
if (node1 == null || node2 == null) {
return null;
}
return getPathBreadth(node1, node2, new HashSet<String>(), new LinkedList<Node>());
}
private List<String> getPathBreadth(Node start, Node destination, HashSet<String> visited, Queue<Node> queue) {
List<String> path = new ArrayList<String>();
if (start == destination) {
path.add(start.name);
return path;
}
visited.add(start.name);
queue.offer(start);
while (queue.size() > 0) {
Node cur = queue.poll();
for (String friend : cur.friends) {
if (visited.contains(friend)) {
continue;
}
visited.add(friend);
if (getNode(friend) == destination) {
path.add(friend); // I got the final node, I could also add cur, but how do I get the previous nodes along the path
return path;
}
queue.offer(getNode(friend));
}
}
return path;
}
Suppose we want to go from John to Linda, so I wish to return something like [Linda, Robert, John] or [Linda, Patrica, John], but the best I can do for now is get the last and second to the last nodes. In this case they are Linda and Robert. How do I get all the previous nodes along the path?
To make the code usable please add Node definition, and tree (test data).
(see mcve ).
I think the problem is here :
if (getNode(friend) == destination) {
path.add(friend);
return path;
}
The only node you add to the path in the last one. Try:
path.add(friend);
if (getNode(friend) == destination) {
return path; //or better: break;
}
Unfortunately I can't run and check it.
A side note:
visited.add(friend) Returns true if this set did not already contain friend
so:
if (visited.contains(friend)) {
continue;
}
visited.add(friend);
can be replaced with
if (! visited.add(friend)) {
continue;
}
I have a List of Nodes which contains nested list of child nodes. I am trying to iterate through all of them to find a particular node.Currently I start at the child nodes from the root level, then go one level deep to sub child node and so on using for-each loop.
This is my code:
List<Node> children = root.getChildren();
boolean found = false;
while (!found) {
for (Node node : children) {
if (!node.getData().toString().toUpperCase().contains("BRANCH")) {
if(condition){//some processing}
} else {
//swap children with sub children
if (children.get(0) != null) {
children = children.get(0).getChildren(); // this operation is not possible during iteration
}
}
} else {
continue;
}
}
}
}
If child node doesn't find any match, then I need to swap the collection with sub child node and continue iteration and so on.
Is there a better way to iterate through a nested nodelist of children?
Instead of swapping the collections, you could add the elements to a queue, and keep iterating till the queue is empty (i.e. you didn't find a match). Or you do find a match and return early.
public static void algorithm(Node root) {
Queue<Node> q = new LinkedList<>();
q.add(root);
while(!q.isEmpty()) {
Node current = q.poll();
if(current .getData().toString().toUpperCase().contains("BRANCH")) {
continue;
}
if(condition){
//some processing
return;
} else {
q.addAll(current.getChildren());
}
}
}
algorithm(root);
You can't swap like this mid-iteration. Remember that your for loop is translated in Java like this:
for (Iterator<Node> it = children.iterator(); it.hasNext(); ) {
Node node = it.next();
// The rest of it
}
So even if you change what children is, your iterator stays as it is.
I would suggest using a Queue to help you here.
PS Do you really want to skip all of the non-first children? That appears to be what you're currently doing.
I have simple code which prints path to a specific node in a tree. My implemention using java String is as below
//using strings
public static void getPathS(Node node,String path,int key){
if (node == null) {
return;
} else if(node.data == key) {
System.out.println(path+" "+key);;
}
getPathS(node.left,path+" "+node.data,key);
getPathS(node.right,path+" "+node.data,key);
}
Suppose there is tree as give below,
if I call getPathS on 3 , Above implementation will print
1 34 3 //path from root to the element
If I implement same method using ArrayList as below
public static List getPath(Node node, List<Integer> path, int key) {
if (node == null) {
//1 . path = new ArrayList<Integer>();
path = new ArrayList<Integer>();
// 2. or tried path.clear() -- it should clear the path
//return path;
return null;
} else if (node.data == key) {
path.add(node.data);
return path;
}
path.add(node.data);
return nonNull(getPath(node.left, path, key), getPath(node.right, path, key));
}
private List nonNull(List path1, List path2) {
if (path1 != null)
return path1;
if(path2 !=null )
return path2;
return null;
}
// class Node { Node left, Node right , int data; };
//Code to call getPath
Node node = new Node(1);
node.left = new Node(2);
node.left.left = new Node(4);
node.right = new Node(34);
node.right.right = new Node(3);
System.out.println(getPath(node, new ArrayList(), 3));
In second implementation, I tried two approaches, when we get NULL node, in 1st approach if I assign new ArrayList to path, it prints all the elements i.e.
[1, 2, 4, 34, 3]
If I use path.clear(), it only prints last element i.e. element to be searched.
How can we make sure ArrayList will work as String in recursion ?
The problem here is that you don't consider failure for both branches with your call to nonNull().
Here is a correction that takes into account that possibility, and removes the data of the current node if we failed to find the key in its children.
public static List<Integer> getPath(Node node, List<Integer> path, int key) {
if (node == null) {
return null;
} else if (node.data == key) {
path.add(node.data);
return path;
}
path.add(node.data);
// path is unchanged if nothing is found in left children
if (getPath(node.left, path, key) != null || getPath(node.right, path, key) != null) {
// found in one branch or the other
return path;
}
// not found in either branch, remove our data
path.remove(path.size() - 1);
return null;
}
Of course, it looks like we're manipulating different lists, but there is only one: the one provided as argument the first time. That's why data should be removed from it. You need to be clear about your arguments.
A cleaner solution, that emphasizes the fact that there is one list only.
/**
* Appends to the specified list all keys from {#code node} to the {#link Node} containing the
* specified {#code key}. If the key is not found in the specified node's children, the list is
* guaranteed to be unchanged. If the key is found among the children, then the specified list
* will contain the new elements (in addition to the old ones).
*
* #param node
* the node to start at
* #param path
* the current path to append data to
* #param key
* the key to stop at
* #return true if the key was found among the specified node's children, false otherwise
*/
public static boolean getPath(Node node, List<Integer> path, int key) {
if (node == null) {
// leaf reached, and the key was not found
return false;
}
// add data to the path
path.add(node.data);
// the OR is lazy here, so we treat everything in the given order
// if getPath failed on the left children, path is unchanged and used for right children
if (node.data == key || getPath(node.left, path, key) || getPath(node.right, path, key)) {
// the key is found in the current node, its left children, or its right children
return true;
}
// not found in either branch, remove our data
path.remove(path.size() - 1);
return false;
}
Note that I didn't use path.remove(node.data) because there could be more that one node with that data, and the first one would be removed instead of the last.
I have a problem with my code, I have made a singly linked list class in which you can add, remove, modify, merge etc... however, I am attempting a simple bubble sort and have come across problems in which the list is not correctly sorted. here are some things to note:
it is a custom implementation of a linked list
the nodes of the singly linked list contain 2 things: a CustomerFile object with all the data for a customer and a 'next' node pointer to the next item in the list
the list is sorted in ascending order (A-Z) by the surname stored in the customer file of each node
the add record function inserts the nodes at the correct position in the list so that the list does not need to be sorted initially - however if the surname is changed, as part of the program, the list needs to be sorted again
I would rather not create a new list and re-use this insert record on that list to create a new list as this is memory intensive and my task is to be as efficient as possible
the very structure of the linked list cannot be changed - it is decided and I am too far to change to something such as an array
the list has a head node, it has next items but does not have a tail node. It has an appointed NULL next pointer to indicate the end pf the list
the code
public static void sortList()
{
if (isEmpty() == true)
{
System.out.println("Cannot sort - the list is empty");
}
else if (getHead().getNext() == null)
{
System.out.println("List sorted");
}
else
{
Node current = getHead().getNext();
CustomerFile tempDat;
boolean swapDone = true;
while (swapDone)
{
current = getHead().getNext();
swapDone = false;
while (current != null)
{
if (current.getNext() != null &&
current.getData().getSurname().compareTo(
current.getNext().getData().getSurname()) >0)
{
tempDat = current.getData();
current.setData(current.getNext().getData());
current.getNext().setData(tempDat);
swapDone = true;
}
current = current.getNext();
}
}
if (getHead().getData().getSurname().compareTo(
getHead().getNext().getData().getSurname()) >0)
{
current = getHead().getNext();
getHead().setNext(current.getNext());
setHead(current);
}
}
}
I would appreciate the feedback
Your code is an odd mixture, mostly swapping data but treating the head specially trying to swap it with the next by switching the links.
As noted in another answer, this last swap is not correctly implemented, but really there's no reason to treat the head specially if you're doing things by swapping the data.
All you have to do is start each traversal of the list at the head in the outer loop.
This yields a working solution:
public void sortList()
{
if (isEmpty())
{
System.out.println("An empty list is already sorted");
}
else if (getHead().getNext() == null)
{
System.out.println("A one-element list is already sorted");
}
else
{
Node current = getHead();
boolean swapDone = true;
while (swapDone)
{
swapDone = false;
while (current != null)
{
if (current.getNext() != null && current.getData().getSurname().compareTo(current.getNext().getData().getSurname()) >0)
{
CustomerFile tempDat = current.getData();
current.setData(current.getNext().getData());
current.getNext().setData(tempDat);
swapDone = true;
}
current = current.getNext();
}
current = getHead();
}
}
}
I've made this non-static because as noted in comments it wasn't clear to me how yours could work as a static. You may be able to make it static in your context.
I also changed a couple other minor things. It's never necessary to check a condition by code like
if (isEmpty() == true)
In Java, this is entirely equivalent to
if (isEmpty())
And tempDat doesn't need to be declared outside of where it's used.
I think the main issue here is that you start with
current = getHead().getNext()
With this code you will leave the head completely out of the sorting process and it could be the case that the data in here needs to go to the other end of the list, but in this case it will always be either the data in the head element, or be the data directly after the head node (the latter is due to your if statement at the end).
Try starting from the head of the list instead and see what that brings up.
You are not including the head in your sorting.
public static void sortList()
{
if (isEmpty() == true)
{
System.out.println("Cannot sort - the list is empty");
}
else if (getHead().getNext() == null)
{
System.out.println("List sorted");
}
else
{
Node current = getHead();
// Node current = getHead().getNext();
CustomerFile tempDat;
boolean swapDone = true;
while (swapDone)
{
current = getHead().getNext();
swapDone = false;
while (current != null)
{
if (current.getNext() != null && current.getData().getSurname().compareTo(current.getNext().getData().getSurname()) >0)
{
tempDat = current.getData(); // td -> objectA, cd -> objectA
current.setData(current.getNext().getData()); // cd -> objectB
current.getNext().setData(tempDat); // nd -> td -> objectA
swapDone = true;
}
current = current.getNext();
}
}
//if (getHead().getData().getSurname().compareTo(getHead().getNext().getData().getSurname()) >0)
//{
// current = getHead().getNext(); // current -> head+1
// getHead().setNext(current.getNext()); //head+1 -> head+2
// setHead(current); // head -> head+1
//}
}
}
Also there is a error in the commented out code above. Applying that code drops a node.
// list: a -> b -> c -> d
current = getHead().getNext(); // current = b
getHead().setNext(current.getNext()); // a -> c
setHead(current); // head = b
// list: b -> c -> d
// and nothing points to 'a' anymore
Rather then swapping data you should try swapping nodes. If you go about that approach you should find a better solution.