Removing a node from a doubly linked list? - java

Here's the linked list code
public class LList
{
protected int size;
protected DNode tail, header;
public LList()
{
size = 0;
tail = null;
header = tail;
}
public void addDNode(DNode v)
{
// means list is empty, so add first element
if (header == null)
{
header = v;
tail = header; // first element so (head == tail)
}
else
{
tail.setNext(v);
v.setPrev(tail);
v.setNext(null);
tail = v;
}
size++;
}
and the remove node method that's the problem
public DNode removeDnode(DNode current)
{
if(current.nextNode() == null)
{
DNode previous = current.prevNode();
previous.setNext(null);
current.setPrev(null);
}
else if (current.prevNode() == null)
{
DNode next = current.nextNode();
next.setPrev(null);
current.setNext(null);
}
else
{
DNode next = current.nextNode();
DNode previous = current.prevNode();
previous.setNext(next);
next.setPrev(previous);
current.setPrev(null);
current.setNext(null);
}
size = size - 1;
return current;
}
The problem is that when I use previous.setNext(null); it won't let me add a node again which I think it has something to do with the header and tail.
however when i use previous.setNext(tail); it doesn't seem to remove it from the list??

Aren't you forgetting to point the tail of the list to the previous element when you remove the last?
if(current.nextNode() == null) {
DNode previous = current.prevNode();
previous.setNext(null);
current.setPrev(null);
tail = previous; //isn't this missing?!
}

Problem is, if you remove tail, you must move it. So try
DNode previous = current.prevNode();
previous.setNext(null);
current.setPrev(null);
tail = previous;
You have the same problem with the next—but I believe once you're done with tail, you'll fix it easily.

Handling remove from a doubly-linked list is VERY tricky. When you see a bug while testing, don't just make a "quick fix" -- go back and understand precisely why it didn't work, and understand how your intended fix will modify all behaviors, not simply the one that's currently misbehaving.
If you think about it carefully you can make it work with a minimum of problems. If you don't think carefully you'll be chasing your tail for hours.

Related

How to find the first value in a linked list?

I have a linked list I'm given and I need to find the first value in the list via a getFirst method.I need to display an error message and quit the program if the value is null. The linked list is already given to me link so:
class MyLinkedList
{
private class Node // inner class
{
private Node link;
private int x;
}
//----------------------------------
private Node first = null; // initial value is null
//----------------------------------
public void addFirst(int d)
{
Node newNode = new Node(); // create new node
newNode.x = d; // init data field in new node
newNode.link = first; // new node points to first node
first = newNode; // first now points to new node
}
//----------------------------------
public void traverse()
{
Node p = first;
while (p != null) // do loop until p goes null
{
System.out.println(p.x); // display data
p = p.link; // move p to next node
}
}
}
//==============================================
class TestMyLinkedList
{
public static void main(String[] args)
{
MyLinkedList list = new MyLinkedList();
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
System.out.println("Numbers on list");
list.traverse();
}
}
Here's what I tried out for the method:
public static Node getFirst(Node list)
{
if (list == null)
{
System.out.println("Error!");
System.exit(1);
}
return MyLinkedList.first;
}
I know this isn't exactly right, we just started this in my class so I'm having trouble understanding what's going on with it. Thank you!
I think you should look at https://docs.oracle.com/javase/7/docs/api/java/util/LinkedList.html and get an idea for the behavior of a linked list initially. Once you have an idea on how it behaves, you can think about how to add functionality around it. Right now you just have a single method which you call more than you should. What also might help is to create an interface and document it so you know what each method should do.
You should check that first isn't null in order to do what you describe in the question. Also, it is kind of weird that the first node autoreferences itself because usually it is left in null until you add another node
notice that the first value is linked to the first Node with is null. Then you have to check two things
Node == null (you got this)
Node.next == null (you have to do this)
When Node.next == null. It means that Node is first value because it is linked to the initial Node with is null.
Then you have
public static Node getFirst(Node list)
{
// if the list is empty
if (list == null)
{
System.out.println("Error!");
System.exit(1);
} else if(list.link == null) {
// this is the first value!
return list;
} else {
// keep searching recursive with the next Node
return getFirst(list.link);
}
}
The class MyLinkedList in your question follows the pattern of a stack data structure(At the time when i am writing this answer). That is: ever time you add a new element, the new element replaces the previously added element as the first element.
I guess you want to get 1 as your first element, if you have added elements 1,2,3 in that order. Correct me if i am wrong.
In that case your linked list and it's retrieval should be like this:
(Note: i have avoided private vars , public getter , settter , etc; to make code easily readable. But readers should add them.)
class Node{ int x; Node next; }
class LinkedList
{ Node head,tail;
void add(int y)
{ Node node = new Node();
node.x=y;
if(head==null)
head = tail = node;
else
tail = tail.next = node;
}
int getFirst()
{ if(head!=null)
return head.x;
else
throw new java.util.NoSuchElementException("List is empty");
}
}
If you look at java.util.LinkedList, you will find methods that are conventionally used in linked lists. If this is not a homework question, then i suggest you do not reinvent the wheel. Just use the existing libraries.
If you have to use the stack data structure, and you cannot change it, then i suggest you have to change your getFirst() like this:
int getFirst()
{ if(tail!=null)
return tail.x;
else
throw new java.util.NoSuchElementException("List is empty");
}
If you not allowed to add Node tail in your code, then your getFirst() will look like this:
int getFirst()
{ if(head==null)
throw new java.util.NoSuchElementException("List is empty");
Node node = head;
while(node.next!=null)
node=node.next;
return node.x;
}

could this remove method for Linkedlist work?

I asked my friends about this and they said you can never take the current to the previous node and when I asked why they didn't give me a clear reason can anyone help me?
//here is the signature of the method;
public void remove (){
Node<T> tmp = null;
tmp.next = head;
// I want to delete the current by the way!;
while (tmp.next != current)
tmp = tmp.next;
tmp.next = current.next;
//now am taking the current to the node before it so that it only becomes null if the linkedlist is empty;
current=tmp;
}
The basic idea is sound if you are looking to mutate an existing linked list and remove an element. Although there is a lack of curly braces after the while and a NullPointException that will occur at the first tmp.next.
Generally a remove method would be passed the data in the node to remove. It's unclear in your question what current is supposed to point to. Your code does not support removing the head of the list or empty lists (null head).
Here's a potential implementation:
public boolean remove(T value) {
if (head == null) {
return false;
} else if (head.value.equals(value)) {
head = head.next;
return true;
} else {
Node<T> current = head;
while (current.next != null) {
if (current.next.value.equals(value)) {
current.next = current.next.next;
return true;
}
current = current.next;
}
return false;
}
}
If the current variable is supposed to end up pointing to the node before the removed node then you have a problem: what happens if you are removing the head of the list?

Add to end of a doubly linked list

I am really struggling in adding a node at the end of a double linked list, adding to the head was easy, now I really can't find a way to add to the end. It seems stupid but I'm losing a lot of time, so if anyone can help that would be appreciated. Here's my partial implementation in Java.
public class DoublyLinkedList implements Iterable<Node>, Iterator<Node> {
private Node head = new Node();
private Node tail = head;
private Node current;
private Node previous;
#Override
public Iterator<Node> iterator() {
current = head;
previous = null;
return this;
}
public void addToHead(Node node) {
Node next = head.getNext(null);
Node previous = head.getNext(next);
head.setNext(previous, node);
node.setNext(null, head);
head = node;
}
#Override
public boolean hasNext() {
return current != tail;
}
#Override
public Node next() {
Node tmp = previous;
previous = current;
current = current.getNext(tmp);
return previous;
}
And here's Node Class
public class Node {
Node next = null;
Node previous = null;
Node getNext(Node node){
if(node == next){
return previous;
}
if(node == previous){
return next;
}
throw new IllegalStateException("something went wrong");
}
void setNext(Node node, Node next){
if(node == previous){
previous = next;
}
else if(node == this.next){
this.next = next;
}
else{
throw new IllegalStateException("something went wrong");
}
}
}
I hope you will understand the code above, basically I will need the following :
public void addToEnd(Node node) {
// implementation
}
What interface are you aiming to implement on this double-linked list? The way you've implemented getNext and setNext is not a clear way of doing it. If you only have one node, and you attempt to setNext with a null value for node, there is no way of telling unless you look at the code where your new node is going to end up. Along with that, I'm not sure if there is a nice way for you to set the previous node of a new tail node with the way you've implemented it. It's very ambiguous.
Can I recommend that you instead implement getNext(), setNext(Node), getPrevious() and setPrevious(Node) methods in your Node class? This would greatly simplify and clear up your code.
You would then be able to implement your addToEnd(Node) method very simply.
public void addToEnd(Node node) {
node.setPrevious(tail);
tail.setNext(node);
tail = node;
}
If you want to be able to iterate through the list in both directions using 'getNext()' and 'hasNext()', you could provide a forward or reverse Iterator implementation. There are examples of how to create Iterators floating around. The first answer on this question has an example of a reverse iterator.
The basic idea is just the same as adding at the beginning of the list.
We can split it up into two parts:
adding the item to the list
updating the head/tail-node of the list
In pseudocode:
node to_insert
//step 1
tail.setNext(to_insert)
to_insert.setPrevious(tail)
//step 2
tail = to_insert

Confusion over how Java's Garbage Collector works (Nodes + Queue) [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 7 years ago.
So I been trying to implement LinkedList, Stack, Queue in Java.
For each one i'm using a node class such that, now I don't really want to discuss how my implementation is since I am aware there are better ways to do it, I just want to focus on my question.
public class Node<E> {
private E data;
private Node<E> next;
private Node<E> prev;
public Node(E data) {
this.data = data;
this.next = null;
this.prev = null;
}
public E getData() {
return this.data;
}
public Node<E> getNext() {
return this.next;
}
public Node<E> getPrev() {
return this.prev;
}
public void setPrev(Node<E> prev) {
this.prev = prev;
}
public void setData(E data) {
this.data = data;
}
public void setNext(Node<E> next) {
this.next = next;
}
}
Now with the node class there, I keep getting mixed up on how the garbage collector works, so lets say this is my queue class
public class Queue<E> {
private int size;
private Node<E> head, tail;
public Queue() {
this.size = 0;
this.head = this.tail = null;
}
public Queue(E data) {
Node<E> temp = new Node<E>(data);
this.tail = this.head = temp;
this.size = 0;
}
public boolean enqueue(E data) {
Node<E> temp = new Node<E>(data);
if (this.head == null) {
this.tail = temp;
this.head = temp;
} else {
temp.setNext(this.head);
this.head.setPrev(temp);
this.head = temp;
}
this.size++;
return true;
}
public E dequeue() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else {
E data = this.tail.getData();
this.tail.setPrev(null);
this.tail = temp;
this.tail.setNext(null);
this.size--;
return data;
}
}
public int getSize() {
return this.size;
}
public E peak() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else
return this.tail.getData();
}
public boolean contains(E data) {
if (this.head == null)
return false;
else {
for (Node<E> cursor = this.head; cursor != null; cursor = cursor
.getNext()) {
if (cursor.getData().equals(data))
return true;
}
}
return false;
}
}
Now I am getting how the garbage collector works confused. I have heard, it will clean up any references that don't get pointed too. So I keep getting nullpointerexception on my dequeue class on the part that does the
this.tail.setNext(null);
now, hearing that for garbage collector to work, nothing can reference it, so I thought to myself my nodes are set up like this
head tail
null<-[1]-><-[2]-><-[3]->null
where each node can point to next and to previous, so for my dequeue I think I have to do few things
1) get the data (that is easy)
2) get a temp Node that points to previous
Node<E> temp = this.tail.getPrev()
3) now here is where I start to get lost, in order for each node to no longer be referenced, I have to get rid of all things pointer to it, so this means that I must set to null the
this.tail.setPrev(null);
since when I delete the node after that, I can't go backwards to erase that reference
head tail
null<-[1]-><-[2]-> null<-[3]->null
<-[temp]-> ( equals node [2])
4) Is set tail to point at temp node, which is what the prev node was
this.tail = temp;
no it should look like this
head tail
null<-[1]-><-[2]->(this still points to [3]) null<-[3]->null
5) but the second node still points to the memory address of [3], so i continue to
this.tail.setNext(null);
in order to make it so nothing at all references any spot of memory no longer in us,
head tail will be deleted by GC
null<-[1]-><-[2]->null null<-[3]->null
However, THIS PART gives me NullPointerException when there is only one node left in queue.
Now, I know I may be wrong on a lot of this, I am still learning, but I am jsut not sure how much stuff I have to do to each node to make sure garbage collector gets it so any help will do, do i need to set both prev and next to null? or only one? etc, so any help will be appreciated, thank you ;)
You don't really need to care about how the garbage collector works. If your list implementation is correct then the garbage collector will function correctly.
Your NullPointerException will be caused by a logic error. Nothing to do with garbage collection.
Your head and tail references in the queue should reference the first and last elements.
Each node should correctly point to previous and next elements. Your logic should recognise the beginning and end of the list and should correctly handle insert and deleting of nodes.
If you get that right from a functional point of view, then deleted nodes won't be referenced by anything and the garbage collector will clean it up.
Concentrate on writing unit tests for edge cases (empty lists, one node lists) and test the operations insert and delete.
Once it is functionally correct, the garbage collection will work ok.
EDIT:
In a long list, inner nodes will have a previous and last element, but the head and tail don't, so you need special logic to deal with deleting them.
If the list has one element, the head and tail are the same, so both the head and tail special logic will apply to that one node.
There is bug in your code. It has nothing to do with Garbage Collector.
You get NullPointerException because this.tail is null in your example when you have only one node in the queue. You assign temp = this.tail.getPrev(); which is null for one node only. Then you assign this.tail = temp;. Below you will find right implementation of dequeue().
You don't have to, but maybe some people would consider this a good practice to set everything to null in deleted node.
public E dequeue() {
if (this.tail == null)
throw new IndexOutOfBoundsException();
else {
E data = this.tail.getData();
Node<E> temp = this.tail;
this.tail = temp.getPrev();
if ( this.tail == null ) { // if that was last node
this.head = null;
return data;
}
this.tail.setNext(null);
temp.setPrev(null);
temp.setNext(null);
this.size--;
return data;
}
}
In the method enqueue() you check head for an emtpy queue. But in the method dequeue() you check tail for the same. It might be little confusing. You should probably check both for null. It's additional test of your program.
There is also a bug in constructor. this.size should be set to 1 not 0.
public Queue(E data) {
Node<E> temp = new Node<E>(data);
this.tail = this.head = temp;
this.size = 1;
}

LinkedList remove(), next, prev and head

I need to remove() from a (custom) LinkedList, setting last iterated item to null. There are two cases, one where prev is head, one where it is not. The nodes are next, prev and head. I was wondering if you spot any problems in the following code? Do I need to set the iterator variables in between? (or will emptying prev do the trick?) Thanks guys!
public void remove() {
if(prev == null) {
throw new IllegalStateException();
}
else {
if(prev == head){
head = head.next;
prev = null;
sizeOfList--;
}
else {
prev = null;
sizeOfList--;
}
}
}
In case your previous is not the head(second case in your code) you should fix the next link of the previous node before prev. Otherwise it will be left pointing to prev.

Categories

Resources