Questions about traversing, inserting, and deleting nodes - java

Just started learning nodes and I have a few questions. Let's say I have a node class that looks like this:
private E number;
private Node next;
/**
* Constructor
*/
Node(E e){
number = e;
next = null;
}
and I have a series of linked nodes with the starting node named first like so:
first->(1)->(2)->(3)->(5)->(6)
Assuming the list is not empty, to traverse the list, I would do something like so:
Node curr = first;
while(curr != null){
System.out.print(curr);
curr = curr.next;
}
I understand that you cannot traverse a link list like this backwards, so does this mean that whenever I call curr.next the previous elements are just lost?
I was also wondering if first, my original list, will ever be affected by a temporary node list like curr? For example, if I was to insert or delete a node on the list with codes similar to these:
Insert:
Node curr = first;
Node newNode = new Node(4);
while(curr != null){
if(curr.number == 3){
newNode.next = curr.next;
curr.next = newNode;
}
curr = curr.next;
}
Delete:
Node curr = first;
Node parent = first;
while(curr != null){
if(curr.number == 3){
parent.next = curr.next;
}
parent = curr;
curr = curr.next;
}
Would the above code modify first or would I have to set first = curr; after the insertion or deletion for the changes take place? If they do modify first, how come curr = curr.next; doesn't modify first? What if I wanted to return the deleted node? Would I just do something like curr.next = null; and then return curr;?

does this mean that whenever I call curr.next the previous elements are just lost?
As you traversed down the list, you never modified first and you never modified the next link of any Node. So no node is ever really lost. Previously visited nodes just become a bit less convenient to access because you don't have a "handle" on them, but you could always find them again by starting back at the head of the list (first) and following the next links.
I was also wondering if first, my original list, will ever be affected by a temporary node list like curr?
The variable first will never be affected by anything other than an assignment statement having first on the left-hand side of the =. On the other hand, the list to which first refers might change quite dramatically or be completely unaffected, depending on what you are using curr for, but first itself can only be affected by assigning a new value to first.
Would the above code modify first or would I have to set first = curr;
The insert and delete code, as given, never modifies first. But it should, if it is going to be able to handle inserting a new node before the current first node, or deleting the current first node. Maybe not with first = curr specifically, but first will definitely need to be updated to reflect a new head of the list. I leave that, as the textbooks say, "as a exercise for the reader".

Related

Reversed Linked List only prints as one node

I was coding on gfg practice to test whether a linked list is a palindrome, and after I reversed the linked list, it showed only one node.
This is the code below (the linked list passed is 1->2->1) :-
class Solution
{
//Function to check whether the list is palindrome.
boolean isPalindrome(Node head)
{
Node temp=head;
Node reversed= reverseList(temp);
Node cur = head;
while(cur!=null)
{
System.out.println(cur.data+" inloop");
cur=cur.next;
}
return true;
}
Node reverseList(Node node)
{
Node prev = null;
Node current = node;
Node next = null;
while (current != null) {
next = current.next;
current.next = prev;
prev = current;
current = next;
}
node = prev;
return node;
}
}
The output of this code is ( the linked list passed is 1->2->1) : -
1 inloop
But if I comment out the Node reversed = reverseList(temp); line, the output is as expected: -
1 inloop
2 inloop
1 inloop
Where is my mistake?
When the list is reversed, the previous head of the list has become the tail. Concretely, after that reversal, head references the tail of the now reversed list. A tail has (by definition) a next member that is null, and so your while loop will only iterate once.
Realise that when you reverse the list, you don't have the original order anymore: all next members have lost their original value and now (only) have a reference that reflects the reversed order.
You have a few options to do it right:
Have your reverse method create a new list, so that caller will then have both the original and the reversed list. This will allow you to iterate both lists in tandem and compare the node values as intended
Create an array from the list. Now check whether the array is a palindrome.
The above options need O(n) extra space. You can solve this without O(n)
auxiliary space: only reverse half of the list. Here you don't need to create new nodes, but by only reversing half of the list, you can traverse the reversed part and the non-reversed part in tandem and make value comparisons

Flatten a Multilevel Doubly Linked List leetcode

