Deleting duplicates from a Linked List - java

I am trying to delete duplicates from a Linked List. However, the method that I wrote causes an infinite loop and I'm not sure why there is one. Here is my method:
import java.util.HashSet;
import java.util.Hashtable;
public class Question {
public static void deleteDupsB(LinkedListNode n) {
LinkedListNode runner = null;
LinkedListNode previous = null;
while(n != null) {
runner = n.next;
while(runner != null) {
if(n.data == runner.data) {
previous.next = runner.next; //This line is causing an infinite loop and I'm not sure why.
}
else {
previous = runner;
}
runner = runner.next;
}
n = n.next;
}
}
public static void main(String[] args) {
LinkedListNode first = new LinkedListNode(0, null, null); //AssortedMethods.randomLinkedList(1000, 0, 2);
LinkedListNode head = first;
LinkedListNode second = first;
for (int i = 1; i < 8; i++) {
second = new LinkedListNode(i % 2, null, null);
first.setNext(second);
second.setPrevious(first);
first = second;
}
System.out.println(head.printForward());
// LinkedListNode clone = head.clone();
deleteDupsB(head);
System.out.println(head.printForward());
// deleteDupsC(clone);
// System.out.println(clone.printForward());
}
}
I know there are some problems with it such as runner throwing an exception eventually. But I guess I'm not too worried about that right now. I think the method is kind of producing the effect because when I insert a break inside of first while loop and the second, it deletes all the 0s. Anyhow, do you have any suggestions?

Make sure your object's equals(Object) method is sound, then copy the LinkedList to a LinkedHashSet. Better yet, if you need to eliminate duplicates, just use the LinkedHashSet instead of the LinkedList.

May be reason in the next code line:
for(...) {
...
first = second;
}
You should use "first = second" only in case if your list contains 1 element only.

I would suggest using streams. It's probably less efficient, but it's less error prone and it saves a lot of time.
LinkedListNode list = ...;
list.stream()
// This removes duplicates
.distinct()
// And this turns it back into a linked list
.collect((Supplier<LinkedList<Integer>>) LinkedList::new,
LinkedList::add,
(left, right) -> {
left.addAll(right);
}
);

Related

Implementing an AddAtIndex method in a LinkedList

currently, I am working on implementing an AddAtIndex method and for the most part it seems to be working fine. However, my method is not passing my JUnit test and I can't seem to understand why. Thus, I have chosen to show the code I have done thus far:
**
* Add an element to the list at the specified index
* #param The index where the element should be added
* #param element The element to add
*/
public void add(int index, E element ) //Method should be O(1) time.
{
// TODO: Implement this method
if (index < 0) {
System.out.println("Can't add an element at a negative index.");
}
int i = 0;
LLNode<E> currentNode = head.next;
while ( i < size ) {
if ( i == index ) {
LLNode<E> newNode = new LLNode<E>(element);
LLNode<E> tempNode = new LLNode<E>(currentNode.data);
currentNode.next = tempNode;
currentNode.data = newNode.data;
newNode.prev = currentNode.prev;
newNode.next = tempNode;
tempNode.prev = newNode;
size++;
}
currentNode = currentNode.next;
i++;
}
}
My thought process behind the code is that the method creates a new Node, then it replaces the data at the specified index of the linked list. However, the data at the node it is replacing is stored in a temporary node which is incremented in position to the next node after the new node. I am about 80% confident in my implementation though the code looks a bit sloppy. I have created a driver to demonstrate the implementation. The drivers code is as follows:
public class LinkedListDriver {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyLinkedList<String> nameList = new MyLinkedList<String>();
nameList.add("Hamadi");
nameList.add("Ballo");
nameList.add(1, "Salisu");
nameList.add(2, "Galo");
System.out.println(nameList.toString());
System.out.println(nameList.size());
nameList.set(2, "Abdullahi");
System.out.println(nameList.toString());
nameList.remove(1);
System.out.println(nameList.toString());
MyLinkedList<Integer> list1 = new MyLinkedList<Integer>();
list1.add(65);
list1.add(21);
list1.add(42);
System.out.println(list1.toString());
list1.remove(0);
System.out.println(list1.toString());
}
}
The Output from the driver is as follows:
List: Hamadi, Salisu, Galo, Ballo,
4
Replacing Galo with Abdullahi
List: Hamadi, Salisu, Abdullahi, Ballo,
Removing Salisu from the list
List: Hamadi, Abdullahi, Ballo,
List: 65, 21, 42,
Removing 65 from the list
List: 21, 42,
The unit test fails however with the following error:
It fails at the AssertEquals method:
shortList.add(2, "E");
shortList.add(3, "F");
**assertEquals("AddAtIndex: at position 2 ", "E", shortList.get(2)); //fails here**
assertEquals("AddAtIndex: at position 3 ", "F", shortList.get(3));
assertEquals("AddAtIndex: List size is ", 6, shortList.size());
I would like to know what I'm doing wrong. I have this literally completely figured out, though I know that there is something a bit off about my AddAtindex method. Thanks!
You don't need that tempNode. Just create the newNode and insert it properly between currentNode and its previous node.
You should also consider the possibility of adding an element at the beginning (no previous) or end (no next) of the list.
I used head and tail as sentinel nodes. Created a new node to be added in the list.
public boolean add(E element) {
// create new element
LLNode<E> variable = new LLNode(element);
variable.next = null;
variable.prev = null;
// if element is null, throw exception
if (element == null) {
// return false;
throw new NullPointerException("Element is null");
} else {
// get the value stored in tail.prev in variable temp.
variable.prev = tail.prev;
variable.next = tail;
// now modify the tail node prev and new node next
tail.prev = variable;
// get prev node next link changed
variable.prev.next = variable;
// update size
if (head.next.next != tail) {
size++;
}
return true;
}
}

