This is a fairly easy question but I'm confused:
Given a Singly Linked List, write a function to delete a given node.
1) It must accept pointer to the start node as first parameter and node to be deleted as second parameter i.e., pointer to head node is not global.
2) It should not return pointer to the head node.
3) It should not accept pointer to pointer to head node.
The solution in Java is as following:
void deleteNode(Node node, Node n) {
if (node == n) {
if (node.next == null) {
System.out.println("There is only one node. The list "
+ "can't be made empty ");
return;
}
node.data = node.next.data;
n = node.next;
node.next = node.next.next;
System.gc();
return;
}
// When not first node, follow the normal deletion process
// find the previous node
Node prev = node;
while (prev.next != null && prev.next != n) {
prev = prev.next;
}
if (prev.next == null) {
System.out.println("Given node is not present in Linked List");
return;
}
prev.next = prev.next.next;
System.gc();
return;
}
I'm confused about why in deleting the head node, we're not modifying the head pointer but copying the fields instead (changing the content), but in deleting other nodes, it's simply prev.next = prev.next.next
Does it work if we just do head = head.next instead when deleting head node?
Thank you!
The reason the code copies the data rather than modifying the variable referencing the head is that other users of the list will have a reference to the head node. Changing the local variable will have no effect on their references so you won't have actually deleted the node. Copying the data to the head node effectively removes it.
So, for example, if you had code that did the following:
Node head = new Node("A");
Node tail = new Node("B");
head.next = tail;
deleteNode(head, head);
Then you would expect head.data to be "B" because the original node has been deleted. If you merely do node = node.next then head will still point to the original deleted node.
There are quite a few issues with the code you've posted so please add a comment if you want suggestions on improvements that should be made. It is not a typical algorithm for deleting nodes from a linked list.
One clear issue you've asked about is the use of System.gc. It is not necessary. There are rare cases when Java code needs to take explicit control of garbage collection. This isn't one of them. There's a good explanation of this in the accepted answer to this question.
You asked in the comments why deleting the head requires moving data while deleting other nodes only requires redirection around the node. The reason is because you don't have access to references to the head (as explained in the answer above). You do have access to references to other nodes (i.e. the previous node's next) so they can be changed directly rather than having to copy data.
For your reference, a much more standard implementation is to have the list itself store a reference to the head. Then the copying of node data can be completely avoided. Also note this compares to a value because the node class is private.
static class LinkedList<T> {
private class Node {
private final T value;
private Node next = null;
public Node(T value) {
this.value = value;
}
}
private Node head = null;
public void add(T value) {
Node node = new Node(value);
node.next = head;
head = node;
}
public void remove(T value) {
while (head != null && head.value.equals(value))
head = head.next;
Node prev = head;
while (prev != null && prev.next != null) {
if (prev.next.value.equals(value))
prev.next = prev.next.next;
else
prev = prev.next;
}
}
}
This avoids the arbitrary restrictions in the example you provided such as not being able to delete the head if it's the only node.
Related
How and where is the head list implemented in task 83 on leetcode? Where is it initialized? Can you please write the code for this in what it would look like in a regular editor?
Here is the solution:
public ListNode deleteDuplicates(ListNode head) {
if(head==null || head.next==null)return head;
ListNode node=head;
while(head!=null && head.next!=null){
if(head.val==head.next.val){
head.next=head.next.next;
}
else head=head.next;
}
return node;`
}
How and where is the head list implemented
In Computer science, the first Node of a Linked list is usually called the head (see: Linked list - Basic concepts and nomenclature).
And head an instance of Node class (a building block of the Linked list data structure), like any other node.
Method deleteDuplicates() expects an instance of Node which should be provided by the caller (i.e. a part of the code that uses deleteDuplicates()).
And by the way, it's a good practice to avoid mutating arguments, therefore it would be cleaner to use node reference to iterate through the list and preserve the head intact. This way it's more obvious that method returns precisely the same reference to the beginning of the list that has been passed to it.
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) return head;
ListNode node = head;
while (node != null && node.next != null) {
if (node.val == node.next.val) {
node.next = node.next.next;
} else node = node.next;
}
return head;
}
Looking at the first example, the calling code might look like this:
ListNode source = new ListNode(1, new ListNode(1, new ListNode(2)));
ListNode result = new Solution().deleteDuplicates(source);
// code that checks that result is a list of two elements and contains 1 and 2 in that order
I've made a remove method that removes an object from a singly linked list.
So far the method removes the first node and the second node without a problem. When removing the third node and onward the method removes everything between the specified node and the head node. My question is how can I make my method remove the specified node and not everything before it?
I know I need to keep a reference to the previous node in order to assign its next node to the specified nodes next node. This will close the gap and remove the node. I understand the logic but can't seem to implement it.
public class List_test{
public Node head;
public List(){
head = null;
}
public List(Node head_) {
this.head = head_;
Node ref = head;
}
public void remove(Node node) {
Node ref= head;
if (ref.equals(node)) {
head = head.next;
return;
}
while (ref != null) {
if (ref.equals(node)) {
head.next = node.next;
}
ref = ref.next;
}
}
public static void main(String[] args) {
new List_test();
}
For removing a node, why are you changing the head pointer? You should be using the previous node and point it to the next node of the node to be removed.
I think this page have nice explanation for this topic:
deleting node in linked list c/c++, java, python
my question is about a exercise in lintcode which requires delet node, but for some reason my simple program won't work.
For example, input (1->2->3->4->null, 3) should output (1->2->4->null), my idea is delete next node and "copy" its value to input THIS node.
/**
* Definition for ListNode.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int val) {
* this.val = val;
* this.next = null;
* }
* }
*/
public class Solution {
/**
* #param node: the node in the list should be deleted
* #return: nothing
*/
public void deleteNode(ListNode node) {
// write your code here
ListNode pnext;
if (node != null) {
if (node.next != null) {
ListNode tempnode = node.next;
if (node.next.next != null) {
pnext = node.next.next;
}
else {
pnext = null;
}
node.next = null; //delete next node
node = tempnode; // give next node to THIS node
node.next = pnext; // link next
}
}
else node = null;
}
}
I am very confused about giving value to node, does node1 = node2 even work? I know node.val can. Can anyone simply guide me please?
You did not include in your post what actually happens, and you did not include the part where you create your list, so I can only assume what happens is that you do not delete 3, but delete 4? Or does it not delete anything at all?
So you have a method that should delete a Node, and the Node you want to delete is given as a parameter. But as your list is only linked one way you have no way of determining the node before it (which holds the reference to the node you want to delete). Actually the delete method should be as easy as this:
public void deleteNextNode(ListNode node) {
if(node == null || node.next == null) return; //nothing to delete
if(node.next.next == null){
node.next = null;
return;
} else{
node.next = node.next.next;
}
}
Although I did not test it. As long as you don't store any other references to your nodes this is sufficient, otherwise you need to call next.null explicitely in the else part.
Edit: My function deletes the Node that follows the Node you pass, but you want a function that deletes the Node you pass it. The problem here is that the previous node stores the reference to the Node that you want to delete, but you have no way of determining that Node with only the reference to the Node you want to delete. So either you use this function and always pass the previous node, or you make a wrapper function and run through the list and always safe the previous Node and call the function with that node:
public ListNode deleteNode(ListNode start, ListNode deleteMe)
{
ListNode prev = start;
if(start == deleteMe) return start.next;
while(prev.next != null) {
if(prev.next == deleteMe)
deleteNextNode(prev);
prev = prev.next;
}
return start;
}
Note that I made the function return a ListNode, this is always the first Node of the List (in case you decide to delete the first node).
I hope this helps
You are complicating it unnecessarily and it is not that tough. As I see, you are just taking the ListNode as the parameter and not the data which you want to delete. Also, since you are using if and not while or for, you'll never end up iterating the whole list.
You should modify your function to take both the node as well as the data and start using a loop to iterate the complete list.
Here is the code snippet:
private void deleteNode(ListNode node, int data) {
while(node != null && node.data != data) {
node = node.next;
}
if(node != null) {
System.out.println("Deleted Node With Data: " + node.data);
node = node.next;
}
}
I think by:
node = tempNode; // give next node to THIS node.
You meant:
node.val = tempNode.val; // give next node value to THIS node.
That way, the next node's value is copied into the current node.
Next you would want to also do:
node.next = tempNode.next;
This way, the node effectively becomes the tempNode. I think this was your intention.. ?
I'm preparing for interviews and wrote up this simple function for recursively reversing a singly linked list. The first node is a sentinel node, head. The following code works fine for: list.reverse(list.head.next), but I can't seem to get it to work if I just pass it head.
public Node<T> reverse(Node<T> current)
{
if (current == null)
return head;
if (current.next == null)
{
head.next = current;
return current;
}
reverse(current.next).next = current;
current.next = null;
return current;
}
I assume it doesn't work when I pass it head instead of head.next because I say current.next = null, but even if I check if current == head or if current.data == null and only use current.next = null when those aren't true, it still doesn't work. I'm sure there's a really simple fix, but I'm just not seeing it right now.
The above if passed head returns an empty list, and if the suggested changes are made, simply doesn't finish running, but I don't get any sort of error.
(EDITED)
I kind of get your problem now:
Simply speaking, the sentinel head acts simply as a pointer to the first node, instead of being part of the linked list. Therefore it will not be involved in the reverse process, and need to handle separately.
which means, the original list looks like:
HEAD -> a -> b -> c -> null
after reverse, it should look like
HEAD -> c -> b -> a -> null
In brief, it should look like (assume your code already works when passing in head.next)
public Node<T> reverse(Node<T> current)
{
if (current == head) {
return reverse(current.next);
}
// rest of your original code.
}
Just a further suggestion:
Your reverse() method, as an public instance method of your list class, shouldn't accept the current node, as it is conceptually meaningless for caller.
I believe you should make this method protected, which means something like:
public void reverse() {
this.head = reverseInternal(head);
}
private Node<T> reverseInternal(Node<T> node) {
// your original reverse logic
}
With such encapsulation, you don't even need to struggle before how to make your reverse works when you pass in the sentinel head: you can simply call reverseInternal(head.next) in your public reverse() method.
First: if it returns an empty list it doesn't "work".
There is no need for head to be an empty node. You should normally just keep the first node (in your case list.head.next) as your list.head. head should be a reference to where the list starts, not a separate node.
The reason your code empties the list when you pass it list.head is it sets list.head.next to null. This is because you assume the node you pass to the list is a regular one, while your head node is special.
Here's a solution for your assumptions (I'll assume someone insisted on this bizarre detached head thing. Just don't do it if you're designing the list yourself. Please...)
public Node<T> reverse(Node<T> current)
{
if (current == null)
return head;
if (current.next == null)
{
head.next = current;
return current;
}
Node<T> temp = current.next;
current.next = null;
head.next = temp;
reverse(temp).next = current;
return current;
}
Explanation: This still sets the last node's next to null, but it pushes the list's head one spot down as it runs through the list, eventually pointing it to the last (now first) member.
This reeks of homework.
But still.
In general:
f(Node<T> current, ...) {
f(current.next, ...);
}
For a list a > b > c > d > e sitting in the midle at d, one
probably has built c > b > a already, so guess what is needed as additional parameter to f?
Good luck.
After comments:
public Node<T> reverse(Node<T> current)
{
return reverseRec(current, null);
}
/**
* #param current to-do (sub-)list.
* #param resultDone done reversed list sofar.
* #return reversed result.
*/
public Node<T> reverseRecursively(Node<T> current, Node<T> resultDone)
{
if (current == null) {
return resultDone;
}
Node<T> next = current.next;
current.next = resultDone;
return reverseRecursively(next, current);
}
When I call this insert before method, it does what it is supposed to do at first, but then it causes the linked list to keep going on and on forever until i click stop (with system out print). I can't find where it goes wrong in this method
private boolean insertBefore(Node aNode, Node beforeNode)
{
Node currentNode;
Node prevNode;
//aNode= new Node();
currentNode = this.getHead();
while(currentNode!=null && currentNode.getNext()!=aNode)
{
if(currentNode == beforeNode)
{
prevNode = this.getPrevious(beforeNode);
prevNode.setNext(aNode);
aNode.setNext(beforeNode);
//aNode.setNext(currentNode);
return true;
}
currentNode = currentNode.getNext();
}
currentNode.setNext(beforeNode);
return false;
}
This is much simpler than the code specified above, given you have a doubly-linked list there is no need to loop over all the elements:
private boolean insertBefore(Node aNode, Node beforeNode) {
if(beforeNode.getPrevious() != null) {
beforeNode.getPrevious().setNext(aNode);
aNode.setPrevious(beforeNode);
} else {
head = aNode;
}
aNode.setNext(beforeNode);
beforeNode.setPrevious(aNode);
}
If the beforeNode is at the head of the list, your new node becomes the head.
Otherwise, there is a node behind your beforeNode. This must now point at your new node.
Either way, your new node's next pointer points at the beforeNode node.