You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below.
Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list.
class Solution {
/*Global variable pre to track the last node we visited */
Node pre = null;
public Node flatten(Node head) {
if (head == null) {
return null;
}
/*Connect last visted node with current node */
if (pre != null) {
pre.next = head;
head.prev = pre;
}
pre = head;
/*Store head.next in a next pointer in case recursive call to flatten head.child overrides head.next*/
Node next = head.next;
flatten(head.child);
head.child = null;
flatten(next);
return head;
}
}
For the question, the above leetcode solution works. But I don't understand this:
/*Store head.next in a next pointer in case recursive call to flatten head.child overrides head.next*/
Node next = head.next;
Can anyone explain this part? How does the head.child override head.next?
flatten(head.child) may change head.next due to the part where we connect last visited node with the current node:
if (pre != null) {
pre.next = head;
head.prev = pre;
}
At this stage, pre represents older head and head represents head.child. So we actually connect our head with its child. But if we do so, we will lose our connection to the actual head.next, therefore we have to keep it in an extra variable called next. That's why we keep it before calling the flatten() function.

How a java class works with it's own reference type

I'm fairly new to JAVA and OOP and I'm currently following an academic course where I'm learning data structures and algorithms in java.
As I was learning about implementation of linked lists I've ran into a small problem of not understanding the code how to create a node when implementing a linked list(I'm familiar with constructors and bit of recursion ).
Code of the Node class as follows
public class Node {
public int info;
public Node next, prev;
public Node (int el) {
this (el,null,null);
}
public Node (int el,Node n,Node p){
info = el; next =n; prev=p;
}
}
I need to know what's happening behind the scene when the code executes(especially how the line3 works)and code of the List class is as follows
public class List {
private Node head, tail;
public List ( ){
head = tail = null;
}
public boolean isEmpty( ){
return head == null;
}
public void addToTail (int el) {
if (!isEmpty ( )) {
tail = new Node (el, null, tail);
tail.prev.next = tail;
}
else head = tail = new Node(el);
}
public int removeFromTail ( ){
int el = tail.info;
if (head == tail)
head = tail =null;
else
{
tail = tail.prev; tail.next = null;
}
return el;
}
}
(This example is given by the academy where I'm learning and I want to know how it works)
Please explain how the Node class works.
Ok let's start from the Node Class
public Node next, prev;
public Node (int el) {
this (el,null,null);
}
Here the objects next and prev are references to the next and previous nodes to the current node (which is your current object (this))
this (el,null,null);
It means you are creating a node which has no previous or next node. as you pass null, null for next and previous. Its similar to creating a head as head doesn't have next and previous nodes.
When you create the head you will never change it but you will change the next of the head the time when you create the second element in your list
When you create a tail of the linked list
public void addToTail (int el) {
if (!isEmpty ( )) {
tail = new Node (el, null, tail);
tail.prev.next = tail;
}
else head = tail = new Node(el);
}
here you first create a tail Node by tail = new Node (el, null, tail);
And then get the previous of tail and set the next element of the prev as the tail by doing tail.prev.next = tail;
Every time you add a new Node to the list you are calling addToTail(int e1) which updates the tail and updates the next of the old tail.
Let's suppose the entry point of your code is a call to:
List list = new List();
list.addToTail(1);
list.addToTail(2);
list.removeFromTail();
The first line of code execute the constructor into the list class, which simply set head and tail equal to null.
The second line of code add the method addToTail, and set the variable head and tail equal to new Node(1), which invoke the constructor having one parameter only in the Node class.
The third line of code add the method addToTail, but this time the list is not empty, so the script enters in the if statement and the node that you save with Node(2, null, tail). If you check this Node constructor with three parameters, tail (the node you added before which value was 1) will be set as the prev (previous) node associated with the current one.
The last line of code remove the 2nd node with the code tail = tail.prev; tail.next = null; and return the element you just removed.
firstly node in java its a specific type of variable, which can do the normal work of a variable and have an other information allow her to point on an other node in your case is public Node next, prev;
usually node are used in list ,think like train this is why you have addtootail here or removefromtail .
in this link you find a pretty quite explanation about that.
http://www.vias.org/javacourse/chap14_02.html
The Node class encapsulates the data, in this case an integer value named info.
Also, the List is implemented as a sequence of Nodes.
The reason for the next and prev values in Node is so that the node can be linked to its previous and next nodes in the list sequence. The only way to access a node in the list is to traverse the list, either forwards using the next reference, or backwards using the prev reference.
Note that lists do not support random access like an array would. To get to an element, you need to traverse the list in some direction.
I need to know what's happening behind the scene when the code executes(especially how the line3 works).
Please explain how the Node class works.
public Node next, prev;
Java variables that refer to object types are references, not values. The fields next and prev are not Node objects. No Node object contains other Nodes. Instead, next and prev reference Node objects stored separately.
If you're coming from C or C++, you can think of these references somewhat like pointers.
EDIT:
If you're instead asking about the construction of linked lists in general, your Node class represents a single node, containing a single item, in a doubly linked list. In a doubly linked list, each node refers to the node before and the node after.
See this illustration of a doubly linked list in this related wikipedia article.
The List is like a train. A Node is like a car in the train. The links are references between Nodes, which are similar to the coupling between train cars. The next and prev fields that you ask about are the links between nodes.
Logically, the List contains the entire train. Physically, your List references the head and tail of the train, so that it can travel over the Nodes from front-to-back, or back-to-front.

I can't add a node into an empty list

I stumbled upon a weird problem with adding two linked lists into a third one in Java, the first linked list "myList1", the second linked list "myList2" and the third one "myList3".
The combining method is supposed to to add the first LinkedList "myList1" then the second "myList2" into the third LinkedList "myList3", but I faced a problem with adding them to third list while it's empty, but if the third list has at least one element every thing goes smoothly.
The code:
Node current = myList1.head;
while (current != null) {
Node newcurrent = myList3.head;
int h1 = current.getData();
Node newNode = new Node(h1);
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
else {
if (newcurrent.getLink() == null) {
newNode.setLink(newcurrent.getLink());
newcurrent.setLink(newNode);
} else {
Node current11 = newcurrent;
while (current11.getLink() != null) {
current11 = current11.getLink();
}
current11.setLink(newNode);
}
}
current = current.getLink();
}
The node is not added to the third LinkedList if the third list is empty, and I tried many other codes but it didn't work either, but if I entered at least one element to the third LinkedList the list is added normally.
other codes I tried :
newcurrent.setLink(newNode);
and
newNode = newcurrent;
newcurrent = newNode;
and
newNode.setLink(newcurrent);
newcurrent.setLink(newNode);
and
newNode.link = newcurrent;
newcurrent.link = newNode;
You're overcomplicating this a bit, I think. The links within the list are already there. You only need to link myList3.tail to myList1.head, no need to loop through adding each node independently. Since you don't look like you are storing a tail, you'll need to iterate to the end of myList3 to find it.
if (myList3.head == null)
myList3.head = myList1.head;
else {
Node list3iter = myList3.head;
while (list3iter.getLink() != null) {
list3iter = list3iter.getLink();
}
list3iter.setLink(myList1.head);
}
}
One further note, I find it painful to try to keep track of names like current, current11, newcurrent, etc. They all mean just about the same thing to my brain. If you're like me, a bit more descriptive naming might help you keep track of what your variables are meant to be doing here.
Node newcurrent = myList3.head;
....
if (newcurrent == null)
//the problem is with this code
newcurrent = newNode;
Not sure why you have two lists, but the last line above is just assigning to local variable. Should it be as follows instead?
myList3.head = newNode

