Trouble tracing reverse function for doubly inked list [duplicate] - java

This question already has answers here:
What is a debugger and how can it help me diagnose problems?
(2 answers)
Closed 3 years ago.
I have a complete working method to reverse a doubly linked list. To be honest I've been going back and fourth for months trying to trace this code to see exactly how it works but I get confused at the end of the while look when I update my current node with current.prev
Ive tried to print out the values of the nodes for each time it changes the next and previous pointers however I get a nullpointerexception, so no luck there.
public void reverse(){
Node temp = null;
Node current = head;
while(current != null){
temp = current.prev;
current.prev = current.next;
current.next = temp;
current = current.prev;
}
if(temp != null){
head = temp.prev;
}
}
There are no errors here, I passed it thru my own test cases for the worst and best scenario. I just can't seem to understand what is going on. I know this is essentially swapping the next and prev pointers but I need to know how.

public void reverse(){
// Create initial values
Node temp = null;
// Note that you are using current to traverse through the linked list
Node current = head;
// While current is not at the end of the original (non-reversed) list
while(current != null){
// Swapping prev and next
temp = current.prev; // temp 'temporarily' holds copy of current.prev
current.prev = current.next; // current.prev is overwritten with current.next
current.next = temp; // current.next is overwritten with temp (containing original current.prev)
// You are setting current to the newly redefined prev
// This was equal to current->next in the original (non-reversed) list
// So you are traversing through the original list
// Anything 'before' this has already been reversed
// Anything 'after' still needs to be reversed
current = current.prev;
}
// Condition checks for edge case of a one node linked list
if(temp != null){
// Set the head of the reversed list
head = temp.prev;
}
Commented code above. I'm not a Java programmer, but in C I would print out the addresses of each node before and after the reversal to check I am doing things properly. Perhaps, you can use hashcodes to do something similar?

This is similar to a swap in Java:
while(current != null){
temp = current.prev; //gets the 'end' of the doubly linked list
current.prev = current.next; //sets the 'end' of the doubly linked list to the 'first' of the list
current.next = temp; //sets the 'first' of the list to temp, which is the 'end' of the list
current = current.prev; //iterate through the list in reverse order (similar to i++ in for loops)
}
if(temp != null){
head = temp.prev; //edge case when there is only 1 node.
}

Related

Remove consecutive nodes in Single-Linked List

Can anyone help me figure it out? I create a method called remainingNodes() in the SingleLinkedList class to count remaining nodes after removing all consecutive nodes of same value in a single-linked list. However it didn't work the way it should have worked:
If there are multiple ways to remove nodes, remove the leftmost node.
[1,2,2,3,3,1] -> [1,3,3,1] -> [1,1] ->[] returns 0
[1,2,3,4,5,6] returns 6
[1,2,3,2,2,1] -> [1,2,3,1] returns 4
[1,2,2,2,3,1] -> [1,2,3,1] returns 4
public int remainingNodes(){
Node curr = start;
while (curr != null && curr.next != null) {
while(curr.value == curr.next.value){
curr.next = curr.next.next;
}
curr = curr.next;
}
int count = 0;
curr = start;
while (curr != null) {
count++;
curr = curr.next;
}
return count;
}
I can just only remove one node of a consecutive pair and I also know it is because I only add the next node of my current node = the next node of the next node of current. However, I don't really know what else should I add to get it worked like my expectation.
You have two main problems:
Missing null-pointer check for curr.next = curr.next.next;
No attempt to go backward or restart when you find a duplicate.
The first fix is easy. Change
while (curr != null && curr.next != null) {
while(curr.value == curr.next.value){
curr.next = curr.next.next;
}
curr = curr.next;
}
To
while (curr != null && curr.next != null) {
while(curr.next != null && curr.value == curr.next.value){
curr.next = curr.next.next;
}
curr = curr.next;
}
The second problem is more involved. Let's look at your logic with the example [1 1]. If I understand you correctly, you want it to convert the list to [], and return 0. I will use notation -> to mean "references" and NodeK(value, next) to refer to nodes in memory.
Initialize: start -> Node1; curr -> Node1. Node1(1, Node2). Node2(1, null).
Outer loop check if curr and curr.next are not null. They are not.
Inner loop if check curr.value (1) is the same as curr.next.value (1). They are.
Inner loop sets curr.next to curr.next.next. This redefines Node1 as Node1(1, null). curr still references Node1.
Inner loop stops because curr.next is null.
Outer loop sets curr to null.
Outer loop stops because curr is null.
The result is that start -> Node1(1, null). So the list is [1] and the return value will be 1.
It looks like you've found some code online that replaces runs of duplicates to a single item. What you're looking for is logic that removes all of the entries, so that there are none left.
To do that, you need to look an extra level deep:
if (start == null) {
return 0;
}
// Begin by checking if the start reference needs to change.
// Keep executing this until no changes are made.
boolean changed = false;
while (!changed) {
changed = false;
Node curr = start.next;
while (curr != null && start.value == curr.value) {
curr = curr.next;
changed = true;
}
// If the start needs to change, update it to the first node whose value was different.
if (changed) {
start = curr;
}
}
// Then proceed with the rest of the list in a similar manner.
changed = false;
while (!changed) {
Node previous = start;
while (previous != null) {
changed = false;
anchor = previous.next;
curr = anchor.next;
while (curr != null && anchor.value == curr.value) {
curr = curr.next;
changed = true;
}
if (changed) {
previous.next = curr;
}
}
}
That should get you started at least. There's some null checks that need to happen here, but the basic idea is what you need. You need to keep a reference to the previous node (or the start node) because you're getting rid of all of the duplicates.
Another bit that's missing is that in the case of inputs like [1 2 2 1] where you expect the result to be [], you need to run the whole algorithm again from the start if there's any changes. You're probably better off taking the idea and putting it into a function, so that you can call it repeatedly, and so that the start case and previous cases don't have so much repetition.
First of all, I would set aside the counting of the remaining nodes after nodes have been removed, as that is a trivial exercise. Let's focus on the node removals.
As you have discovered, this algorithm will need to be able to step back to nodes that were already visited in order to still remove them if needed. The problem is that when you have already walked to a next neighbor node, there is no more reference to the node you came from.
An intuitive way to solve this is to use recursion, such that the recursive call does the job for the nodes after the current head node, and then after the recursion comes back the head can be compared with the first node in the list that was returned. If the same, both nodes are removed.
Here is how that would look:
private Node recurRemove(Node head) {
if (head != null) {
head.next = recurRemove(head.next);
if (head.next != null && head.next.value == head.value) {
head = head.next.next; // Remove a twin
}
}
return head;
}
public int size() {
int count = 0;
for (Node curr = start; curr != null; curr = curr.next) {
count++;
}
return count;
}
public int remainingNodes() {
start = recurRemove(start);
return size();
}
This looks elegant in my humble opinion, but the downside is that it needs stack space, and that is limited.
Even if you would use an explicit stack as local variable instead, it is a pity that the algorithm would have an auxiliary space complexity of O(𝑛).
Solution with O(1) auxiliary space
We could make this work with O(1) auxiliary space by realising that the linked list itself can be used as a stack. This could be done with this algorithm:
Start a new list (as empty)
Keep taking nodes from the original list (making it shorter) and prepend those nodes one by one to that new list (as if it were a stack) until duplicate values are encountered.
When that happens there is one of the twin nodes already on the "stack", while the second one is still the original (shortened) list. From that list remove pairs for as long as itself has twins at its start. Once that is done check if now the first remaining node still has the same value as the node already on the stack. If so pop the node from the stack and remove it from the original list as well.
Repeat this process until the original list is empty.
The "stack" is now the list we want to have but in reversed order.
To get the list back in its intended order, repeat the above steps once more. This time no more nodes will be removed, but the list will be reversed still, which is the intention.
Here is an implementation:
public int reverseAndRemoveTwins() {
Node right = start;
start = null; // We will prepend unique values to this (stack)
int count = 0; // This time we will also keep a counter (size of stack)
while (right != null) {
Node next = right.next;
if (start == null || start.value != right.value) {
// Move the right node to become the head of the left list (reversal!)
right.next = start;
start = right;
right = next;
count++;
} else {
// Remove all identical values at the right
boolean odd = false;
while (right != null && start.value == right.value) {
right = right.next;
odd = !odd;
}
// ...and pop the one at the left too when an odd count was deleted
if (odd) {
start = start.next;
count--;
}
}
}
return count;
}
public int remainingNodes(){
reverseAndRemoveTwins(); // This also reverses the list
return reverseAndRemoveTwins(); // This reverses it back
}
This code seems less elegant, but if you care about memory efficiency it is the way to go.

Linked List reverse Function in java

I am new to programming, so can anyone explain me how the below code works?
Node reverse(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;
}
I just want to know exactly what is happening in the while loop.
Thanks in advance.
Basically it tries to reverse a linked list and as you probably know, every Node has two pointers: a next and a prev. In order to reverse the linked list, we should change these pointers in a way that you can see in the below picture(we want to change blue chain to red chain)
So for example imagine Node 3 in the picture, what its prev field has been pointing to (Node 2) is going to be the next node of Node 3 (in our desired red chain), so current.next = prev;(line 2) makes sense. But because we want to get to Node 4 in the next iteration of the loop, before that we write next = current.next;(line 1) to save the next pointer of Node 3 which currently points to Node 4, and finally at the end of each iteration we put back that with current = next;(line 4) to iterate to the next Node.
In the next iteration we need to do the same thing so now prev should point to Node 3 and that's why we had prev = current;(line 3) to prepare prev.
Picture reference
For each node in the list it is changing the next pointer to point to the previous node.
eg. It is taking this linked list:
a->b->c
and changing it into:
a<-b<-c

Understanding Doubly-Linked List Node Removal in Java

Clarification: The main question is which node is something like current.previous.next = current.next actually pointing to?
I'm using some sample code I found on a YouTube video, but really trying to break it down and understand it. Everything works as-is, I've added comments to each section to help me understand. Where I am really running into problems explaining what is happening in plain English is for node removal when the code start using previous and next in the same line. I'm not following exactly what it's pointing to now. I've worked through the code in the debugger, but I need some plain English explanation if someone wouldn't mind.
Let's say for example I've got a DLL that has 3,4,5,6,7. So I'm going to remove 5, which is the 3rd index. Here is the method for removal.
//Method to remove node at a specified position
public void removeAt(int index) {
//If the head doesn't exist, break out of the logic
if(head == null) return;
//If the requested index is smaller than 1 or greater
//than the size of the list, break out.
if(index < 1 || index > size) return;
//Declares the currently used link as the head
Link current = head;
//Declare int i counter for use in while loop
//While i is less than the index set current to the next node
//and add to the counter i
int i = 1;
while(i < index) {
current = current.next;
i++;
}
//If the next node doesn't exist, set current...previous next?
if(current.next == null) {
current.previous.next = null;
}
//Else if the node before the current is null, set current to
//the next node and then set the previous to null (I thought it
//already was null??) Set the head to the current node
else if(current.previous == null) {
current = current.next;
current.previous = null;
head = current;
}
//If none of the above conditions, set current previous next?? to
//the next node and current next previous to the previous node??
else {
current.previous.next = current.next;
current.next.previous = current.previous;
}
//Subtract from the size of the list
size--;
}
My main understanding issues come in when it starts using current.previous.next and current.next.previous. To me, current.previous.next is just saying to remain at current. If I have three numbers, 3 4 5 and current is 4, then previous is 3, so next would just go back to 4. I know this isn't right, but after reading other posts here, the Javadoc, and watching videos, I'm still not understanding what is going on.
Here is the other class:
public class Link {
private int data;
public Link previous;
public Link next;
public Link(int data) {
previous = null;
this.data = data;
next = null;
}
public Link(Link previous, int data, Link next) {
this.previous = previous;
this.data = data;
this.next = next;
}
public int Data() {
return data;
}
}
I'd appreciate some explanation. Thanks!
If node.next is null, it means that the node is the last element of the list. So if you try to remove the last element of the list, the element before the last element becomes the last element. Which is what this code does
if(current.next == null) {
current.previous.next = null;
}
If node.previous is null, it means that it is the first element of the list. First, we have to keep a reference to the following element, define that the following element must not have a previous element, and then define it as the head of the list. Which is what this code does
if(current.previous == null) {
current = current.next;
current.previous = null;
head = current;
}
Then to stay on your example
if current is 4, then current.previous is 3 and current.next is 5
so
current.previous.next = current.next
define that 3.next is now 5
and
current.next.previous = current.previous
define the 5.previous is now 3
So then 4 is not reference anymore in the list
Doubly link list (DLL) is a list that can be traversed in both directions as you already know. Each node has previous and next pointers to their siblings allowing navigation to both directions. You already have head variable pointing to the head of the linked list.
In the remove method you passed in the index of the node you wish to delete.
while(i < index) {
current = current.next;
i++;
}
Above while loop sets the current node to be the node we wish to delete. There are three cases to be handled at this point.
Current node can be the first node in the list. So current.previous points to nothing and current.next may point to next node if there is one.
Current node can be the last node of the list so that current.next = null.
Current node is neither head or tail of the list so that current.next and current.previous have corresponding values.
if(current.next == null) {
current.previous.next = null;
}
Above code section will is the second point mentioned above. current points to last node. In order to remove last node you have to go back to current.previous and set its link to null so that current will no longer be accessible from the previous node.
else if(current.previous == null) {
current = current.next;
current.previous = null;
head = current;
}
checks to see if the current node is the head of the node. If current is pointing to head of the linked list, it does not have any previous sibling so that current.previous points to null. So current.next must become the new head of the list. With current.previous = null we set the previous link to null and set this current head as the head variable. Now previous node has gone. No references to it.
Third case is the above bullet point 3. It's handled by
else {
current.previous.next = current.next;
current.next.previous = current.previous;
}
where we want to remove current node. That means we need to link current.previous and current.next as siblings.
As a final note, your Link class uses public fields. They should be private and access via getters and setters.

Removing element from `LinkedList`

folks, my code has to remove a certain element from the list. It has to remove all of the occurences of the list. For example, if I'd like to remove "3" and the input is:
1
2
3
4
3
5
then the output should be:
1
2
4
5
But my code only removes the last occurence of the element as it can be seen when I run my code:
3
4
3
2
1
After removing element 3
4
3
2
1
Could smb please help me out with that? THanks in advance!
public void removeElements(String number){
if(isEmpty()){
System.out.println("The list is empty!");
}
else{
if(firstLink.data.equals(number)){
firstLink = firstLink.next;
}
else{
Link current = firstLink.next;
Link previous = firstLink;
while(current != null){
if(current.data.equals(number)){
previous.next = current.next;
break;
}
else{
previous = current;
current = current.next;
}
}
}
}
}
Your loop to remove elements is breaking on the first match. Maybe something like the following would work better. When current is a match, update previous.next but leave previous pointing at the previous node, and when it's not a match, update previous to point to the current node.
while (current != null) {
if (current.data.equals(number)) previous.next = current.next;
else previous = current;
current = current.next;
}
Remove the break, your loop is breaking after it gets into it for the first time.
Another point is, it is falling in your if(firstLink.data.equals(number)) and completely ignoring the else block. You should not have that block in else. It should be outside.
if(firstLink.data.equals(number)){
firstLink = firstLink.next;
}
Link current = firstLink.next;
Link previous = firstLink;
while(current != null){
if(current.data.equals(number)){
previous.next = current.next;
} else {
previous = current;
current = current.next;
}
}
What you can do is iterate through the entire loop and check if the value of the element matches the searched value. If it does then you can use remove that element. I won't give away the solution to this but I can provide you with the algorithm.
for(int i = 0; i < length of list; i++)
{
if(ith element of the list == value to be removed)
//remove the ith term using .remove(i) method
}
What you could do is make a new collection containing all the values you want to remove from the LinkedList, and then call removeAll on the list:
ArrayList<String> numbersToRemove = new ArrayList<String>();
numbersToRemove.add("3");
list.removeAll(numbersToRemove);
This way, if you want to remove multiple numbers later on, you can just add them to numbersToRemove.
Some of the other answers are a bit simpler and more straightforward, though, so use them if they make sense, like iterating through the list and removing any elements that match the element you are removing. The only problem with this is that you will get a ConcurrentModificationException if you modify the list while iterating through the list using the object : list syntax, and will probably get an index out of range exception if you iterate through it using indices, so you will probably need to do something like this instead:
while (list.contains("3")) {
ll.remove("3");
}

How to insert an "info" node into a linked-list, numerically or alphabetically, without using a sorting method?

I have to create a method that inserts information numerically but, the problem specifically says not to use a sorting method.
The list should always be sorted by rfidTagNumber. However, this doesn't mean you run a sorting algorithm on the list. As you insert each info into the sorted list, traverse the list to figure out where the new info should go and insert there. Then the new list is still sorted without running a sorting algorithm.
Also, I have a node that has multiple components. How can I make one node greater than the other based on one component such as price. (Price is one of the components)
I'm new to linked lists so I'm struggling here..
Thanks guys
if (head == null) // if the list is empty then you can just put the info in the first link
head = temp = cursor = null;
else { //other options
while (temp != null)
for (temp = head; temp.next != null; temp = temp.next );
if ( temp > temp.prev && temp < temp.next )
temp = temp.next;
}
^ this is what I have right now. Its full of errors but what I want to do is pretty much say that if a node is greater than the previous and less than the next one then, insert here.
How can I implement this and how can I make the RFDNumber the metric in which the computer decides whether or not a node is greater than another.
Just start at the beginning of the list and at each step compare your rfidTagNumber to number for the next node. If it is less than or equal to that node's number, insert the new node after the current node.
If you give each node a public rfidTagNumber member, you can reference it directly in your conditional. Also, your function should have two parameters: a head node head that refers to the start of the list, and a node you want to insert, let's call it insertNode.
Given these two values, let's change the conditional of your function a bit. You do not want to compare insertNode to the previous and next node, you want to compare it to the current and next node. So instead of:
if ( temp > temp.prev && temp < temp.next )
We'd do something like:
if ( insertNode.rfidTagNumber >= temp.rfidTagNumber && insertNode.rfidTagNumber <= temp.next.rfidTagNumber )
If this condition is true then you want to insert your node here. This involves changing next for the current node and prev for the next node. Here we also need to properly assign prev and next for insertNode. We can do this with the following code:
insertNode.prev = current
insertNode.next = current.next
current.next.prev = insertNode
current.next = insertNode
Keep in mind the order of these statements matters as you do not want to modify current.next without first storing a reference to it in insertNode.next.

Categories

Resources