I'm learning data structures and try to understand Linked lists in Java. My problem is that i have troubles with deleting nodes at a given index recursively. My goal is to get O(log n) instead of using loops and end up with O(n).
public class LinkedList {
Node head;
int index=0;
Node temp;
Node prev;
public LinkedList(Node head){
this.head=head;
temp=head;
prev=null;
}
public int length(){
int counter=0;
Node n= head.next;
while(n!=null){
counter=counter+1;
n=n.next;
}
return counter;
}
public void push(Node newNode){
newNode.next=head;
head=newNode;
}
public void add(Node prevNode, int value){
if(prevNode==null){
System.out.println("The given previous node can not be null!");
return;
}
Node newNode= new Node(value,null);
newNode.next=prevNode.next;
prevNode.next=newNode;
}
public void add(int index, int value){
length();
if((index<0)||(index>length())){
System.out.println("Array out of bound!");
return;
}
if(index==0){
push(new Node(value,null));
return;
}
Node newNode= new Node(value,null);
Node prevNode=head;
for(int i=1;i<index;i++){
prevNode=prevNode.next;
}
newNode.next=prevNode.next;
prevNode.next=newNode;
}
public void delete(){
head=head.next;
}
public void delete(int index){
if((index<0)||(index>length())){
System.out.println("Array out of bound!");
return;
}
if(index==0){
delete();
return;}
if(head.next==null||head==null){
head=null;
return;}
if(this.index!=index){
this.index++;
prev=temp;
temp=temp.next;
delete(index);
}if(this.index==index){
prev=temp.next;
}
}
public void search(int value){
if(head!=null){
if(value!=head.value){
head=head.next;
index=index+1;
search(value);
}else if(value==head.value){
System.out.println("The value \""+value+"\" was found in index: "+index);}}}
public void display(){
Node n= head;
System.out.print("{");
while(n!=null){
System.out.print(" ("+n.value+") ");
n=n.next;
}System.out.print("}");
System.out.println("\n------------------------------");
}
public static void main(String[]args){
LinkedList ll= new LinkedList(new Node(2,null));
ll.push(new Node(5,null));
ll.push(new Node(6,null));
ll.push(new Node(13,null));
ll.push(new Node(1,null));
ll.display();
ll.add(ll.head.next,8);
ll.display();
ll.add(0, 0);
ll.display();
ll.add(6, 4);
ll.display();
System.out.println(ll.length());
ll.search(13);
ll.delete(2);
ll.display();
}
}
So when i'm trying to delete the entry at the index 2, it deletes all the digits before that index but not at that index - so it deletes [0] and [1] but not [2].
For example in this code, the array before deleting is filled with: {0,1,13,8,6,5,4,2}.
After calling delete(2), it has the following entries: {13,8,6,5,4,2}
All what i want is to delete only the 13, so that the array would look like this: {0,1,8,6,5,4,2}
I would really appreciate any tips to improve my code.
It was very difficult to understand your code, but as you asked for logic to improve your understanding, so sharing psuedocode, which you could refer to correct your code accordingly.
Node delete (index i, Node n) // pass index and head reference node and return head
if (n==null) // if node is null
return null;
if (i==1) // if reached to node, which needs to be deleted, return next node reference.
return n.next;
n.next= delete(n.next,i-1);
return n; // recursively return current node reference
A Java recursive delete method in a linked list
Ok, let's go through this with an example. It's simplistic, but once you get the hang of it and understand the delete recursion algorithm, you can easily make the sample classes generic, take care of encapsulation, optimize the code and then go on to production.
Classes in this example
Assume, for the sake of example, that the basic Singly LinkedList and Node classes are very simplistic. The inner static Node class only stores primitive int types and it only includes a next reference to the following Node element in the list. The LinkedList only includes a head node, which is the beginning of the linked list. This is not a doubly linked list and it does not have a reference to the previous node. Traversals are done sequentially from the given Node (typically head node) through the next reference, one node after the other. I've added a toString() implementation to both, which will come handy later:
public class LinkedList {
protected Node head;
public LinkedList(Node head) {
super();
this.head = head;
}
static class Node {
protected int data;
protected Node next;
Node(int data, Node next) {
this.data = data;
this.next = next;
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Node ");
builder.append(data);
if (null != next)
builder.append(" -> ");
return builder.toString();
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("LinkedList [");
Node node = head;
while (node != null) {
builder.append(node);
node = node.next;
}
builder.append("]");
return builder.toString();
}
}
Implementing a recursive delete method
Now, let's add a recursive delete() method. Deleting a node in a singly linked list can only be done by un-linking it from the previous node's next reference. The only exception to this rule is the head node, which we null to delete. Hence, it is obvious that we'll need (in addition to a starting point current node reference), a reference to the previous node.
Thus, our recursive delete() method signature can be:
private LinkedList delete(Node node, Node prev, int key)
Although the return type of this method can be omitted altogether (void) it is very useful to support chain-ability, so that API calls can become one-liner, dot separated syntax such as:
System.out.println(list.push(0).append(2).deleteAll(1));
Hence, for the sake of chain-ability, we'll return a reference to the entire LinkedList instance from this method too. As per the arguments list:
The first argument is the current node to check if it matches the given key. The next argument is the previous node, in case we need to un-link the current node. The last argument is the key we're looking for in all nodes to be deleted (unlinked).
The method modifier is private because it's not meant to be used publicly. We'll wrap it in a user friendly facade method, which will start the recursion with head as the current node and null as the previous node:
public LinkedList deleteAll(int key) {
return delete(head, null, key);
}
Now, let's see how we can implement the recursive delete(...) method and we begin with the two base conditions that will terminate the recursion; a null current node or a single node in the list, which is also the head node:
private LinkedList delete(Node node, Node prev, int key) {
if (node == null)
return this;
if (node == head && head.data == key && null == head.next) { // head node w/o next pointer
head = null;
return this;
}
//...more code here
}
Reaching the first base condition means either that we've reached the end of the linked list (found the key or not), or that the linked list is empty. We're done and we return a reference to the linked list.
The second base condition checks to see if our current node is the head node, and that it matches the key. In this case we also check if it happens to be the single node in the linked list. In such case the head node requires a 'special' treatment and must be assigned null in order to be deleted. Naturally, after deleting the head node, the list is empty and we're done, so we return a reference to the linked list.
The next condition checks if the current node matches the key, if it's the head node, but is not alone in the list.
private LinkedList delete(Node node, Node prev, int key) {
//...previous code here
if (node == head && head.data == key) { // head with next pointer
head = head.next;
return delete(head, null, key);
}
//...more code here
}
We'll later optimize this code but for now, in such a case we simply move the reference to head one step forward, so head is effectively deleted (the old reference will be garbage collected) and we recur with the new head as the current node and null is still the previous node.
The next case covers a regular (middle or tail) node matching the key:
private LinkedList delete(Node node, Node prev, int key) {
//...previous code here
if (node.data == key) {
prev.next = node.next;
return delete(prev, null, key);
}
//...more code here
}
In this case we delete the current node by un-linking the next pointer of the previous node from the current node and assigning it the next to the current node address. We essentially 'skip' the current node, which becomes grabage. We then recur with the previous node being the current node and null as the previous node.
In all of these handled cases we had a match for key. Finally, we handle the case where there's no match:
private LinkedList delete(Node node, Node prev, int key) {
//...previous code here
return delete(node.next, node, key);
}
Obviously, we recur with the next node as the current node, and the old current node as the previous node. The key remains the same through all recursion calls.
The entire (un-optimized) method now looks like this:
private LinkedList delete(Node node, Node prev, int key) {
if (node == null)
return this;
if (node.data == key && node == head && null == node.next) { // head node w/o next pointer
head = null;
return this;
}
if (node.data == key && node == head) { // head with next pointer
head = head.next;
return delete(head, null, key);
}
if (node.data == key) { // middle / tail
prev.next = node.next;
return delete(prev, null, key);
}
return delete(node.next, node, key);
}
Tail Recursion Optimization
Many compilers (javac included) can optimize recursive methods, if they use a tail-recursion. A recursive method is tail recursive when a recursive call is the last thing executed by the method. The compiler can then replace the recursion with a simple goto/label mechanism and save the extra memory space required in run-time for each recursion frame.
We can easily optimize our recursive delete(...) method to comply. Instead of returning recursively from each of the handled conditions (cases) we can keep a reference to the current node and previous node prev and assign them with appropriate values inside each case handling. This way, the only recursive call will happen at the end of the method:
private LinkedList delete(Node node, Node prev, int key) {
if (node == null)
return this;
if (node.data == key && head == node && null == node.next) { // head node w/o next pointer
head = null;
return this;
}
Node n = node.next, p = node;
if (node.data == key && head == node) { // head with next pointer
head = head.next;
n = head;
p = null;
} else if (node.data == key) { // middle / tail
prev.next = node.next;
n = prev;
p = null;
}
return delete(n, p, key);
}
To test this recursive method:
I've added a simple main test driver method to test the delete(...) method implementation, via the facade method deleteAll(...):
public static void main(String[] args) {
LinkedList list = new LinkedList(new Node(0, new Node(1, new Node(1, new Node(2, new Node(2, new Node(3, null)))))));
System.out.println(list);
System.out.println(list.deleteAll(6));
System.out.println(list.deleteAll(1));
System.out.println(list.deleteAll(3));
System.out.println(list.deleteAll(2));
System.out.println(list.deleteAll(0));
}
The output (using my supplied toString() methods) is:
LinkedList [Node 0 -> Node 1 -> Node 1 -> Node 2 -> Node 2 -> Node 3]
LinkedList [Node 0 -> Node 1 -> Node 1 -> Node 2 -> Node 2 -> Node 3]
LinkedList [Node 0 -> Node 2 -> Node 2 -> Node 3]
LinkedList [Node 0 -> Node 2 -> Node 2]
LinkedList [Node 0]
LinkedList []
Although it's been 3 years since the initial post, I trust some other beginning Java programmers, if not the OP, find this explanation useful.
after struggling i managed to solve the problem, here is the answer, but i am still not sure about the complexity whether it's O(n) or O(log n).
public void delete(int index){
//check if the index is valid
if((index<0)||(index>length())){
System.out.println("Array out of bound!");
return;
}
//pass the value head to temp only in the first run
if(this.index==0)
temp=head;
//if the given index is zero then move the head to next element and return
if(index==0){
head=head.next;
return;}
//if the array is empty or has only one element then move the head to null
if(head.next==null||head==null){
head=null;
return;}
if(temp!=null){
prev=temp;
temp=temp.next;
this.index=this.index+1;
//if the given index is reached
//then link the node prev to the node that comes after temp
//and unlink temp
if(this.index==index){
prev.next=temp.next;
temp=null;
return;
}
//if not then call the function again
delete(index);
}
}
Related
I just started to learn the concept of Linked List recently, and have a problem on how the else statement works in the below code. How does it traverse to next node until a NULL value is found in the node?
public class LinkedList {
Node head;
public void insert(int data) {
Node node = new Node(data);
node.data = data;
if(head == null) {
head = node;
}else{
Node temp = head;
while(temp.next != null) {
temp = temp.next;
}
temp.next = node;
}
}
}
The Node class is the representation of a node in a linked list. Each Node has its own data and a pointer/reference to the next Node, which is stored in the next member of the Node class.
Now answering your question about traversal. We start the traversal from the head of the linked list - Node temp = head;. We now use a while loop to traverse the linked list and stop only when we reach the last Node in the linked list. The last node will have the next reference as null, hence the condition while(temp.next != null). Inside the loop, you are hopping to the next node and the loop will stop when you reach the last node. You then set the new Node as the next node of the last node in order to fulfill the data insertion function.
So, I have been studied about linked list and had created this insert method.
private void insert(Node head, int data)
{
Node node = new Node(data);
Node first = head;
if(first == null)
{
head = node;
}
else
{
node.nextLink = first;
head = node;
//System.out.println(node.data);
}
}
and this traverse method
public void traversingLinkedList(Node head)
{
Node current = head;
while(current != null)
{
int data = current.data;
System.out.println(data);
current = current.nextLink;
}
}
But it is not showing the node when I am inserting it.
The node data shows when I uncomment the print line in method insert.
for example,
LinkedList present is 10 -> 20 -> 30
after using insert(head,4)
I still get 10 -> 20 -> 30
though in the method insert when I uncheck the print method
it is showing first node data as 4
but when traversing it is not showing!
Why?
When calling a method in Java, the variables are copied, not referenced. This means that in your case, the variable head inside the insert method is only local and its modifications will not be visible outside the method.
Thus, since you are inserting elements at the front, the new head after the insertion is the node you have created (not the previous one) and you need to return it to update the next calls. Moreover, you could simplify the code of your insert method since you will always update the head value and the only conditional part is if there are more elements in the list or not.
private Node insert(Node head, int data)
{
Node node = new Node(data);
if (head != null) {
node.nextLink = head;
}
head = node;
return head;
}
In this case your main method should look like:
// LinkedList 10->20->30
head = new Node(30);
head = insert(head, 20);
head = insert(head, 10);
// Add the new 4 head: 4->10->20->30
head = insert(head, 4);
// Traversing
traversingLinkedList(head);
head is a local variable, so assigning values to it inside your insert(Node head, int data) doesn't affect the Node passed to the method.
If your insert method is part of some LinkedList class, that class should hold a reference to the head of the list, and insert should assign to that reference. In that case you won't need to pass Node head as argument to insert.
The only way you can modify the list by using the passed Node argument is if the method would change the nextLink of that Node.
I am using recursion to insert a node at the end of the linked list. The code works fine. But I am a little confused with the return statements.
Going through one by one of my understanding:
The first return will return a new node head == null and the function will finish - nothing more to do
The second will return a node that was created at the end of the tail - nothing more to do
The last will put all the nodes on the stack everytime insertNodeAtTail is called recursively. When the second return is called head.next == null. All the nodes will be popped off the stack until it gets to the first one. Effectively being the first node in the linked list (pointing to head).
Is my understanding correct?
public Node insertNodeAtTail(Node head, int data) {
if(head == null) {
/* List is empty so just return the new node */
Node node = new Node();
node.data = data;
node.next = null;
return node;
}
else if (head.next == null) {
/* We are at the end of the list so insert the new node */
Node node = new Node();
node.data = data;
head.next = node;
return head;
}
else {
/* Travese the list by passing the next node in the list until next is NULL */
insertNodeAtTail(head.next, data);
}
/* This will put all the head nodes on the stack and then pop them off at the end */
return head;
}
Many thanks for any suggestions,
Simply do this
public Node insertNodeAtTail(Node head, int data) {
if(head == null) {
head = new Node(data);
}
else{
head.next = insertNodeAtTail(head.next,data);
}
return head;
}
You simply put return in wrong place, having returned same head element.
else {
/* Travese the list by passing the next node in the list until next is NULL */
return insertNodeAtTail(head.next, data);
}
Im writing this from head please check.
Yes, your understanding is correct... :)
Last return will cause problems as it will cause Head to always point to second last node of your list. You should remove that as suggested by FallAndLearn. Correct me if I am wrong.
I am having trouble with my remove method for circular Linked List. It only executes the if statement. What am i doing wrong? How do I fix this issue?
In a circular linked List you only need to keep track of the first element pointing to the last
public void remove()
{
Node currNode = first;
Node prevNode = null;
if(first != null)
{
if(currNode.getNext() == first)
{
first = null;
}
}
else
{
prevNode = currNode;
currNode = currNode.getNext();
}
}
class Node
{
private int data;
private Node next;
public Node(int data, Node next) {
this.data = data;
this.next = next;
}
public int getData() {
return data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
Node currNode = first;
Node prevNode = null;
is local variable, so after function remove(), they deleted, and you didn't store this value. Every times when you call remove() you have some value in currNode and prevNode. So you should use this variable as class variable:
...
Node currNode = first;
Node prevNode = null;
public void remove()
{
if(first != null)
{
if(currNode.getNext() == first)
{
first = null;
}
}
else
{
prevNode = currNode;
currNode = currNode.getNext();
}
}
Or you should use currNode.setNext(...) instead of currNode = ...
I will be making certain assumptions, since I can't post a comment
-You access the circular linked list using the first Node, that means if first is not null your circular linked list is not empty
-secondly you call the remove() function only when the linked list is non empty, so from my first assumption you cannot reach the else block.
Your remove() logic is NOT clear.
If you invoke remove() when first!=null or linked list in non-empty you are checking if the second node is same is first and then deleting the reference to first(first=null) means you lose the linked list without assigning the second node to be the new first(node). To me it looks like you deleted the entire linked list not just the first element.
Now If you invoke remove() function on an empty linked list i.e first=null
currNode= first //currNode = null
so the else block will look like this
prevNode=null;
currNode=null.getNext() //Null pointer Exception!!
Last I checked in circular linked list , the next of last node should point to the first node and not the first pointing to the last.
PS-If any of my assumptions are wrong please comment instead of downvoting :)
I am improving my data structures skills. I am trying to implement a LinkedList from scratch.
This is what I have done so far:
class Node {
Node next = null; //reference to null
Object data; //value of the node
public Node(Object d) {
data = d; //constructor that assigns the node's value to the node
}
void append(Object d) {
Node end = new Node(d); //create the new node
Node n = this;
while (n.next != null) { //keep moving the reference until we reach null which is the reference of the last node
n = n.next;
}
n.next = end; //Assign the null reference to the node end which is the node to be added
}
Node deleteNode(Node head, Object d){
Node n = head; //Call the pointer to the head n;
if (n.data == d) { //Check if the data of the current node is same as node to be deleted
return head.next; //Here I got lost, I don't know why we're returning head.next
}
while (n.next != null) { //as long as we have reached the last node
if (n.next.data == d) {
n.next = n.next.next; //I don't get this
return head; //Why are we returning head
}
n = n.next;
}
return head;
}
}
The problem is I don't understand the deleteNode method. I have found it in the book Cracking the Code interview. Could someone please clarify for me what is actually happening? The whole reference thing is getting me confused.
The deleteNode method seems to return the linked list. Here's what it does:
If the first element of the linked list is the item that we seek (its data matches d), then we just return the list starting from the second element (head.next). There's nothing linking back to that first element, so the first element is gone. Just what we wanted.
Look through all nodes. They do this by looking at n.next. If its data matches d, then this node should be removed. So then let the list skip that element: Set n.next to be n.next.next (which is the element after it) rather than the element that matched d.
Normally these kinds of methods tend to return the element that was removed. Instead, this seems to return the list itself, which is represented by its head. That's why the method keeps returning head.