Inserting an object into a quadtree in Java - java

I'm making a quadtree and need helping inserting an object into it. I understand the concept of doing it, but I'm not great with recursion and the way Java passes variables.
I have a Quadtree class which contains a Node called root. The Node class has four Nodes inside of it, which are the four quads that make it up. The root node is created when you create the quad tree, and the four nodes inside each node aren't created until I call createChildrenQuads(). However, for the root node, that method is called when you create the quad tree.
So my thought process on how to insert an item into a node is this:
Start at the root node
For each node:
Check and see which of the current node's children quad the current
item fits in
If it fits in one of them, insert it into that
node(call the recursive method and start over)
If it didn't fit into
any, add it to the current node's list of objects and be done
Based on that, I created this:
public void insert(Entity e) {
insert(root, e);
}
private void insert(Node n, Entity e){
Rectangle temp = e.getRectangle();
if (!n.childrenHaveBeenCreated())
n.createChildrenQuads(n.m_quadRect);
//first check which node it can go into
if ( n.NW.m_quadRect.contains(temp)) {
n.NW = insert(n.NW, e);
return;
}
if ( n.NE.m_quadRect.contains(temp)) {
n.NE = insert(n.NE, e);
return;
}
if ( n.SW.m_quadRect.contains(temp)) {
n.SW = insert(n.SW, e);
return;
}
if ( n.SE.m_quadRect.contains(temp)) {
n.SE = insert(n.SE, e);
return;
}
n.m_objects.add(e);
}
I think quadtree-wise, the logic I have there is fine. When I debug the code, it looks like everything works as it should. However, I believe my problem is that Java passes parameters by value and not reference, so while I add it at the correct spot, it doesn't get "saved" because it is just a local variable. I believe that is the problem, but I could be wrong.
So I tried changing it a bit to make it so that the insert method returns the current node, so that everything should get "saved" correctly. This is what I came up with:
public void insert(Entity e) {
root = insert(root, e);
}
private Node insert(Node n, Entity e) {
Rectangle temp = s.getRectangle();
if (!n.childrenHaveBeenCreated())
n.createChildrenQuads(n.m_quadRect);
//first check which node it can go into
if ( n.NW.m_quadRect.contains(temp)) {
n.NW = insert(n.NW, e);
return n;
}
if ( n.NE.m_quadRect.contains(temp)) {
n.NE = insert(n.NE, e);
return n;
}
if ( n.SW.m_quadRect.contains(temp)) {
n.SW = insert(n.SW, e);
return n;
}
if ( n.SE.m_quadRect.contains(temp)) {
n.SE = insert(n.SE, e);
return n;
}
n.m_objects.add(e);
return n;
}
However, I still have the same problem. Now I'm not sure what my problem is.
I'm using this for a game, and I have it so that it draws an outline around where all of the quads are, and I can click to add an Entity into the quadtree. Both versions of my code seem to act the same. What happens is when I add an entity to the quadtree, it gets added and stays there, so I guess my theory of it not getting "saved" because of how Java passes references is wrong.
However, my code should(at least in my mind should) place each entity into the quadtree to as low of a level as it can, creating new children quads in each node as it goes down the tree. What actually happens though, is that sometimes it seems to just add the new entity into the current quad, not going down the tree at all, or it goes down a level or two and that is it, when it can easily go down a few more levels.
Can anyone see anything wrong with the code or my logic at all?

I think it is the way you've structured your program. The quadtree gets to test every quadrant, but it also alway adds the element at the end... So even though it will recursively make it's way to the bottom, on the way back up it will always run your last n.m_objects.add(e);
therefore changing where it is added on the way back up through the recursion. You need to change it to more of an If (..) else if (...) else (...)

Related

Balance a binary search tree

