Doubly Linked List of Menu Items - java

I have a program which processes menu items (and submenu items) using a linkedList data structure. My program works except for the delete function which works if deleting an element inserted before an existing element but doesn't if the element is inserted after an existing element. Any advise is much appreciated! Here's the insert function
public Item insert(String newElem, String existingElem, int key) {
// search for given existing element starting at head
currentNode = new Item("");
currentNode = head;
while (!currentNode.element.equals(existingElem)) {
currentNode = currentNode.next;
if (currentNode == null)
break; // cannot find the given key
}
// create a new node
newNode = new Item(newElem);
if (key == 1) // if key = 1 insert after the existing element
{
newNode.next = currentNode.next;
newNode.prev = currentNode;
currentNode.next = newNode;
tail = newNode;
tail.next = null;
counter++;
}
if (key == 2) // if key = 2 insert before the existing element
{
newNode.next = currentNode;
newNode.prev = currentNode.prev;
currentNode.prev = newNode;
head = newNode;
head.prev = null;
counter++;
}
return newNode;
}
I also realize that I am assuming only one existing element, however, if I could make the delete function more generic then it would be best and I don't want to split the function into multiple functions! Here's the delete function:-
public Item delete(String elem) {
// search for given existing element starting at head
currentNode = new Item("");
currentNode = head;
while (!currentNode.element.equals(elem)) {
currentNode = currentNode.next;
if (currentNode == null)
break; // cannot find the given key
}
// check if element is the first element
if (currentNode == head) {
head = currentNode.next;
} else {
currentNode.prev = currentNode.next;
}
// check if element is the last element
if (currentNode == tail) {
tail = currentNode.prev;
} else {
currentNode = currentNode.prev;
}
return currentNode;
}

Problems
You should not be updating tail and head every time you insert a new node. You should only do so if inserting at the beginning or end of the list, if you are not at the begining of the list, you need to also update the prev/next of the element that used to be before/after the one you are inserting next to:
if (key == 1) // if key = 1 insert after the existing element
{
newNode.next = currentNode.next;
newNode.prev = currentNode;
currentNode.next = newNode;
if (newNode.next == null)
tail = newNode;
else
newNode.next.prev = newNode;
}
else if (key == 2) // if key = 2 insert before the existing element
{
newNode.next = currentNode;
newNode.prev = currentNode.prev;
currentNode.prev = newNode;
if (newNode.prev == null)
head = newNode;
else
newNode.prev.next = newNode;
}
Note: I am also using an else if statement, instead of two ifs
Your delete function need to be updated also. You are not updating the surrounding nodes, just the one you are removing:
if (currentNode.prev == null) { // is head
head = currentNode.next;
} else {
currentNode.prev.next = currentNode.next;
}
if (currentNode.next == null) { // is tail
tail = currentNode.prev;
} else {
currentNode.next.prev = currentNode.prev;
}
Other comments
It seems unusual that you would be searching for the node to insert before/after by value, what if I had a list like this: [1,2,2,2,3]? It would be impossible to insert a value in several places in that list. Surely your API should be based on Items:
public Item insert(String newValue, Item existingItem, int key);
public Item delete(Item item);
Or indicies:
public Item insert(String newValue, int index, int key);
public Item delete(int index);
You can then have another method to find the Item or index for a particular value:
public Item find(String value);
Or:
public int indexOf(String value);
And use them like this to get the equivalent of your methods:
list.insert("value", list.find("other value"), 1);
You might want to consider changing the insert method to an insertBefore and insertAfter method, or at least changing the key parameter to an enumerated type:
public enum Position {
BEFORE,
AFTER,
}
After all, what happens when key is 0, or 23?
You currently have currentItem as a class member, it should really be a local variable of the function (after all, what use does it have outside of those functions), and you also don't need to initialise it to a new element, just set it straight to head:
public Item insert(String newElem, String existingElem, int key) {
Item currentNode = head;
...
}
Finally, your search code doesn't work if there are no items in the list. The following line will fail with a NullPointerException if head is null:
while (!currentNode.element.equals(existingElem))
Because you can't call .equals(...) on a null reference.