Linked list remove method return removed node and new node

I'm using a linked list and I altered the standard remove method because I wanted to return both the removed node which is normal, but also return the node before it so for example if I wanted to change a direct reference i.e a tail node to the previous node I could. I was wondering if using a hashmap to achieve this as shown below is the best way to go about this or if there is a better way to achieve what I want? (Note: the below code works I'm just looking to see if there is a more elegant solution)
public HashMap<String, Node> remove(int i)
{
if(isEmpty()) return null;
else
{
HashMap<String, Node> temp = new HashMap<>();
if(i == 0)
{
temp.put(REMOVE_NODE_KEY, firstNode);
firstNode = (E)firstNode.getNext();
temp.put(REMOVE_NEW_KEY, firstNode);
}
else
{
NodeIterator<E> iterator = new NodeIterator<>(firstNode, i, 1);
Node prev = iterator.getEnd();
temp.put(REMOVE_NODE_KEY, prev.getNext());
prev.setNext(prev.getNext().getNext());
temp.put(REMOVE_NEW_KEY, prev);
}
size--;
return temp;
}
}
Couple you just return an array of Nodes?
temp = Node[2];
temp[0] = prev.getNext();
temp[1] = prev.getNext.getNext();
return temp;
A HashMap is rather heavy for this kind of usage. Really, all you want is some kind of "record" or "struct" with two elements. You could define a simple class:
public class NodeAndNewKey {
public Node nodeKey;
public Node newKey;
public NodeAndNewKey(Node nodeKey, Node newKey) {
this.nodeKey = nodeKey;
this.newKey = newKey;
}
}
public NodeAndNewKey remove(int i) { //etc.
You can probably come up with better names than I did.
Another possibility is to return a 2-element array:
public Node[] remove(int i) { // etc.
and define the [0] element as holding the "node key" and the [1] as holding the "new key", or whatever. I don't like it as much because it's less readable when you use it, but Android libraries do things like this sometimes. You could define constants like public static final int REMOVE_NODE_KEY = 0; public static final int REMOVE_NEW_KEY = 1; to make it more readable when retrieving elements from the result array.

How to reverse LinkedList in java

Please accept my apologies first, but I could not reverse my Linked List in java..
I have class and inner class:
MyList{
class Element{
private Element next;
public Element getNext(){return next;}
}
public void reverseMyList(Element curr) {
if (curr.next == null) {
head = curr.next;
return;
}
reverseMyList(curr.next);
while (curr.next != null) {
curr.next.next = curr.next;
curr.next = null;
}
}//:~
I need to reverse my List, I am using method reverseMyList, which needs Element curr.
If my way of thinking in this case is correct ?
Thank you in advance!
Since this kinda looks like homework, I'm not going to lay out the entire solution here, but I will explain how you should conceptually do it.
Imagine that you have 2 linked lists. You have your input list that you need to reverse, and you have an empty one.
If you keep taking the first element off of the original list, and keep putting that on the front of the new list until your original list is empty, than your new list will be what the original was, except reversed.
public static void Reverse(Element element)
{
Element current = element;
Element next = current.Next;
Element nextToNext;
var first = current;
while (next != null && next.Next != null)
{
nextToNext = next.Next;
next.Next = current;
current = next;
next = nextToNext;
}
if (next != null)
{
next.Next = current;
}
first.Next = null;
}
the method you are looking for already exist in package java.utils:
Collections.reverse(mylist);
this method will change the order of element direcly inside your list and you dont need to instance a new list-object... here you can find more specified documentation

Can you help me create a method that will return an entire Linked list and Queue-array;

As of now this is the method I have for my Queue,
public String rQueue()
{
for(int i = 0; i < queueName.length; i++)
return queueName[i];
return
" ";
}
The problem here is that the i++ is never reached because the queueName[i] is returned, when I use this method, only the first one is returned, and I can see why. How can I fix it so that I can return the entire contents of the queue.
I am also confused on the linked list, I have a linked list of an Object(). I only want to return Object.getMethod(); for every instance of each link. So this is what I have. The only way I can think of doing it is with another loop and to look at each spot, but I don't know how to return something like that.
public String displayLink()
{
Link current = first;
while(current != null)
{
current.displayMethod();
current = current.next;
}
}
I am really more stuck on the linked list, and the queue is just sort of a technical problem.
If you want to return a concatenation you could do something like:
public String rQueue() {
String r = "";
for(int i = 0; i < queueName.length; i++) {
if (i != 0) {
r += ", ";
}
r += queueName[i];
}
return r;
}
For the linked list you can return a collection. If displayMethod returns string, you could do something like:
public Collection<String> displayLink() {
Collection<String> result = new List<String>();
Link current = first;
while(current != null) {
result.add(current.displayMethod());
current = current.next;
}
return result;
}
First part,
public String[] qQueue { return queueName; } // or defensively copy, if you prefer
Then you have the array as the queue contents, if that's how it fits into the rest of your code.
Second part looks just fine to me.
What you are doing with the Linked List is actually the only way to traverse it (assuming it is a simple linked list with links from one node to the next). This is also why retrieving an element from a linked list is an O(n) operation, since you have to traverse the list to find the object you are looking for.
Now as far as returning the values is concerned you can just append them to a String and return this String in the end. Something like this would work:
public String rQueue() {
StringBuffer sb = new StringBuffer();
for(int i = 0; i < queueName.length; i++)
sb = sb.append(queueName[i]);
return sb.toString();
}
Note that I am using a StringBuffer in the above code since it is more efficient than concatenating to the end of a String, especially for a large number of concatenations.

Implementing my own HashSet class... my add() logic seems faulty

when scanning a file for words and using the built-in hashset class from the API, my word count returns 349 (which is what it's supposed to be)
Using my home-made hashset class, I get 235... so something in my add() method must be wrong, but I can't understand what it is.
thanks for any help!
public class HashWordSet implements WordSet {
private int size = 0;
private Node[] buckets = new Node[8];
public Iterator<Word> iterator() {
return new WordIterator();
}
//Add word if not already added
public void add(Word word) {
int key = getBucketNumber(word);
Node node = buckets[key];
while (node != null) {
if (node.value.equals(word))
return;
else
node = node.next;
}
node = new Node(word);
buckets[key] = node;
size++;
if (size == buckets.length) rehash();
}
private int getBucketNumber(Word word) {
int hc = word.hashCode();
if (hc < 0) hc = -hc;
return hc % buckets.length;
}
It seems like you override nodes[key] with the new word [only] instead of appending a new node to the list, so you lose all old data that was already in this node.
It should work fine if there are no elements in there before add() was invoked, but if there are - you will lose some data.
node = new Node(word);
buckets[key] = node;
Though it is hard to be 100% sure about it without the actual implementation of Node.
node = new Node(word);
buckets[key] = node;
If there are any nodes already in the bucket you have just thrown them away. Try something like:
node = new Node(word);
node.next = buckets[key];
buckets[key] = node;

Categories