deleting a specific item from a java LL - java

So I'm trying to delete an item from a linked list in java.I'm not using java's predefined LL but I'm working with my own.
I know the concept to delete an item is to traverse in the link and compare the data in the list one by one.
so here's what I came up with, but it doesn't work!
public void delStudent(int regN) {
Node current = head;
Node q = head;
if (current.getStudent().getRegN() == regN) {
head = head.link;
}
for (int i = 0; i < this.length() - 1; i++) {
if (current.getStudent().getRegN() != regN) {
current = current.link;
q = current;
}
}
q.link= current.link.link;
}

Well, if your list is empty, the execution of the if statement in the beginning will immediately give a NullPointerException (because current will be null). Generally, for the LinkedList delete method, you must consider three cases: size == 0, size == 1, and size > 1 (where size is the number of nodes in the Linked List).
public void delStudent(int regN) {
Node current = head;
Node previous = head;
while (current != null ){ // keep traversing till end of list
if (current.getStudent().getRegN() == regN) { // found it!
previous.link = current.link; // relink
if (current == head){ // edge case : removed first element
head = current.link; // move head forward.
}
break;
} else {
previous = current;
current = current.link;
}
}
}
The above code assumes regN is unique and that there is only one student with that regN. Hope this helps.

The mistake is (I think) in these three lines:
current = current.link;
q = current;
(where you set q to be the same as current) and
q.link= current.link.link;
(and maybe also in using length() depending on it's implementation)
To see why let's look at how to delete in more detail:
Let's consider there are three nodes in your list x->y->z and you want to delete y.
In order to do so you need to set x.link = z.
To return to you example it means that the variable q should store the element before current, then deleting current can be done by
q.link = current.link;
In order order to have q be the predecessor of current you have to reverse the two lines above, i.e., use
q = current;
current = current.link;
Why am I saying depending on the implementation of length ? If you implementation of length just returns some number maintained by increasing whenever you add a value to the list, you should also decrement it when deleting one. If length traverses the list in order to find the number of elements then its ok, although not very efficient.
One last comment: your code will not work (even with the fixes I explain above) when there is no element with the given regN. Why? Because you always delete one element. Moreover, you might want to rethink the logic inside the loop. Currently if the element to delete is the second one and there is 1000000 elements you will run the loop nearly 1000000 times.

When you are checking each node you need to delete the node or break once you find a match. You need to maintain a node for the previous node to be able to delete the current node once you find a match.
I just realized that you were attempting to to that with Node q
for (int i = 0; i < this.length() - 1; i++) {
if (current.getStudent().getRegN() != regN) {
q = current;
current = current.link;
} else{ //we have a match
//remove the link to the current item
q.link = current.link;
break;
}
if you are using a doubly linked list you can use node.prev().link = current.link;

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.

Java Linked List pointer confusion

This Code below is from a java LinkedList implementation.The method adds a string element at an index point of the list and is taken from one of my cs books.
The linked list class has 2 global private variables
Node first;
Node last;
public void add(int index, String e) {
if (index < 0 || index > size()) {
String message = String.valueOf(index);
throw new IndexOutOfBoundsException(message);
}
// Index is at least 0
if (index == 0) {
// New element goes at beginning
first = new Node(e, first);
System.out.println("ran");
if (last == null)
last = first;
return;
}
// Set a reference pred to point to the node that
// will be the predecessor of the new node
Node pred = first;
for (int k = 1; k <= index - 1; k++) {
pred = pred.next;
}
// Splice in a node containing the new element
pred.next = new Node(e, pred.next);
System.out.println(toString());
// Is there a new last element ?
if (pred.next.next == null)
System.out.println("ran");
last = pred.next;
}
My question
I don't understand how Node first, last get updated in the condition below
Suppose you have a list that looks like ["1","2","3","7","4","5,"6"]
Then you add the the element "4" to index 3
So, the list looks likes ["1","2","3","4","7","4","5,"6"], but looking at the code of the add method I don't know how the first or last node pointers gets updated. Because in my mind these are the only pieces of code that run because the index isn't 0 and the last doesn't change
EDIT
The Object Node first is used in the toString method(not shown) to traverse through the collection
// Set a reference pred to point to the node that
// will be the predecessor of the new node
Node pred = first;
for (int k = 1; k <= index - 1; k++) {
pred = pred.next;
}
// Splice in a node containing the new element
pred.next = new Node(e, pred.next);
System.out.println(toString());
Before the add, the first element is "1" and the last element is "6".
After the add, the first element is "1" and the last element is "6".
You're right that the first and last dont change-- they don't need to, because you haven't changed the first or the last element.
I think you are getting bogged down by the way this linked list implementation works. The first and last pointers exist to just keep track of the start and end of the list. Hence, when you insert an element in the middle of the list, these pointers do not get updated, nor is there any need for such an update. But they do get updated should an insert land before the current head or after the current tail of the list. Here is the code which handles head inserts:
if (index == 0) {
// New element goes at beginning
first = new Node(e, first);
System.out.println("ran");
if (last == null)
last = first;
return;
}
The critical line here is actually this:
first = new Node(e, first);
The first pointer gets assigned to a new node, which in turn points to the old first node. Similarly, should an insert of a new node land at the end of the list, the following code will handle that:
if (pred.next.next == null) {
System.out.println("ran");
last = pred.next;
}
Here the last pointer is assigned to the new node which was inserted after the old last.
But other than these two edge cases, there is no need to update first and last with inserts.
If the index is 0, the node is added in the beginnng and the for loop doesn’t work.
If it is 1, the loop again doesn’t execute and node is simply added to the list.
However if it something else, then until the index-1 position is reached the loop shifts each element one step behind. Then the code just outside the loop (which slices in a node containing new element) inserts the element at index.
If after the loop has executed, the index turns out to be the last one then the last node is updated.
Hope this helps!

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.

What is an efficient algorithm to find whether a singly linked list is circular/cyclic or not? [duplicate]

This question already has answers here:
How to detect a loop in a linked list?
(29 answers)
Closed 5 years ago.
How can I find whether a singly linked list is circular/cyclic or not? I tried to search but couldn't find a satisfactory solution. If possible, can you provide a pseudo-code or Java-implementation?
For instance:
1 → 3 → 5 → 71 → 45 → 7 → 5, where the second 5 is actually the third element of the list.
The standard answer is to take two iterators at the beginning, increment the first one once, and the second one twice. Check to see if they point to the same object. Then repeat until the one that is incrementing twice either hits the first one or reaches the end.
This algorithm finds any circular link in the list, not just that it's a complete circle.
Pseudo-code (not Java, untested -- off the top of my head)
bool hasCircle(List l)
{
Iterator i = l.begin(), j = l.begin();
while (true) {
// increment the iterators, if either is at the end, you're done, no circle
if (i.hasNext()) i = i.next(); else return false;
// second iterator is travelling twice as fast as first
if (j.hasNext()) j = j.next(); else return false;
if (j.hasNext()) j = j.next(); else return false;
// this should be whatever test shows that the two
// iterators are pointing at the same place
if (i.getObject() == j.getObject()) {
return true;
}
}
}
A simple algorithm called Floyd's algorithm is to have two pointers, a and b, which both start at the first element in the linked list. Then at each step you increment a once and b twice. Repeat until you either reach the end of the list (no loop), or a == b (the linked list contains a loop).
Another algorithm is Brent's algorithm.
Three main strategies that I know of:
Starting traversing the list and keep track of all the nodes you've visited (store their addresses in a map for instance). Each new node you visit, check if you've already visited it. If you've already visited the node, then there's obviously a loop. If there's not a loop, you'll reach the end eventually. This isn't great because it's O(N) space complexity for storing the extra information.
The Tortoise/Hare solution. Start two pointers at the front of the list. The first pointer, the "Tortoise" moves forward one node each iteration. The other pointer, the "Hare" moves forward two nodes each iteration. If there's no loop, the hare and tortoise will both reach the end of the list. If there is a loop, the Hare will pass the Tortoise at some point and when that happens, you know there's a loop. This is O(1) space complexity and a pretty simple algorithm.
Use the algorithm to reverse a linked list. If the list has a loop, you'll end up back at the beginning of the list while trying to reverse it. If it doesn't have a loop, you'll finish reversing it and hit the end. This is O(1) space complexity, but a slightly uglier algorithm.
I you count your Nodes and get to the *head again.
How about following approach:
Sort the link list in ascending order by following any standard algorithms.
Before sort: 4-2-6-1-5
After Sort: 1-2-4-5-6
Once sorted, check for each node data and compare with link node's data, something like this:
if(currentcode->data > currentnode->link->data)
i.e. circular = true;
At any comparison, if any of "currentnode->data" is greater than "currentcode->link->data" for a sorted link list, it means current node is pointed to some previous node(i.e circular);
Guys, i dont have setup to test the code.Let me now if this concept works.
Use the Tortoise-Hare algorithm.
A algorithm is:
Store the pointer to the first node
Traverse through the list comparing each node pointer to this pointer
If you encounter a NULL pointer, then its not circularly linked list
If you encounter the first node while traversing then its a circularly linked list
#samoz has in my point of view the answer! Pseudo code missing. Would be something like
yourlist is your linked list
allnodes = hashmap
while yourlist.hasNext()
node = yourlist.next()
if(allnodes.contains(node))
syso "loop found"
break;
hashmap.add(node)
sorry, code is very pseudo (do more scripting then java lately)
Start at one node and record it, then iterate through the entire list until you reach a null pointer or the node you started with.
Something like:
Node start = list->head;
Node temp = start->next;
bool circular = false;
while(temp != null && temp != start)
{
if(temp == start)
{
circular = true;
break;
}
temp = temp->next;
}
return circular
This is O(n), which is pretty much the best that you will able to get with a singly linked list (correct me if I'm wrong).
Or to find any cycles in the list (such as the middle), you could do:
Node[] array; // Use a vector or ArrayList to support dynamic insertions
Node temp = list->head;
bool circular = false;
while(temp != null)
{
if(array.contains(temp) == true)
{
circular = true;
break;
}
array.insert(temp);
temp = temp->next;
}
return circular
This will be a little bit slower due to the insertion times of dynamic arrays.
Here is a nice site on which the different solutions can copied.
find loop singly linked list
This is the winner on that site
// Best solution
function boolean hasLoop(Node startNode){
Node slowNode = Node fastNode1 = Node fastNode2 = startNode;
while (slowNode && fastNode1 = fastNode2.next() && fastNode2 = fastNode1.next()){
if (slowNode == fastNode1 || slowNode == fastNode2) return true;
slowNode = slowNode.next();
}
return false;
}
This solution is "Floyd's
Cycle-Finding Algorithm" as published
in "Non-deterministic Algorithms" by
Robert W. Floyd in 1967. It is also
called "The Tortoise and the Hare
Algorithm".
It will never terminate from the loop, it can also be done in following solution:
bool hasCircle(List l)
{
Iterator i = l.begin(), j = l.begin();
while (true) {
// increment the iterators, if either is at the end, you're done, no circle
if (i.hasNext()) i = i.next(); else return false;
// second iterator is travelling twice as fast as first
if (j.hasNext()) j = j.next(); else return false;
if (j.hasNext()) j = j.next(); else return false;
// this should be whatever test shows that the two
// iterators are pointing at the same place
if (i.getObject() == j.getObject()) {
return true;
}
if(i.next()==j)
break;
}
}
Try this
/* Link list Node */
struct Node
{
int data;
struct Node* next;
};
/* This function returns true if given linked
list is circular, else false. */
bool isCircular(struct Node *head)
{
// An empty linked list is circular
if (head == NULL)
return true;
// Next of head
struct Node *node = head->next;
// This loop would stope in both cases (1) If
// Circular (2) Not circular
while (node != NULL && node != head)
node = node->next;
// If loop stopped because of circular
// condition
return (node == head);
}

Categories

Resources