Related

Remove duplicates from a sorted linked list in a efficient way

I'm on HackerRank and I need to remove duplicate items from a sorted linked list. I passed all the cases except for two of them which the input is something like: 10001102034
So my program takes to seconds to complete and exceed the time. How can I do my code more efficiently, I heard about using square root but I don't know how to use it. Any guide is appreciate. Here is my code.
private static Node removeDuplicates(Node head) {
/* Another reference to head */
Node current = head;
Node next;
/* Traverse list till the last node */
while (current != null && current.next != null) {
if (current.data == current.next.data) {
next = current.next.next;
if (next == null) {
current.next = null;
break;
}
current.next = next;
} else {
current = current.next;
}
}
return head;
}
Again. It works but takes too much times with longer numbers.
You should replace condition if (current.data == current.next.data) with while loop and use break 'label':
out:
while (current != null && current.next != null) {
while (current.data == current.next.data) {
next = current.next.next;
if (next == null) {
current.next = null;
break out;
}
current.next = next;
}
current = current.next;
}
You can't use the square root because when u want to remove duplicates from a list you have to check all the list .
The square root technique is used for searching in a sorted list.
But for your question if you can improve the runtime on that your code in O(n^2) but if you change your code to use hashtable you can make it O(n).
import java.util.HashSet;
public class removeDuplicates
{
static class node
{
int val;
node next;
public node(int val)
{
this.val = val;
}
}
/* Function to remove duplicates from a
unsorted linked list */
static void removeDuplicate(node head)
{
// Hash to store seen values
HashSet<Integer> hs = new HashSet<>();
/* Pick elements one by one */
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;
}
}
/* Function to print nodes in a given linked list */
static void printList(node head)
{
while (head != null)
{
System.out.print(head.val + " ");
head = head.next;
}
}
I hope this will help you.

Java - Removing an element from LinkedList except first

