I need help understanding this. I know how to implement the queue but there is one small part bothering me. I drew on my notebook the flow of how things work but I don't get how the head has a nextNode without me setting it. How does the head end up pointing to the next node?
public void enqueue(T data){
count++;
Node<T> current = tail;
tail = new Node<>(data);
if(isEmpty()) {
///////////////////////////////////////////////////////////////////////////////
// when this runs, doesn't head.getNextNode point to null?
// if the list is empty, then tail is null.
// On the deque method, I can sout head.getNextNode() and I get data back, how?
///////////////////////////////////////////////////////////////////////////////
head = tail;
} else {
current.setNextNode(tail);
}
}
Below, the dequeing works fine, I think I'm having an issue understanding the whole reference/pointer thing
public T dequeue() {
if(isEmpty()) {
return null;
}
count--;
T dataToRemove = head.getData();
/////////////////////-+{[UPDATE]}+-////////////////////////////////
// WHERE DOES HEAD GET THE NEXT NODE FROM? THIS WORKS, BUT WHERE IS
// THE NEXT NODE COMING FROM IS WHAT I'M ASKING?
///////////////////////////////////////////////////////////////////
head = head.getNextNode();
return dataToRemove;
}
I figured it out:
When the list is empty, the head points to tail
then, when the enqueue method gets called again,
current will be pointing to tail, but head will still be pointing to tail reference
so now head is pointing to the same reference as current
in the else statement, current sets next node to the same reference the
head is pointing to. That's how the head gets its nextNode set.
When the method runs again, current will
point to another reference again but head will still be pointing to its original reference. BAM
public void enqueue(T data){
count++;
Node<T> current = tail;
tail = new Node<>(data);
if(isEmpty()) {
head = tail;
} else {
current.setNextNode(tail);
}
}
Related
Below is a program to reverse a doubly linked list in gfg.
When I try to submit, I get the following error
Your program took more time than expected. Time Limit Exceeded
Expected Time Limit 0.00sec.
Can anyone tell me how to decrease my program's time?
Code
public static Node reverseDLL(Node head)
{
Node p,cur;
p=head;
cur=head.next;
while(cur!=null)
{
if(cur.next==null)
{
p.next=cur.next;
cur.next=head;
head.prev=cur;
head=cur;
break;
}
p.next=cur.next;
cur.next.prev=p;
cur.next=head;
head.prev=cur;
head=cur;
cur=p.next;
}
return head;
}
Your code is not correct:
The prev member of the (original) tail node is not set correctly. It is not set in the if block, but in the previous iteration, where it got its value with cur.next.prev=p, which is setting it to the (original) head node. This creates an infinite loop in the data structure. In the end, none of the prev links is null. It might be that the testing framework keeps following those prev links in circles until a time out happens.
Also, the function assumes that head is not null. This may not be guaranteed.
There are also too many assignments happening. With a doubly linked list it is quite simple: just swap the value of next and prev in every node:
public static Node reverseDLL(Node head)
{
if (head == null) {
return null;
}
Node next = head.next;
while (next != null) {
head.next = head.prev;
head.prev = next;
head = next;
next = head.next;
}
head.next = head.prev;
head.prev = null;
return head;
}
this is classic solution for O(N) time, so I guess time constraint for this task is required to. solve it for O(1).
Take a look for this thread: https://www.google.com/url?sa=t&source=web&rct=j&url=https://discuss.codechef.com/t/reverse-a-doubly-linked-list-in-o-1/72850&ved=2ahUKEwjipdrQw_T1AhVYLTQIHahIDx8QFnoECBQQAQ&usg=AOvVaw1c0BDUotM0suEK7I4B9pQs
Environment:
java :java version "1.8.0_201"
os:Ubuntu 16.04.6 LTS Linux version 4.15.0-91-generic
Recently I read the source code of java.util.concurrent.ConcurrentLinkedQueue#offer, and I am confused with the code below.
public boolean offer(E e){
checkNotNull(e);
final Node<E> newNode = new Node<E>(e);
for (Node<E> t = tail, p = t; ; ) {
Node<E> q = p.next;
if (q == null) {
// p is last node
if (p.casNext(null, newNode)) {
......
when ConcurrentLinkedQueue is initialized, the item of head and tail is null.
public ConcurrentLinkedQueue() {
head = tail = new Node<E>(null);
}
but after I first invoke ConcurrentLinkedQueue#offer(with queue.offer(1)) and the code executed the line
p.casNext(null, newNode)(here p and head are the same reference), the reference of head was changed to newNode,and 'item' value of head was change to 1.
Detail of p.casNext is like this
boolean casNext(Node<E> cmp, Node<E> val) {
return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
}
It seems only next filed of head was modified in the cas method, but why was the reference of head changed?
Can you give me some explation? Thanks in advance!
It seems only next field of head was modified, how is the reference of head changed?
Correct, head.next was changed.
The head field of the queue is intentionally not modified by the offer(e) method.
The head and tail fields of the queue are never null, so when the queue is empty, they both refer to the same node, and that node has item = null. It is always valid for one or more nodes to have item = null. Those nodes will be skipped when querying or polling the queue.
It's all done that way to make the code thread-safe without the use of locking.
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?
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;
}
I tried implementing the insert method for circular linked list. I think I had some success.
Problem:
When I display the list. The display method will loop because every next variable of the link is linked to a non-null node object. So head will never be a null object. From what I recall about singly linked list, head always point to the first node in the list or the first node with data inside of it.
My conceptual understanding of circular linked list:
From what I can understand circular linked is somewhat like a singly linked list but with a slight twist: the next variable of the tail object points to the head.
I'm coding it like the diagram has presented provided by the source link.
Source: http://sourcecodemania.com/circular-linked-lists/
public void insert(String data)
{
Link link = new Link(data);
if(head == null)
{
head = link;
tail= link;
}
else
{
tail.next = link;
tail = link;
tail.next = head;
}
}
public void display()
{
// good implementation for display #2
while(head != null)
{
// System.out.println (head.data);
head = head.next;
}
}
Once you insert at least one element, you would never come across null. It will keep on going till infinity.
Also, it might not be a good idea to modify head just for displaying the list. Operation like display should not have any side effects.
In stead, keep a member field size in your list class and update it in each insert and delete method.
Now you would know how many times you should iterate the loop.
ListClassName current = head; // Head is not modified.
for (int i = 0; i < this.size; i++) {
// System.out.println (current.data);
current = current.next;
}
Good luck.
You can keep a reference to the first Link object and check to make sure head is not equal to this object while looping:
public void display()
{
boolean first=true;
Link firstItem=null;
// good implementation for display #2
while(head != null && head!= firstItem)
{
if(first){
firstItem=head;
first=false;
}
// System.out.println (head.data);
head = head.next;
}
}