Add to end of a doubly linked list - java

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

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

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

Making first object with a recursive class defination [duplicate]

This question already has answers here:
Java final modifier
(2 answers)
Closed 8 years ago.
I was experimenting a bit in java and stumbled across this problem
Suppose i have a class with this recursive defination
public class Node<T> implements Iterable<T>{
public final T element;
public final Node<T> next;
public Node(T head, Node<T> tail) {
this.element = head;
this.next = tail;
}
// Contains few more methods and implementation of iteratable like add, remove etc
}
Now, the thing is I will be using this class as a field in another class with final keyword. Now if in the beginning i would be making an empty list and then add it to the list, how should i proceed.
TO make it simple
class NodeList <T>{
private final Node<T> head;
public NodeList(){
}
// Few more functions
}
Using NodeList class how can i create an empty list and later on add data using add function
In java reference works as pointer to an object in memory that internally can point to another one in the same way.
Let's try to understand it visually:
What happens to the pointer head when the object obj is added to an empty linked list?
You have to remove final keyword from head because it's reference that changes every time when new node is added to point the new node.
In below snapshot head is a reference that point to first object in the memory and first object contains another reference next that points to second object and so on...
how should i proceed.
create a new node
point next of new node to next of head
point head to new node
Sample code:
class Node<T> {
public final T element;
public final Node<T> next;
public Node(T head, Node<T> tail) {
this.element = head;
this.next = tail;
}
}
class NodeList<T> {
private Node<T> head;
public void add(T value) {
if (head != null) {
Node<T> node = new Node<T>(value, head); // create a new node
head = node; // point `head` to new node
} else {
// if head is null then assign it to head
head = new Node<T>(value, null);
}
}
}
NodeList<String> nodeList = new NodeList<String>();
nodeList.add("First");
nodeList.add("Second");
nodeList.add("Third");
// print all nodes
Node<String> node = nodeList.head;
while (node != null) {
System.out.println(node.element);
node = node.next;
}
output:
Third
Second
First
You cannot do it with the final keyword on the head attribute, since it will force you to initialize it during the instanciation : you should then initalize it to null to represent the empty list and won't be able to append an element to it. Remove the final keyword, it has no use there.
I'm not even convinced of the use of final in your Node class. What if you want to add or remove an element in the middle of the list ? Using final there limits considerably the number of operations you can perform on your data structure.

Linked List in Java method implementation

Now Im preparing for coding interview and I have 1 question regarding linked list in Java. Can you tell me some reliable sources from which I can learn and practice the basic linked list methods. I liked this one: www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/code/LinkedList.java but I'm confused with some method implementations. For example the method E get(int pos) returns NOT node but the data E contained in the node at position pos. While here http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7u40-b43/java/util/LinkedList.java#LinkedList the method Node node(int index) returns the node at that position (not the data contained in it). Which implementation should I follow?
Well the Data Structures is a very conceptual and context based discipline. The various implementations that exist for each data structure are based on the requirements and scope of the data structure.
One can even argue that the LinkedList implementation of the Collection API is buggy as it doesn't work well if multiple threads work on it concurrently. Then a synchronizedList from the Collections class need to be made or at least an implementation which works well with multiple threads need to be used.
Follow the minimum viable convention that gets you going, as the interviewer won't just ask you a LinkedList implementation. What the interviewer wants to know is if your concepts and coding skills are up to a certain mark or not.
Think of what you can do with a Linked List. For that you'll have to think as to what type of LinkedList you are actually considering as there are many different kinds of LinkedLists, like SinglyLinkedList, DoublyLinkedList, SkipList, etc.
Considering a SinglyLinkedList, your LinkedList implementation should at least have the following methods: add(), remove(), contains(), clear(), size().
Following is my implementation of a SinglyLinkedList:
import java.util.Iterator;
import java.util.StringJoiner;
public class LinkedList<T> implements Iterable<T>
{
private Node head;
private Node tail;
private int size;
private class Node
{
private T value;
private Node next;
public Node(T value)
{
this.value = value;
}
}
public void add(T value)
{
Node node = new Node(value);
if (head == null)
{
head = node;
}
else
{
tail.next = node;
}
tail = node;
++size;
}
public boolean remove(T value)
{
Node previous = null;
Node current = head;
while (head != null)
{
if (current.value.equals(value))
{
if (previous != null)
{
previous.next = current.next;
if (current.next == null)
{
tail = previous;
}
}
else
{
head = current.next;
if (head == null)
{
tail = null;
}
}
--size;
return true;
}
previous = current;
current = current.next;
}
return false;
}
public boolean contains(T value)
{
Node current = head;
while (current != null)
{
if (current.value.equals(value))
{
return true;
}
current = current.next;
}
return false;
}
public void clear()
{
head = null;
tail = null;
size = 0;
}
public int size()
{
return size;
}
#Override
public Iterator<T> iterator()
{
return new Iterator<T>()
{
private Node current = head;
#Override
public boolean hasNext()
{
return current != null;
}
#Override
public T next()
{
Node next = current;
current = current.next;
return next.value;
}
};
}
#Override
public String toString()
{
StringJoiner joiner = new StringJoiner(", ");
for (T value : this)
{
joiner.add(value.toString());
}
return joiner.toString();
}
}
As you can see, my implementation may be different from the implementations that you may find elsewhere. Small differences are not any issue as long as the data structure's concepts aren't radically changed and as long as it works properly with its defined interface.
For disciplines like Data Structures, you have to think for yourself and based on your requirements, use or implement data structures that fit your needs. As far as interviews go, an implementation of a minimum viable data structure is all that you need to show and all that is required. Just make sure that such minimum viable data structure isn't buggy in its context.

Removing a node from a doubly linked list?

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.

Categories

Resources