I'm new to Java.
I have created a method where it will remove elements from LinkedList except the first one. The idea is a boolean will be set to true if a LinkedList's element data (Which is in Integer) matched with parameter. Once the boolean sets to true, it will remove any element that also matched with initial one.
Now for the problem. For example, if I were to remove 5 except the first one from this LinkedList:
5 5 5 6 5 7 8 9
I will get result like this:
5 5 6 7 8 9
As you can see, it didn't remove the 5 on the second position. Is there anything wrong with my code?
Here's the code by the way
public void append(int data) {
Node newNode = new Node(data);
if (head == null) {
head = new Node(data);
return;
}
Node lastNode = head;
while (lastNode.next != null) {
lastNode = lastNode.next;
}
lastNode.next = newNode;
return;
}
public void insert(int data) {
Node newData = new Node(data);
newData.next = head;
head = newData;
}
public void removeExceptFirst(int dataValue) { //The mentioned method
boolean duplicate = false;
Node currentNode = head;
while (currentNode.next != null) {
int value = currentNode.next.data;
if (value == dataValue) {
if (!duplicate) {
duplicate = true;
currentNode = currentNode.next;
} else {
currentNode.next = currentNode.next.next;
}
} else {
currentNode = currentNode.next;
}
}
return;
}
The problem here is with
if (!duplicate) {
duplicate = true;
currentNode = currentNode.next;
}
you are marking duplicate = true and immediately assigning the "currentNode = currentNode.next;"
due to this reference is getting preserve of the next node
So
1. Put the condition outside of the loop to check whether the head element itself is
that node, if->yes mark isDuplicate = true and proceed in the loop.
2. Inside the loop check afterward and then assign the next node.
Hope this should work
You skipped head node. Try replace
Node currentNode = head;
with
Node currentNode = new Node();
currentNode.next = head;
You should update the current node reference as well as head->next should point to the current node after removing the node.
try the below code:
if (!duplicate) {
duplicate = true;
currentNode = currentNode.next;
head.next= currentNode.next;
}else {
currentNode.next = currentNode.next.next;
currentNode = currentNode.next;
head.next = currentNode; }
`

Linked List sorted insertion

I'm trying to insert a request and sort it via priority, so highest(1) is first in the list.
public Node addByPriority(Object request, int priority) {
size++;
//creates a new node with a priority, owner and creator and sets its next node to the root
Node newNode = new Node(request, priority);
//node to store prev
Node prevNode = null;
//node to store current
Node currNode = first;
//cycle thru the nodes til either the priority is higher or current is null
while (currNode != null && priority >= currNode.getPriority()) {
prevNode = currNode;
currNode = currNode.getNext();
}
if (prevNode == null) {
newNode.setNext(first);
first = newNode;
}
else {
prevNode.setNext(newNode);
newNode.setNext(currNode);
}
// what would be the return statement??
}
It says I need a return statement but not sure what has to be put, or if there's another way.
You didn't state what Node you're supposed to return, but it stands to reason that you'd return the newly created one:
return newNode;
You can return the head of the linked list as:
return first;
This can be helpful to access the updated list again.

Can someone explain this Linked List Null Pointer Exception?

Node class
private class Node<E> {
E data;
Node<E> next;
public Node(E obj) {
data = obj;
next = null;
}
}
insert method (ascending order)
public void insert(E obj) {
Node<E> newNode = new Node<E>(obj);
Node<E> prev = null, curr = head;
while(curr != null && ((Comparable<E>)obj).compareTo(curr.data) >= 0) {
prev = curr;
curr = curr.next;
}
if(prev == null)
head = newNode;
else {
prev.next = newNode;
newNode.next = curr;
}
currentSize++;
}
remove method
public E remove() {
if(isEmpty())
return null;
E tmp = head.data;
head = head.next;
currentSize--;
return tmp;
}
I get Null Pointer Exception at the line
E tmp = head.data;
in the remove method
The error is fixed if the change the else statement in my insert method to
else
prev.next = newNode;
newNode.next = curr;
There's a problem when you insert a new node that needs to go at the beginning of the list (because it's smaller than the current head node). When you get to this part:
if(prev == null)
head = newNode;
you're setting head to be the new node you've just created, but you also need to set newNode.next to be the previous head. So you really want
if(prev == null) {
newNode.next = head;
head = newNode;
}
which inserts the new node at the beginning but tacks the previous head onto it.
With your code as it stands, when you add a second element that should go at the beginning, you're accidentally discarding the element that's already there, but you're still increasing currentSize; so you end up with a list with only one element, but a currentSize of 2. When you then try to remove two elements, the second one fails with a NullPointerException because you try to read the data inside a non-existent element.

Remove all occurrences of item from a Linked List

I've been working on this lab assignment for a few hours and can't understand why this code is not working. The question is to add the method int removeEvery(T item) that removes all occurrences of item and returns the number of removed items to a link list class that implements a link list interface.
This is my code: It removes some occurrences of the item, but not all of them.
public int removeEvery(T item){
int index = 0;
Node currentNode = firstNode;
for(int i = 1; i <= numberOfEntries; i++)
{
System.out.println(currentNode.getData());
if (item.equals(currentNode.getData())){
index++;
remove(i);}
else{
currentNode = currentNode.getNextNode();}
}
if(index != 0)
return index;
return -1;
}
Here is the remove method that was included in the LinkList class:
public T remove(int givenPosition)
{
T result = null; // return value
if ((givenPosition >= 1) && (givenPosition <= numberOfEntries))
{
assert !isEmpty();
if (givenPosition == 1) // case 1: remove first entry
{
result = firstNode.getData(); // save entry to be removed
firstNode = firstNode.getNextNode();
if (numberOfEntries == 1)
lastNode = null; // solitary entry was removed
}
else // case 2: givenPosition > 1
{
Node nodeBefore = getNodeAt(givenPosition - 1);
Node nodeToRemove = nodeBefore.getNextNode();
Node nodeAfter = nodeToRemove.getNextNode();
nodeBefore.setNextNode(nodeAfter); // disconnect the node to be removed
result = nodeToRemove.getData(); // save entry to be removed
if (givenPosition == numberOfEntries)
lastNode = nodeBefore; // last node was removed
} // end if
numberOfEntries--;
} // end if
return result; // return removed entry, or
// null if operation fails
} // end remove
There is something special with your linked list, you can access next element with current.getNextNode but you delete using the element index. You should look in the rest of your implementation how this index is managed. Does the first element have index 0 or 1 (you start your loop with 1). What happens to the indexes of all elements when you remove one. Do the elements know their index ?
You could use something like
int deletedNodes = 0;
int currentIndex = 0; // check if 1 or 0
currentNode = fist;
while(currentNode != null){ // I guess lastNode.getNextNode() is null
if(//should remove){
remove(currentIndex);
deletedNodes++
// probably no need to change the index as all element should have been shifted back one index
} else {
currentIndex++; // index changes only if no node was deleted
}
currentNode = currentNode.getNextNode(); // will work even if it was deleted
}
return deletedNodes;
I think the problem you have comes from remove(i).
When you remove the i-th element, the i+1-th element becomes the i-th and so on: every element is shifted. Therefore if you need to remove 2 elements in your list that are at index j and j+1, removing the j-th element calling remove(j) will shift the j+1-th element at the index j. Hence removing that second element requires calling remove(j) again, and not remove(j+1).
So you need to decrement i after removing.
Since your remove method actually decrements numberOfEntries, the condition on your while loop is properly updated. So all you need to do is replace
if (item.equals(currentNode.getData())) {
index++;
remove(i);
}
else {
currentNode = currentNode.getNextNode();
}
by
if (item.equals(currentNode.getData())) {
index++;
remove(i--);
}
// update the current node, whether removing it or not
currentNode = currentNode.getNextNode();
Iterator.remove()
This problem you are describing shows the usefulness of Iterator.remove() when using data structures from the JDK for going through an iterable collection and removing elements as you go through it.
After removing a node, as #Vakimshaar suggested, you need to decrement the i because the node at this index has been removed and there is a new node at the same index. In addition to that, you also need to update the currentNode reference as it would still be pointing to the node you've just removed, but it should really be pointing to the new node that has moved to this index.
So in the if (item.equals(currentNode.getData())){ block you need to do the following:
Node nextNode = currentNode.getNextNode();
index++;
remove(i--);
currentNode = nextNode;
With this, your code should correctly remove all occurrences.
Here is a Java Code to delete all occurrences of an item from a linked list :
public class LinkedList{
Node head;
class Node{
int data;
Node next;
Node(int d){data =d; next = null;}
}
public void push(int new_data){
Node new_node = new Node(new_data);
new_node.next = head;
head = new_node;
}
public void insertAfter(Node givenNode, int new_data){
if(givenNode == null)
System.out.println("Given node cannot be empty");
Node new_node = new Node(new_data);
new_node.next = givenNode.next;
givenNode.next = new_node;
}
public void append(int new_data){
Node new_node = new Node(new_data);
if(head == null)
head = new_node;
else{
Node last = head;
while(last.next != null)
last = last.next;
last.next = new_node;
}
}
public void printList(){
Node temp = head;
while(temp != null){
System.out.println(temp.data + " ");
temp = temp.next;
}
}
void deleteNode(int key){
// Store head node
Node temp = head, prev=null;
// If head node itself holds the key or multiple occurrences of key
while(temp != null && temp.data == key){
head = temp.next;
temp = head;
}
// Delete occurrences other than head
while(temp != null){
// Search for the key to be deleted, keep track of the
// previous node as we need to change 'prev.next'
while(temp != null && temp.data != key){
prev = temp;
temp = temp.next;
}
// If key was not present in linked list
if(temp == null) return;
// Unlink the node from linked list
prev.next = temp.next;
//Update Temp for next iteration of outer loop
temp = prev.next;
}
}
public static void main(String[] args){
LinkedList llist = new LinkedList();
llist.push(6);
llist.append(7);
llist.append(7);
llist.append(7);
llist.append(9);
llist.push(10);
llist.deleteNode(7);
llist.printList();
}
}
Output :
10
6
9

Categories