Java, LinkedList of Strings. Insert in alphabetical order

I have a simple linked list. The node contains a string (value) and an int (count).
In the linkedlist when I insert I need to insert the new Node in alphabetical order. If there is a node with the same value in the list, then I simply increment the count of the node.
I think I got my method really screwed up.
public void addToList(Node node){
//check if list is empty, if so insert at head
if(count == 0 ){
head = node;
head.setNext(null);
count++;
}
else{
Node temp = head;
for(int i=0; i<count; i++){
//if value is greater, insert after
if(node.getItem().getValue().compareTo(temp.getItem().getValue()) > 0){
node.setNext(temp.getNext());
temp.setNext(node);
}
//if value is equal just increment the counter
else if(node.getItem().getValue().compareTo(temp.getItem().getValue()) == 0){
temp.getItem().setCount(temp.getItem().getCount() + 1);
}
//else insert before
else{
node.setNext(temp);
}
}
}
}
Ok so this is inserting all my strings, but not in alphabetical order. Do you see any error?
public Node findIsertionPoint(Node head, Node node){
if( head == null)
return null;
Node curr = head;
while( curr != null){
if( curr.getValue().compareTo(node.getValue()) == 0)
return curr;
else if( curr.getNext() == null || curr.getNext().getValue().compareTo(node.getValue()) > 0)
return curr;
else
curr = curr.getNext();
}
return null;
}
public void insert(Node node){
Node newNode = node;
Node insertPoint = this.findIsertionPoint(this.head, node);
if( insertPoint == null)
this.head = newNode;
else{
if( insertPoint.getValue().compareTo(node.getValue()) == 0)
insertPoint.getItem().incrementCount();
else{
newNode.setNext(insertPoint.getNext());
insertPoint.setNext(newNode);
}
}
count++;
}
There are a few bugs with your code:
Inserting at/before head actually needs to happen in two different scenarios:
If the list is empty, head becomes node
If the list is not empty, but node is less than the first element, head also becomes node
In either case, node links to whatever head was pointing to before (null or a real node), and head now points to node.
If you're not inserting before head, then you must be inserting after some node. We just need to find where this place is. There are two scenarios:
node.getValue() > temp.getValue(), and node.getValue() < temp.getNext().getValue()
node.getValue() > temp.getValue() and temp.getNext() == null
In either case, node is inserted between temp and temp.getNext()
I suggest encapsulating the after insertion point search in its own function. That is, given the list and a value, it needs to return a node. If that node has the same value as the search value, then simply increment; otherwise, insert after. As a special case, return null to indicate that the insertion point is before head.
In pseudocode, it'll look like this:
FUNCTION findInsertionPoint(Node head, V value) RETURNS Node
// return null if value needs to be inserted before head
IF head == null OR value < head.getValue()
RETURN null;
// otherwise, either return a node with the given value,
// or return a node after which value should be inserted
Node curr = head;
REPEAT
IF curr.value == value
RETURN curr;
ELSEIF curr.getNext() == null OR curr.getNext().getValue() > value
RETURN curr;
ELSE
curr = curr.getNext();
PROCEDURE insert(V value) {
Node newNode = NEW Node(value);
Node insertPoint = findInsertionPoint(this.head, value);
IF insertPoint == null // insert before head
newNode.setNext(this.head);
this.head = newNode;
ELSE
IF insertPoint.getValue() == value
insertPoint.incrementCounter();
ELSE // insert after insertPoint
newNode.setNext(insertPoint.getNext());
insertPoint.setNext(newNode);
Update: I see that you've translated my pseudocode to Java, but for some reason you've omitted codes that deals with inserting before head when head is not empty. Specifically, you have inexplicably omitted this part:
IF head == null OR value < head.getValue()
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
and this part:
IF insertPoint == null
newNode.setNext(this.head); // <<<<<<<<<<<
this.head = newNode;
Both of these are essential; it's what allows "A" to be inserted before the head in [ "B", "C", "D" ].
You need to understand why they're important, and really ask yourself why you chose to remove them. Explain to us, to me, to yourself, why you did that; realize the mistake and learn from it.
For making this, instead of developing from scratch my own sorted list I would implement the Queue interface or extend the already existing PriorityQueue (or any other sorted collection that may apply better). I would define the Node class as an implementation of the Comparable interface or instantiate my queue with a Comparator instance and override the PriorityQueue add method to add the new Node only if another object is not already in the queue, incrementing the counter otherwise. If using java >5.0 for type safety I would use generic to allow just Node objects in the Queue.
I think you want to use one of Multiset implementations from Google Collections.
A Multiset works similar to a Set, but allows for duplicates (and counts them!). Look at TreeMultiset:
A multiset which maintains the
ordering of its elements, according to
either their natural order or an
explicit Comparator.
Without seeing the complete code it's hard to do debugging.
I think the problem is that you set
Node temp = head;
before the loop, but you need to reassign temp while while traversing the list to the current element. In this case you continue to compare against head.
You've taken care of the case when
the list is initially empty. You
should also take care of the special
case when the new node goes at be
beginning of the list. If your list
is B->C->D and you are inserting A.
Its good to set node.next to null(If not already done). So
that if the node gets inserted at the
end, we have null as the next of the
last node.
You need to update the temp to move
to the next node if no insertion is
possible. So you are missing a temp = temp.next;
Since this is homework I'm not going to give you any source code. There is one big issue I see with the code:
Suppose your list already has two distinct items, and you're inserting a new item. In your code, you are checking whether node is greater than head, and if so inserting it immediately after, ignoring the rest of the items in the list.
You code do something like this. There are some missing details which you can fill in yourself.
If list is empty, set head = node, head->next = NULL, and you're done.
Otherwise if node->value < head->value, set node->next = head, head = node.
Otherwise, if node->value == head->value, head->count++;
Otherwise, set tmp = head. While tmp->next->value < node->value, set tmp=tmp->next. (check for nulls!).
If tmp->next == NULL, (i.e. you reached end of list) then set tmp->next = node, and you're done.
Otherwise if tmp->next->value == node->value, (i.e. you reached a node with same value) tmp->next->count++.
Otherwise, if node->next = tmp->next, tmp->next = node, and exit

Categories

Resources