0

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.

Rene Knop
1,8543 gold badges17 silver badges28 bronze badges
asked Sep 22, 2018 at 17:21
7
  • who says recursive deletion will be o(logn) in linkedlist ? and why do you want to use recursion ? it can be achieved simple iteration, you just made you code hard to understand Commented Sep 22, 2018 at 17:25
  • (+, -, *, / , if) are one steps, loops are n step, calling function will be n step because the function might contain loop. please correct me if i am wrong. thanks Commented Sep 22, 2018 at 17:33
  • 1
    I have no idea, what are you talking about Commented Sep 22, 2018 at 17:34
  • i know it can be achieved with iteration, but as i said i would like to use recursion so i could improve my knowledge Commented Sep 22, 2018 at 17:42
  • you have complete buggy code. It very difficult to tell, what causing this particular issue rather you should prefer to debug your code your own. Ofcourse, I can share the psuedocode to achieve this, since you mentioned, you are trying to improve your knowledge. Commented Sep 22, 2018 at 17:47

3 Answers 3

1

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
answered Sep 22, 2018 at 18:13
0

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.

answered Oct 7, 2021 at 18:06
0

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);
 }
 }
answered Sep 22, 2018 at 21:27

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.