Implementing a remove method for a doubly linked list - java

I'm trying to implement a doubly linked list for a class assignment. I am currently stuck on implementing a method to remove a node at a specified index.
public void remove(int index) {
if (index < 0 || index > count-1) {
throw new ListIndexOutOfBoundsException("The index "+index+" is out of bounds.");
}
if (isEmpty()) {
System.out.println("List is empty");
return;
}
MedicationNode curr = head;
int k = 0;
while(k < index) {
curr = curr.next;
k++;
}
if (curr.prev == null) {
curr.next.prev = null;
}else if(curr.next == null) {
curr = curr.prev;
curr.next = null;
}else{
curr.next.prev = curr.prev;
curr.prev.next = curr.next;
}
count--;
}
the method can remove any specified node in a linked list except index 0. I'm thinking the problem may be with my add method but im not really sure.

In your first if condition -
if (curr.prev == null) {
curr.next.prev = null;
//Make your head of the linked list point to this node
head = curr.next;
}
This is because you are removing the head from the list so the head should point to the head's next node.

Related

Removing an element by index in a linked list

A method for removing an element from a linked list has been implemented:
public void remove(T e) {
Node<T> node = first;
Node<T> prevNode = null;
while(node != null){
if(e.equals(node)){
if(prevNode == null) {
first = node.next;
}
else {
prevNode.next = node.next;
}
size--;
}
else {
prevNode = node;
}
node = node.next;
}
}
How to correctly implement deleting an element by index? Using the capabilities of the remove method.
public void removeByIndex(int i) {
remove(i);
}
Something like this should work:
public void remove(int i) {
if (i >= size || i < 0) {
throw new ArrayIndexOutOfBoundsException();
}
Node<T> remove;
if (i == 0) {
remove = first;
first = first.next;
first.prev = null; // <- For double linked list
} else {
Node<T> node = first;
for (int j = 0; j < i - 1; ++j) {
node = node.next;
}
remove = node.next;
if (i == size - 1) {
node.next = null;
} else {
node.next.next.prev = node; // <- For double linked list
node.next = node.next.next;
}
}
// Clear links from removed Node
remove.next = null;
remove.prev = null; // <- For double linked list
size--;
}
Find the node before position i. Point that node to the "over next" node.
Edit
The original code example was a rough sketch at best. Updated with a more complete version.
Edit #2
Slight improvements:
Clears links from the removed node
Also handles double linked lists
remove(get(i));
This is not the most efficient solution, but a simple one which uses the remove(T) method. You call it with get(i) as the object to be removed - which is the element at the specified index.
Note: This solution has some issues if the list has duplicate values, but in that case you shouldn't use the remove(T) method anyway. If you want it to be safe, iterate to the specified index:
Node<T> node = first;
for(int i=0;i<index;i++){
prevNode=node;
node=node.next;
}
and do this:
node.prev.next=node.next;
node.next.prev=node.prev;
size--;
Of course, this is just a rough implementation. To ensure full compability, you should check if the index is valid and use the unlink(Node) method of LinkedList.
The LinkedList also has an implementation for the remove(int) method:
checkElementIndex(index);
return unlink(node(index));

Doubly Circular Linked List

