Removing duplicate element from a linked list- Java - 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
}

Related

Reversing a singly linked list [Java]

I was wondering if someone could help explain how to reverse a singly linked list without creating new nodes or changing data in the existing nodes. I am trying to study for finals and we had this question on a previous test. They don't release answers to the coding portions of the test and I haven't been able to figure it out.
They told us the best way to reverse it was by using a "runner technique" which I believe I understand what that is. They described it as using two pointers or counters to run through a list and gather information but I'm not sure how to use that to reverse a singly liked list. I was able to brute-force code to reverse a list of length 2, 3, and 4 but I was unable to make a loop or do it recursively. Any code or an explanation on how to go about this would be appreciated, thank you.
You can derive the code by starting with the idea of merely - one by one - popping elements off the input list and pushing them onto an initially empty result list:
NODE reverse(NODE list) {
NODE result = null;
while (list != null) {
NODE head = <pop the first node off list>
<push head onto result>
}
return result;
}
The result will be the reverse of the input. Now substitute Java for the missing pieces
NODE reverse(NODE list) {
NODE result = null;
while (list != null) {
// pop
NODE head = list;
list = list.next;
// push
head.next = result;
result = head;
}
return result;
}
And you're done...
It depends on your implementation of the list, but I would recurse to the end and then reverse the references.
void Reverse(List pList) {
Reverse(pList, null, pList.First); // initial call
}
void Reverse(List pList, Node pPrevious, Node pCurrent) {
if (pCurrent != null)
Reverse(pList, pCurrent, pCurrent.Next); // advance to the end
else { // once we get to the end, make the last element the first element
pList.First = pPrevious;
return;
}
pCurrent.Next = pPrevious; // reverse the references on all nodes
}

Linked List removing duplicate from list, reference confusion

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.

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();
}

I can't add a node into an empty list

I stumbled upon a weird problem with adding two linked lists into a third one in Java, the first linked list "myList1", the second linked list "myList2" and the third one "myList3".
The combining method is supposed to to add the first LinkedList "myList1" then the second "myList2" into the third LinkedList "myList3", but I faced a problem with adding them to third list while it's empty, but if the third list has at least one element every thing goes smoothly.
The code:
Node current = myList1.head;
while (current != null) {
Node newcurrent = myList3.head;
int h1 = current.getData();
Node newNode = new Node(h1);
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
else {
if (newcurrent.getLink() == null) {
newNode.setLink(newcurrent.getLink());
newcurrent.setLink(newNode);
} else {
Node current11 = newcurrent;
while (current11.getLink() != null) {
current11 = current11.getLink();
}
current11.setLink(newNode);
}
}
current = current.getLink();
}
The node is not added to the third LinkedList if the third list is empty, and I tried many other codes but it didn't work either, but if I entered at least one element to the third LinkedList the list is added normally.
other codes I tried :
newcurrent.setLink(newNode);
and
newNode = newcurrent;
newcurrent = newNode;
and
newNode.setLink(newcurrent);
newcurrent.setLink(newNode);
and
newNode.link = newcurrent;
newcurrent.link = newNode;
You're overcomplicating this a bit, I think. The links within the list are already there. You only need to link myList3.tail to myList1.head, no need to loop through adding each node independently. Since you don't look like you are storing a tail, you'll need to iterate to the end of myList3 to find it.
if (myList3.head == null)
myList3.head = myList1.head;
else {
Node list3iter = myList3.head;
while (list3iter.getLink() != null) {
list3iter = list3iter.getLink();
}
list3iter.setLink(myList1.head);
}
}
One further note, I find it painful to try to keep track of names like current, current11, newcurrent, etc. They all mean just about the same thing to my brain. If you're like me, a bit more descriptive naming might help you keep track of what your variables are meant to be doing here.
Node newcurrent = myList3.head;
....
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
Not sure why you have two lists, but the last line above is just assigning to local variable. Should it be as follows instead?
myList3.head = newNode

Is this an efficient way to remove duplicates from a linked list?

I am writing a function that will take in the head of a linked list, remove all duplicates, and return the new head. I've tested it but I want to see if you can catch any bugs or improve on it.
removeDuplicates(Node head)
if(head == null) throw new RuntimeException("Invalid linked list");
Node cur = head.next;
while(cur != null) {
if(head.data == cur.data) {
head = head.next;
} else {
Node runner = head;
while(runner.next != cur) {
if(runner.next.data == cur.data) {
runner.next = runner.next.next;
break;
}
runner = runner.next;
}
cur = cur.next;
}
return head;
}
If you are willing to spend a little more RAM on the process, you can make it go much faster without changing the structure.
For desktop apps, I normally favor using more RAM and winning some speed. So I would do something like this.
removeDuplicates(Node head) {
if (head == null) {
throw new RuntimeException("Invalid List");
}
Node current = head;
Node prev = null;
Set<T> data = new HashSet<T>(); // where T is the type of your data and assuming it implements the necessary methods to be added to a Set properly.
while (current != null) {
if (!data.contains(current.data)) {
data.add(current.data);
prev = current;
current = current.next;
} else {
if (prev != null) {
prev.next = current.next;
current = current.next;
}
}
}
}
This should run in O(n) time.
EDIT
I hope I was correct in assuming that this is some kind of project / homework where you are being forced to use a linked list, otherwise, as noted, you would be better off using a different data structure.
I didn't check your code for bugs, but I do have a suggestion for improving it. Allocate a Hashtable or HashMap that maps Node to Boolean. As you process each element, if it is not a key in the hash, add it (with Boolean.TRUE as the value). If it does exist as a key, then it already appeared in the list and you can simply remove it.
This is faster than your method because hash lookups work in roughly constant time, while you have an inner loop that has to go down the entire remainder of the list for each list element.
Also, you might consider whether using an equals() test instead of == makes better sense for your application.
To efficiently remove duplicates you should stay away from linked list: Use java.util.PriorityQueue instead; it is a sorted collection for which you can define the sorting-criteria. If you always insert into a sorted collection removing duplicates can be either done directly upon insertion or on-demand with a single O(n)-pass.
Aside from using the elements of the list to create a hash map and testing each element by using it as a key, which would only be desirable for a large number of elements, where large depends on the resources required to create the hash map, sequentially scanning the list is a practical option, but there are others which will be faster. See user138170's answer here - an in-place merge sort is an O(n log(n)) operation which does not use an extra space, whereas a solution using separately-allocated array would work in O(n) time. Practically, you may want to profile the code and settle for a reasonable value of n, where n is the number of elements in the list, after which a solution allocating memory will be used instead of one which does not.
Edit: If efficiency is really important, I would suggest not using a linked list (largely to preserve cache-coherency) and perhaps using JNI and implementing the function natively; the data would also have to be supplied in a natively-allocated buffer.

Categories

Resources