Linked List removing duplicate from list, reference confusion - java

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.

Related

Removing duplicate element from a linked list- Java

EDIT
I decided to use a HashSet instead as it has is O(N). However, I am still having an issue that it's not deleting all repeating numbers, 10 13 11 11 12 11 10 12 11.
It returns : 10 13 11 12 10 11
static void removeDups(Node node) {
HashSet<Integer> values = new HashSet<Integer>();
Node previous = null;
while(node != null) {
if(values.contains(node.data))
previous.next = node.next;
else
values.add(node.data);
previous = node;
node= node.next;
}
}
Irrelevant
I am trying to remove duplicate elements from a linked list but for some reason, It does not remove the last repeating element. For instance if the list is 10,11,12,11,12,9,11, It returns : 10,11,12,9,11.
public static void removeDups1(Node head){
if(head == head.next)
head = head.next.next;
Node fastptr =head;
Node slowptr = head;
while(slowptr.next != null && fastptr.next.next !=null) {
if(slowptr.data == fastptr.data) {
fastptr.next = fastptr.next.next;}
slowptr = slowptr.next;
fastptr = fastptr.next;
}}
Checking fastptr.next.next == null prematurely exits your loop.
Your algorithm is trying to find if there are any duplicates for each element from the current position to the next two positions in the linked list. But duplicates can occur anywhere in the linked list. Therefore, for each element, it should traverse through the linked list once again.
That would be a O(n^2) solution
A better approach would be to maintain a hash to keep track of already visited data.
This would be a O(n) solution.
I think, since at the beginning you are pointing both fastptr and slowptr to the same Node, and always end up pointing them to the same node at the end of the while, you're always comparing the same Nodes here, doing nothing valuable to the algorithm:
if(slowptr.data == fastptr.data)
Anyways, the algorightm's logic seems all wrong.
Like others sayd, you should do two loops, one inside another: the first one to the slowptr, the second one to the fastptr
Try to implement based on this proposition:
For all Node (pointed by slowptr, first loop), for all subsequent nodes (second loop) do see if they are the same.
I don't see real Java when I look to your code. But anyway as #Aishwarya said a better solution is to build a map or hash set for better performance. Using Java built-in functions it is even simpler. Just do:
LinkedList<Node> yourList = ...
LinkedList<Node> filteredList = new LinkedList<>(new HashSet<Node>(yourList));
To make this work properly you must make sure that Node equals(Object o) and hashCode()are correctly implemented.
Then your (generic) duplicate removal function might be:
public static void removeDups(LinkedList pList) {
return new LinkedList(new HashSet(pList));
// indeed specifying <Node> is not really needed
}

Subtracting a passed Linked List in the Method from the Calling Linked 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();
}

Reversing a doubly linked list in Java without creating new list

I'm trying to reverse a doubly linked list in Java and can't seem to figure out why I am getting NullPointer errors. I know that there are other questions on here which deal with the same topic such as
Reversing a Doubly Linked List
Reversing a linked list
and a few others that I have found.
I am sorry for the duplicate but I think what I am doing is the same as what the answers advise and I can't seem to figure out why my function isn't working.
I know that I need to traverse the list and swap the pointers for next and previous until I reach the end of the list, which should result in the list being reversed.
Here is the code for my function:
public void reverse() {
Node<AnyType> temp = null;
//Start at the beginning of the list
Node<AnyType> current = beginMarker;
//Go until the end of the list is reached
while (current != null)
{
//Update temp to remember what came before the current node
temp = current.prev;
//Then switch the previous and next pointers
current.prev = current.next;
current.next = temp;
//Advance current to point to the next node in the list, which is now
//stored in current.prev
current = current.prev;
}
}
and this is what I think I am doing:
Creating a temp variable to hold the value of the next the previous value for when I make a swap, initially set to null because the last node should have a pointer to null for node.next, and a current variable to hold where I am currently in the list.
Then while current is not null, so while I am not at the end of the list, I am setting the temp variable to what came before current. Then I'm swapping the pointers for previous and next, and setting the new value of current to current.prev, which should move me forward a node in the list.
However there must be something that I am missing here. I would appreciate being pushed in the right direction.
Edit:
This is my stack trace but it is not giving me an error within the function so I'm not sure how exactly to read it to find where the issue is
Exception in thread "main" java.lang.NullPointerException
at MyLinkedList$LinkedListIterator.next(MyLinkedList.java:285)
at MyLinkedList.toString(MyLinkedList.java:186)
at java.lang.String.valueOf(String.java:2847)
at java.io.PrintStream.println(PrintStream.java:821)
at MyLinkedList.main(MyLinkedList.java:321)

List vs array as an argument of recursion in java

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).

Java ArrayList and LinkedList - adding element at end implementation details

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.

Categories

Resources