I created a Doubly Circular Linked List.
I need to know the distance from every node to head.
Because when I must delete or get a node with a specific key, if 2 nodes have the same key and same distance, both must be deleted or got, otherwise must be deleted the node closest to head.
I don't know how to calculate the distance because is circular ...
The insertion of this Linked List work in this way.
All the nodes go after the Head.
Example :
1) Head
2) Head-A (Inserted A)
3) Head-B-A (Inserted B)
4) Head-C-B-A (Inserted C)
For now, i did only a normal cancellation without the distance.
This is my code.
/* Function to delete node with the key */
public void deleteWithKey(int key) {
if (key == head.getData()) {
if (size == 1) {
head = null;
end = null;
size = 0;
return;
}
head = head.getLinkNext();
head.setLinkPrev(end);
end.setLinkNext(head);
size--;
return;
}
if (key == end.getData()) {
end = end.getLinkPrev();
end.setLinkNext(head);
head.setLinkPrev(end);
size--;
}
Node current = head.getLinkNext();
for (int i = 2; i < size; i++) {
if (key == current.getData()) {
Node p = current.getLinkPrev();
Node n = current.getLinkNext();
p.setLinkNext(n);
n.setLinkPrev(p);
size--;
return;
}
current = current.getLinkNext();
}
System.out.println("Don't exist a node with this key");
}
Thanks to All.
You actually don't need to know the distance. Rather, you need to find the closest to head.
Since it's a circular doubly linked list, this task is trivial:
define two variables a and b, initializing both to head
if either are the target, remove matching nodes and exit
assign a = a.next and b = b.previous
goto 2
This is the pseudocode I could think of to solve the problem.
Given head,
// no data
if(head==null) return;
// next and prev are always at same distance
next = head;
prev = head.prev;
// ensure nodes are not same or crossed half way through the list
while (next == prev || next.prev == prev){
// delete nodes if values are same
if (next.val == prev.val){
if(next!=head) {
next.prev.next = next.next;
next.next.prev = next.prev;
prev.prev.next = prev.next;
prev.next.prev = prev.prev;
}
// list has only two nodes
else if(head.next==prev){
head = null;
return;
// remove head and its prev node
else{
head = head.next;
head.prev = prev.next;
head.prev.next = head
}
}
// traverse through the list
next = next.next
prev = prev.prev
}
This is the final working code that i did.
Have you improvements?
Thanks to all for the help.
Complexity = O(n)
/* Function to delete node with the key */
public void deleteWithKey(int key) {
if (key == head.getData()) {
if (size == 1) {
head = null;
end = null;
size = 0;
return;
}
head = head.getLinkNext();
head.setLinkPrev(end);
end.setLinkNext(head);
size--;
return;
}
if (key == end.getData()) {
end = end.getLinkPrev();
end.setLinkNext(head);
head.setLinkPrev(end);
size--;
}
Node next = head;
Node back = head;
while (next != end) {
next = next.getLinkNext();
back = back.getLinkPrev();
if ((key == next.getData()) && (key == back.getData()) && (next != back)) {
Node p = next.getLinkPrev();
Node n = next.getLinkNext();
Node p1 = back.getLinkPrev();
Node n1 = next.getLinkNext();
p.setLinkNext(n);
n.setLinkPrev(p);
p1.setLinkPrev(n1);
n1.setLinkPrev(p1);
size -= 2;
return;
}
if ((key == next.getData()) && (next != back)) {
Node p = next.getLinkPrev();
Node n = next.getLinkNext();
p.setLinkNext(n);
n.setLinkPrev(p);
size--;
return;
}
if ((key == next.getData()) && (next == back)) {
Node p = next.getLinkPrev();
Node n = next.getLinkNext();
p.setLinkNext(n);
n.setLinkPrev(p);
size--;
return;
}
}
System.out.println("Don't exist a node with this key");
}

Remove the node that contains the key from the Linked list

I have a remove method , find the element in that list and delete it.That topic is about Doubly linked list.Is my operation true in if and else if statement for the formation of the new list?
public void Remove(int key) {//key = number in the list.
if (head == null) {
System.out.println("Empty List..!");
} else if (key == head.key) {
head.prev.next = head.next;
head.next = null;
noOfNodes--;
} else if (key == tail.key) {
tail.next.prev = tail.prev;
tail.prev = null;
noOfNodes--;
} else {
for (LinkedListNode temp = head; temp.next != null; temp = temp.next) {
if (temp.key == key) {
temp.prev.next = temp.next;
temp.next.prev = temp.prev;
temp.prev = null;
temp.next = null;
noOfNodes--;
}
}
}
}
From your code, it looks like tail is the last node of your linked list.
So, this might be the issue tail.next.prev = tail.prev;. Since tail.next is null as tail is the last element, when you access prev on tail.next it throws NullPointerException.

How would I convert my custom linked list add method into an insert sort method in java?

I have this custom linked list in java (not java collections LinkedList). It adds the item in at the selected position in the list.
public void add(T item, int position) {
Node<T> addThis = new Node<T>(item);
Node<T> prev = head;
int i;
if(position <= 0) {
//throw new ListException("Cannot add element before position 1");
System.out.println("Error: Cannot add element before position 1.");
}
else if(position == 1) {
addThis.setNext(head);
head = addThis;
} else {
for(i = 1; i < position-1; i++) {
prev = prev.getNext();
if(prev == null) {
//throw new ListException("Cannot add beyond end of list");
System.out.println("Cannot add beyond end of list");
}
} // end for
addThis.setNext(prev.getNext());
prev.setNext(addThis);
}
} // end add
What I want to do is instead of adding the item at a selected position, I want the item to be added in alphabetically so the whole list will be sorted alphabetically at insertion. This is the code I have so far but I am stuck. How would I get it to do what I want?
public void add(String title) {
DvdNode addThis = new DvdNode(title);
if (head == null) {
head = addThis;
} else if(title.compareToIgnoreCase(head.getTitle()) < 0) {
// title comes before the current, so add as the first Dvd in the list
} else {
// the new title belongs somewhere later in the list
// while i less than size of the list compare and insert if its greater
// than what its being compared to
// also update the links for the list so the whole list is still accessible
}
}
EDIT:
I finally got it to work. My solution is based off of Byakuya's solution.
public void add(Dvd item) {
DvdNode addThis = new DvdNode(item);
if(head == null) {
head = addThis;
} else if(item.getTitle().compareToIgnoreCase(head.getItem().getTitle()) < 0) {
addThis.setNext(head);
head = addThis;
} else {
DvdNode temp;
DvdNode prev;
temp = head.getNext();
prev = head;
while(prev.getNext() != null && item.getTitle().compareToIgnoreCase
(prev.getNext().getItem().getTitle()) > 0) {
prev = temp;
temp = temp.getNext();
}
addThis.setNext(temp);
prev.setNext(addThis);
}
}
Create something like this:
public void add(String title){
DvdNode addThis = new DvdNode(title);
DvdNode iter = head;
DvdNode prev = null;
while (iter && addThis.name.compareTo(iter.name) < 0){
prev = iter;
iter = iter.getNext();
}
addThis.setNext(iter);
addThis.setPrev(prev);
if (prev)
prev.setNext(addThis);
if (iter)
iter.setPrev(addThis);
if (head == null)
head = addThis;
}
If head is null, then you will simply set addThis next and prev to null and update head. Otherwise the iterator will stop at the first element which name is lexicographically greater or equal to your String title (so if your String title is the greatest yet in the list, the iter will stop at null, and you'll insert your element as the last one in the list, which is correct) and the prev will be the last element before iter. Then proper linking takes place.
My exaple fits with a double-direction list. If you want it to work for a single-direction list, just remove setting Prev's of addThis and iter.

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