I have two solutions to print all paths from the root to all leaves in a binary tree as follows.
In Sol 1, used List as an argument in the recursion to add path from the root to each leaf node and then after returning from the recursion, I have to remove a node which is being returned.
Based on my knowledge, this is because List is an object stored in heap and shared by everyone.
So each recursive call uses the same List object list and thus needs to remove.
However, in Sol 2, I used array as an argument and I don't need to remove the returned node unlike List. I don't understand why?
Based on my understanding, since array is also an object, stored in heap and shared by every recursive call. Thus it was supposed be the same with the List case, and I thought I needed to remove the returned node from the recursive call. But it is not true.
Could you explain why the returned node from the recursive call doesn't have to be removed unlike List? My understand of List case is correct? Please let me know it's confusing for me.
Sol 1: recursive 1 - using List
void printPathsFromRootToLeavesRec1(BTNode node, List<BTNode> list) {
if(node == null) return;
list.add(node);
// viristed and added node from root --> left subtree --> right subtree
if(node.left == null && node.right == null)
printNodeList(list);
printPathsFromRootToLeavesRec1(node.left, list);
printPathsFromRootToLeavesRec1(node.right, list);
**// Note 1: KEY POINT = Remove after print !!!
list.remove(node);**
}
Sol 2: Recursive 2 - using array
void printPathFromRootToLeavsRec2(BTNode node, BTNode[] array, int index) {
if(node == null) return;
array[index] = node;
index++;
if(node.left == null && node.right == null) printPathArray(array, index);
printPathFromRootToLeavsRec2(node.left, array, index);
printPathFromRootToLeavsRec2(node.right, array, index);
**// We don't need to remove the returned node in the array case unlike List**
}
Because of index++. In a list, you are always getting the first element. I mean, you always have 1 element because you remove it at the end. In the array, because index++, you always get the last one element.
Because, in the array, we just overwrite the element in the next function call, so there's no need to remove it.
Note that with the List we're doing an add (which always appends to the end, which obviously won't be overwritten by doing another add, thus we need a remove), but with the array we're simply setting the index-th element (so, if there's already an element at that position, we simply overwrite it).
Related
I created Linked List from scratch, and added methods such as add, remove, set, size, etc. I've also added a simple static and recursive sort method, which accepts a Linked List reference as parameter, so that it can be used in the Main class being called as sort(linkedList); and it returns a sorted linked list.
The program throws Exception in thread "main" java.lang.StackOverflowError, at lines, if (biggest.compareTo(nextNode.value) < 0) biggest = nextNode.value; and return sort(list);. I want the sort method to sort the list in alphabetical order (my Linked List consists of String elements).
This is the method in code:
/**
* The sort method sorts the list in alphabetical order
* #param list list to be sorted
* #return sorted linked list
*/
static DD_RecursiveLinkedList sort(DD_RecursiveLinkedList list) {
DD_Node nextNode = head.next;
String biggest = head.value, smallest = tail.value; //by default biggest is the head, and smallest is the tail
if (isEmpty()) return null; //if list is empty, return null
do { //find the biggest and smallest value in the list
if (biggest.compareTo(nextNode.value) < 0) biggest = nextNode.value; //if nextNode is bigger than the biggest, biggest is nextNode
if (smallest.compareTo(nextNode.value) > 0) smallest = nextNode.value; //if nextNode is smaller than the smallest, smallest is nextNode
nextNode = nextNode.next; //update nextNode
} while (nextNode!=null); //loop until nextNode is null
set(0, biggest); set(size()-1, smallest); //set biggest as the head of the list, and smallest as the tail
// remove(biggest);//remove the biggest (head) from the list
// remove(smallest); //remove the smallest (tail) from the list
// list.add(0, biggest); //add the biggest to the sorted list as head element
// list.add(size()-1, smallest); //add the smallest to the sorted list as tail element
return sort(list); //sort the order of sorted list recursively
}
I've commented out the add and remove lines, because they were included in the error so instead of add and remove methods, I've used the set method, to replace the element at the specified index with the specified element.
The problem here is , the sort(list) method is going to be called infinite number of time, causing the java stack to overflow and impossible to store any variables in stack further.
return sort(list);
1.As there is no restriction saying at what conditions recursion should. Stop. It will keep on calling sort (). - this leading to stackOverflow error
2.Since the remove() statements are commented. List size is not altered at all .So it will not stop at isEmpty check also. Also: bcoz whatever you list is it’s finally going to return null always.
I'm having trouble understanding how this method below removes the duplicates in the linked list. After calling this method, all duplicates are successfully removed. Why is the head not null? Wouldnt the head node be null because the current variable in the method iterated to the end. How does this method successfully update the list to get rid of the duplicate items?
static void removeDuplicate(node head)
{
// Hash to store seen values
HashSet<Integer> hs = new HashSet<>();
node current = head;
node prev = null;
while (current != null)
{
int curval = current.val;
// If current value is seen before
if (hs.contains(curval)) {
prev.next = current.next;
} else {
hs.add(curval);
prev = current;
}
current = current.next;
}
}
Elements get removed by changing the pointer from the previous element to point to the next element. That is how you remove elements in LinkedLists, by skipping them. The garbage collector will later remove the object as no object refers to it anymore.
Here is an illustration of the removal:
Duplicates are identified by memorizing each encountered value in a HashSet. If you find an element which was already encountered before (i.e. contained in the set), it is a duplicate.
The head can not get null because it was not null previously and duplicates can only occur after the first element, since you need to encounter an element at least once until you can find a duplicate. For example a list like [1, 1, 1] gets modified to [1] and not to [].
Also the variable head gets not changed in the method, it points to the head-node before and after the method. It seems that you got confused by current = head but you need to know that this, in Java, does not synchronize both variables. If the method changes current this changes are not reflected by head. The statement just means 'let current point to where head points' and afterwards you let current point elsewhere.
public void dfsSearch (TreeNode root, List<String> curr, List<List<String>> res) {
curr.add(String.valueOf(root.val));
// a leaf node has been reached
if (root.left == null && root.right == null) {
res.add(curr);
return;
}
if (root.left != null) {
List<String> temp = new ArrayList<>(curr);
dfsSearch(root.left, temp, res);
}
if (root.right != null) {
List<String> temp = new ArrayList<>(curr);
dfsSearch(root.right, temp, res);
}
}
The code above is a a method using dfs to find all the paths from root to leaves in a binary tree, and my question is, in the two lines above the recursive call, why do I need to instantiate a new list and pass this temp list (temp) to the recursive call, why can't I just use the curr (the argument in the function)?
Imagine this is your binary tree
1
2 3
4 5
Let's say you didn't use temp. Then you would recurse down the left side of the binary tree, meaning curr would add 1,2,and 4. Because lists are mutable, their values are saved in recursive calls, even when you pop from the recursive stack. So after adding 4 to curr, you will go back to node 2, go to the right, and add 5. Thus curr will contain 1,2,4,5 instead of what you want, 1,2,4 and 1,2,5.
The copies are to prevent concurrent modification of curr when the dfsSearch method is called recursively. The first line curr.add(String.valueOf(root.val)); modifies the curr collection, and you can't loop through a collection while modifying it.
Concurrent Modification is one reason. But additionally, the algorithm collects "all paths" from root to leaves in the List of Lists result. If you didn't create new lists at each level on the recursive way down, you would end up with one big jumbled list after the recursion bubbles back up (all paths from root to leaves collected in one mixed list instead of each path in its own list).
Essentially what this problem does is take in a Linked List as a parameter and subtracts its contents from the calling Linked List if the same contents exist in both. I have to do it this way (so no changing the parameters).
For instance: l1.subtractList(l2) would subtract the contents of l2 from l1.
The trouble here is that the calling Linked List has 2 of the same number and that number is also in the Linked List passed as a parameter. I only have to remove one instance of it as well.
I've managed to subtract everything but that duplicate number, but I'm not sure what I'm doing wrong. Keep in mind this is a very new subject to me, so I may be way off base. But I appreciate any and all help you may offer. Thanks.
public void subtractList(LinkedList list)
{
Node current = head;
Node<Integer> temp = list.getFirst();
Integer count = -1;
while (current != null)
if (current == temp){
count++;
list.listRemove(count);
temp = list.getFirst();
}
else
{
current = current.getNext();
}
}
What is listRemove method? Why do you need count? Just traverse the argument list and check if its element temp exists in the calling Linked List. You will need an outer loop traversing the list passed as argument and an inner loop iterating over the calling list to check the value of the element needing to be removed and to remove it if required
while(temp!=null)
{
while(current!=null){
//Check if element exists in list
//If yes, remove it from the calling list
}
//Repeat
temp = temp.getNext();
}
My understanding of why arraylist is faster than a linkedlist is that with an arraylist you basically only need one action - update the reference at the end array element, whereas with a linked list you have to do much more e.g. create a new node, update 2 references, go through linked list and update the last node to point to the new one etc.
However I am not sure how java implement these. How does the arraylist know where the "last" element is, does it store a value of the last element or does it traverse the array and add a new element after the last?
And linked lists, do they store a reference to the last node in the list, or do they traverse the entire list to get to the end?
Look at the source:
ArrayList:
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
LinkedList:
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
An array-list is only faster at certain operations. If you add an element in the middle of an array the arraylist needs to copy basically all data into a new array. It is only if the arraylist already have allocated room for new data it is fast when inserting data where it is empty (usually in the end). Read/update by index is very-fast.
A LinkedList is fast when inserting since it never require the whole array to be copied. But accessing data in a linked list is slow since you need to "walk" all the elements until you are at the element you want to find.
You can always look at the sources of java.* classes.
However, answering particular your question: there is int field in ArrayList class, which contains current size of filled internal array area. When you add new value in ArrayList object, this field increments and then directly addresses to that element in internal array.