I'm trying to implement a binary search tree class in Java with a method that can rebalance the tree if there's a difference in height. I'm trying to do it by first storing the value of the nodes in an List (an attribute of the class).
I then want to take the middle element of this list and assign this to the root of the tree. After this I take the left- and right part of the list and do the same thing recursively to the left- and right children of the root and so on.
My algorithm doesn't seem to work though and I don't understand what I'm doing wrong. I wonder if someone can take a look at my code and explain what the problem is? What I do is basically pass the ordered list of elements of the tree (an attribute of the class) and the root into the function below:
public void build(BinaryNode<E> n,List<E> list) {
int idx = (int)Math.floor(list.size()/2);
if(n!=null) {
n.element = list.get(idx);
} else if(n==null) {
n = new BinaryNode<E>(list.get(idx));
}
if(!list.subList(0,idx).isEmpty()) {
build(n.left,list.subList(0,idx));
}
if(!list.subList(idx+1,list.size()).isEmpty() ){
build(n.right,list.subList(idx+1,list.size()));
}
return;
}
Kind regards,
Java method calls are "call by value". This means changing a parameter (like n in your case) has no effect outside of the method.
Try to define your method like this
public BinaryNode<E> build(List<E> list) { ... }
Try investigating about AVL tree
Some useful links:
https://en.wikipedia.org/wiki/AVL_tree
https://www.geeksforgeeks.org/avl-tree-set-1-insertion/

I can't get to modify my static variable in java

You give a grid (4x4 here). you need to find out the total no of unique paths from (0,0) to (4,4). main() call a function pathify for this. It finds the possible "next steps" and calls it again. When (4,4) is reached noOfPaths++; is supposed to execute. This doesn't happen and I can't find the problem.
import java.util.ArrayList;
public class NoOfPaths {
static int xRows = 4;
static int yColumns = 4;
static int noOfPaths = 0;
/*A robot is located in the upper-left corner of a 4×4 grid.
* The robot can move either up, down, left, or right,
* but cannot go to the same location twice.
* The robot is trying to reach the lower-right corner of the grid.
* Your task is to find out the number of unique ways to reach the destination.
**/
static ArrayList validNeighbours (int x,int y, ArrayList visited) {
ArrayList valid = new ArrayList();
if((x+1 <= xRows) && !visited.contains(((x+1)*10)+y) ) {
valid.add(((x+1)*10)+y);
}
if((x-1 >= 0) && !visited.contains(((x-1)*10)+y) ) {
valid.add(((x-1)*10)+y);
}
if((y+1 <= yColumns) && !visited.contains(x*10+y+1) ) {
valid.add(x*10+y+1);
}
if((y-1 >= 0) && !visited.contains(x*10+y-1) ) {
valid.add(x*10+y-1);
}
return valid;
}
static void pathify(int x,int y, ArrayList alreadyVisited) {
if(x == xRows && y == yColumns) {
noOfPaths++;
} else {
alreadyVisited.add(x*10+y);
ArrayList callAgain = new ArrayList();
callAgain = validNeighbours(x,y,alreadyVisited);
for (int t=0,temp; t<callAgain.size(); t++) {
temp=(int) callAgain.get(t);
pathify(temp/10, temp%10, alreadyVisited);
}
}
}
public static void main(String[] args) {
ArrayList alreadyVisited = new ArrayList();
pathify(0, 0, alreadyVisited);
System.out.println(noOfPaths);
}
}
The error is in how you're handling alreadyVisited. The first time pathify is called, this list will contain only the initial square (0,0), which is fine. Here's the important part of your code:
for (int t=0,temp; t<callAgain.size(); t++) {
temp=(int) callAgain.get(t);
pathify(temp/10, temp%10, alreadyVisited);
}
You've found the neighbors of the initial cell. Your code will pick the first neighbor; then it will find paths starting with that neighbor, and the recursive calls to pathify will add cells to alreadyVisited.
Now, after all the recursive calls come back, you're ready to find cells starting with the second neighbor of the initial cell. But you have a problem: alreadyVisited still has all the cells it's collected from the paths it found starting with the second neighbor. So you won't find all possible paths starting with the second neighbor; you won't find any path that includes any cell in any path you've previously found. This isn't what you want, since you only want to avoid visiting the same cell in each path--you don't want to avoid visiting the same cell in all your previous paths. (I simplified this a little bit. In reality, the problem will start occurring deeper down the recursive stack, and you won't even find all the paths beginning with the first neighbor.)
When implementing a recursive algorithm, I've found that it's generally a bad idea to keep an intermediate data structure that is shared by recursive invocations that will be modified by those invocations. In this case, that's the list alreadyVisited. The problem is that when an invocation deeper down the stack modifies the structure, this affects invocations further up, because they will see the modifications after the deeper invocations return, which is basically data they need changing underneath them. (I'm not talking about a collection that is used to hold a list of results, if the list is basically write-only.) The way to avoid it here is that instead of adding to alreadyVisited, you could create a clone of this list and then add to it. That way, a deeper invocation can be sure that it's not impacting the shallower invocations by changing their data. That is, instead of
alreadyVisited.add(x*10+y);
write
alreadyVisited = [make a copy of alreadyVisited];
alreadyVisited.add(x*10+y);
The add will modify a new list, not the list that other invocations are using. (Personally, I'd declare a new variable such as newAlreadyVisited, since I don't really like modifying parameters, for readability reasons.)
This may seem inefficient. It will definitely use more memory (although the memory should be garbage-collectible pretty quickly). But trying to share a data structure between recursive invocations is very, very difficult to do correctly. It can be done if you're very careful about cleaning up the changes and restoring the structure to what it was when the method began. That might be necessary if the structure is something like a large tree, making it unfeasible to copy for every invocation. But it can take a lot of skill to make things work.
EDIT: I tested it and it appears to work: 12 if xRows=yColumns=2, 8512 if both are 4 (is that correct?). Another approach: instead of copying the list, I tried
alreadyVisited.remove((Object)(x*10+y));
at the end of the method ((Object) is needed so that Java doesn't think you're removing at an index) and that gave me the same results. If you do that, you'll make sure that alreadyVisited is the same when pathify returns as it was when it started. But I want to emphasize that I don't recommend this "cleanup" approach unless you really know what you're doing.

Creating a filesystem implemented as a tree of singly linked lists in Java, using queues

We are given some Tree implemented as a tree of singly-linked lists. The insertPath function constructs a subtree (or uses an existing subtree) to store a new file, represented by filePathQueue into Tree<FileNode> t. The queue has an order IN -> [ “myFile” , “mySubDir” , “myDir” ] -> OUT, meaning I should be able to dequeue to get parent directories in order, and then check against the current level in the tree to see if the directory exists. Each FileNode has a value which is its name, and a boolean true to indicate that it is a file, and false to indicate it is a directory. I have pasted my code for insertPath as well as the findChild code. The rest was given to us and I assume professor-provided code is working. The only code I implemented was findChild and insertPath.
The exception gets thrown when the code creates a new FileSystem using the "sample" directory in my working folder, which contains three subdirectories, each with some files and their own subdirectories. To clarify: the constructor passes to me separate queues that represent each of the files and folders in the directory that I'm going to convert into a tree in a loop. So insertPath gets called multiple times with an updated tree being passed each time.
I have no idea why adding to the tree would throw an exception: it's telling me that I'm trying to dequeue an empty queue, but based on my code I should return out of it if the queue is empty? Exception is at the bottom. The lines with the problems are the recursive calls in the insertPath method as well as the dequeue at the top. Any help is greatly appreciated. Thanks.
public Tree<T> findChild(T otherLabel) {
if(getFirstChild() == null)
return null;
if(getFirstChild().getLabel() == otherLabel)
return getFirstChild();
Tree<T> test = getNextSibling();
while(test != null){
if(test.getLabel() == otherLabel)
return test;
test = test.getNextSibling();
}
return null;
}
public void insertPath(Tree<FileNode> t, QueueList<String> filePathQueue) {
try{
String check = filePathQueue.dequeue();
if(filePathQueue.size() == 0){
Tree<FileNode> file = new Tree<FileNode>(new FileNode(check,false));
t.addChild(file);
return;
}
Tree<FileNode> dir = new Tree<FileNode>(new FileNode(check,true));
Tree<FileNode> subdir = t.findChild(dir.getLabel());
if(subdir == null){
t.addChild(dir);
insertPath(t.getFirstChild(), filePathQueue);
}
insertPath(subdir, filePathQueue);
}
catch(Exception e){ e.printStackTrace(); return;}
InvalidOperationException: Queue empty: nothing to dequeue.
at QueueList.dequeue(QueueList.java:39)
at FileSystem.insertPath(FileSystem.java:38)
at FileSystem.insertPath(FileSystem.java:50)
at FileSystem.insertPath(FileSystem.java:48)
You're calling insertPath() recursively twice at the end of your insertPath() method:
public void insertPath(Tree<FileNode> t, QueueList<String> filePathQueue) {
...
if(subdir == null){
t.addChild(dir);
insertPath(t.getFirstChild(), filePathQueue); // ONCE
}
insertPath(subdir, filePathQueue); // TWICE
}
So, if going into the above block of code with a filePathQueue that only has one element, each of those calls to insertPath() is going to try to pull one out, and the second will throw the InvalidOperationException you demonstrated in your question.
It seems you either want to include an else block for when subdir is not null, or improve the first line of your insertPath() method to check for the filePathQueue size before you try to dequeue an item from it.
But seeing as this is homework - I'll let you decide which path to take. :-)

DFS a tree with no memory

I'm trying to write a function to traverse a tree with depth first search.
My current algorithm goes something like:
If children
go to first child
If no children
go to next sibling
If no siblings
go to parent
The problem I'm running into is that I can't mark nodes on the tree as having been visited, so when I go to the parent the cycle just resets and it goes to the child again, getting stuck in a loop. Does anyone have any idea as to how I could solve this?
(It's in java using the ANTLR plugin)
EDIT:
Following one of the suggestions I wrote this:
public void traverseTree(Tree tree){
if (tree.getChildCount() > 0){
tree = tree.getChild(0);
traverseTree(tree);
System.out.println(tree.toString());
}
if (tree.getParent().getChild(tree.getChildIndex() + 1) != null){
tree = tree.getParent().getChild(tree.getChildIndex() + 1);
traverseTree(tree);
System.out.println(tree.toString());
}
if (!tree.getParent().toString().contains("ROOT_NODE")){
tree = tree.getParent();
traverseTree(tree);
System.out.println(tree.toString());
}
}
Root node is the name of the root node, but I'm getting a stack overflow error. Anyone have any idea why?
Thanks.
I would use recursion in this case.
class Node {
public List<Node> getChildren() { .... }
public void traverse(Visitor<Node> visitor) {
// If children
// go to first child - by traversing the children first.
for(Node kid: getChildren())
kid.traverse(visitor);
// If no children
// go to next sibling, - by continuing the loop.
visitor.visit(this);
// If no siblings
// go to parent - by returning and letting the parent be processed
}
}
interface Vistor<N> {
public void visit(N n);
}
Using a hash_table map each vertex to boolean indicate whether visited or not
Write a depth first Iterator that keeps track of visited nodes internally. That way the tree doesn't have to change to know that it's being watched.
If "no memory" can be interpreted as O(1) memory, then the change may help:
Remember not only the current node, but also node where you came from
Traverse children only if you didn't came from one of them

AVL Tree Balancing

I am working on an assignment that asks me to implement an AVL tree. I'm pretty sure I have the rotation methods correct, but I'm having trouble figuring out when to use them.
For example, the explanation in the book says that I should climb up the same path I went down to insert the node/element. However, I can't have any parent pointers.
Latest code:
public BinaryNode<T> insert(BinaryNode<T> node) {
if (this.getElement().compareTo(node.getElement()) > 0) {
if (this.getLeftChild() != null) {
BinaryNode<T> b = this.getLeftChild().insert(node);
if(!this.isBalanced()) {
this.balance();
}
return b;
} else {
this.setLeftChild(node);
}
} else if (this.getElement().compareTo(node.getElement()) < 0) {
if (this.getRightChild() != null) {
return this.getRightChild().insert(node);
} else {
this.setRightChild(node);
}
}
return this;
}
What I want to do here is climb back up the tree, but it can only check the balancing AFTER it inserts the node. Hence, this being in the else clause.
I also tried putting the balance code where R Samuel Klatchko suggested, but checked the balance on each insert. For example: If one inserts 7, 9, 5, 3, and 1 consecutively, I get a null pointer exception when trying to insert 1.
EDIT: One reason for the above may have something to do with the way I was doing the height. It works fine with a single right rotation if I calculate the height every time with height() but that breaks the O(log(n)) time of an AVL Tree.
Any thoughts on how to accomplish this?
You code is climbing up the same path you went down. Consider this code:
if (this.getLeftChild() != null) {
return this.getLeftChild().insert(node);
}
and modify it slightly:
if (this.getLeftChild() != null) {
boolean b = this.getLeftChild().insert(node);
// do something here
return b;
}
As the code returns from the recursive calls, each return brings you back to the parent. By not immediately returning the value of the recursive call, you have a chance to do your rebalancing.
Update for latest code
Don't forget to rebalance when you've inserted to the right.
You might try passing the parent pointer into the insert method, or you could convert insert into an iterative method and keep an explicit stack on which you record the path down the tree.
By the way, in order to choose which rotation to use, you can just know that a node is unbalanced, you have to know whether the deeper subtree is on the right or on the left. This means that your simple isBalanced method isn't quite enough. It's also inefficient, and will blow the AVL tree's O(log n) complexity, because you compute the heights each time.

Categories