Iterating through Linked List - java

Is there a linear way to find the middle element of a singly linked list? (Linear - Meaning you can only iterate through the list once AKA the number of iterations you do total cannot exceed the length of the list)
Thanks!
Edit: The question specifies that you cannot know the length of the list beforehand.
Edit 2: The question is written for Java, but using some linked list definition that does not have a length() method

This might not fit your constraints, but they're quite vague. It only requires a single iteration of the list (in the sense of starting at the beginning only once, and reaching the end only once), but it requires two independent pointers being stored as you do so (and therefore follows half of the list's next pointers twice).
Set pointer_1 and pointer_2 to the first node in the list, and counter to 0
Set pointer_1 to pointer_1->next
Increment counter
If counter is even, set pointer_2 to pointer_2->next
If pointer_1 is not at the end of the list, goto 2
When the loop exits, pointer_2 is at the middle of the list

Use two variables that point to the first element of your list. Then iterate through the list, incrementing the first pointer by 1 place and the second pointer by 2 places on each iteration. Once the second pointer reaches the end of the list, the first pointer will contain the middle element (if it exists).
A bit of pseudo code...
list = [1, 2, 3, 4, 5]
ptr1 = list[0]
ptr2 = list[0]
while ptr2 is not null
ptr1 = ptr1.next
ptr2 = ptr2.next.next
return ptr1

Node ptr1 = head;
Node ptr2 = head;
while(ptr1 != null || ptr1->next != null){
ptr1 = ptr1 -> next -> next;
ptr2 = ptr2 -> next;
}
EDIT:
//this is wrong, so not needed so, upper part is enough.
if(ptr1->next != null){
ptr2 = ptr2 -> next;
}

Node p1 = head;
Node mid = head;
while(p1 != null){
p1 = p1.next();
p1 = p1.next();
mid = mid.next();
}
Be aware, the order matters with p1 and mid in the loop. Take for instance if you had only one element in the list. That means the head would be the middle. However, mid would return null because it went to the next element, instead of exiting the loop before getting to the next element.
WRONG IMPLEMENTATION
while(p1 != null){
mid = mid.next(); // incorrect order.
p1 = p1.next().next(); //will cause exception if p1.next == null
}

Related

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 all nodes from linked list (given a start and an end index)

I'm writing an extra removeRange() method that except a start index and end index as parameters. I've passed all the conditions except when the number of nodes equals the range from start to end.
example my linked list contains:
1 -> 2 -> 3
after the method removeRange(0,2) is called:
list should become null, since from 0 to 2, the count is 3 and there are also 3 elements in my list.
look at the picture for a better idea of what's going on if you can.
Code:
public void removeRange(int start, int end) {
if(start < 0 || end < 0) {
throw new IllegalArgumentException();
}
if(start == 0 && end == 0) {
front = front.next;
} else if (start == 0 && end == 1) {
front = front.next.next;
} else {
ListNode head = front;
for(int i = 0; i < start-1;i++) {
head = head.next;
}
ListNode tail = front;
for(int i = 0; i < end;i++) {
tail = tail.next;
}
head.next = tail.next;
}
}
The best way to code any linked data structures (lists, trees, graphs) is to get a white board, and draw the linked list. Then, for the test input, step through the code one line at a time, just like the computer would execute it. For each line, make the corresponding change on the drawn version. At some point the code will tell you to draw something that you know isn't right, and that tells you where your problem is.
One thing I noticed in your code, is that you don't make sure the input range are withing the size of the list. What if the list is 5 nodes long, and someone calls removeRante(10, 12)?
For this particular problem, you have 4 cases, and you should test for and handle them in this order. .
1. Start = head and End = tail, at which point you make head and tail both null, thus emptying the linked list.
2. Start = head. Move head to point to end + 1.
3. End = tail. Move tail to start - 1.
4. Everything else. Head = start + 1, tail = end - 1.
Assuming that your tail.next == null then you still need to set front = head as you have done similarly in your previous cases where you update front.

deleting a specific item from a java LL

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;

JAVA - sorting a linked list by descending order

i tried to write a method that sort a linked list.
this is java training for me.
the method should get a linked list with values and sort it with selection sort.
but not the usual selection sort, but selection sort that find the biggest number and put it in the start of the linked list. until the list is sorted.
i tried follow the debugger but, i cant really understand what i did wrong.
this is what i tired:
public IntList selectionSort()
{
IntNode tempMax = _head;
IntNode current = _head;
IntNode fromHere = null;
IntNode toHere = _head;
IntNode prev = null;
while(toHere != null)
{
current = toHere;
tempMax = toHere;
while (current != null)
{
if (current.getNext() != null && current.getNext().getValue() > tempMax.getValue())
{
prev = current;
tempMax = current.getNext();
current = current.getNext();
}
else current = current.getNext();
}
prev.setNext(prev.getNext().getNext());
tempMax.setNext(toHere);
if (fromHere == null)
_head = tempMax;
else fromHere.setNext(tempMax);
fromHere = tempMax;
toHere = fromHere.getNext();
}
return this;
}
A few tips:
If you want the smallest number then current.getNext().getValue() > tempMax.getValue() should have a > instead of a <
Your code will also fail if the first element is already the minimum value, as you try to do something to null
I can see duplicated code in here too, current = current.getNext(). Pointer operations are generally hard to code, and writing clearer code sticking to the principle of Don't Repeat Yourself will probably help you see bugs!
It will help people here help you if you print compiler/runtime error messages
The main issue with your code, is that it misbehave when a node is already at the position it should be. If we execute with :
5 -> 1 -> 2 -> 3-> 4
prev will be null and we crash.
If we do with :
1 -> 4 -> 5 -> 3-> 2
at the first iteration you obtain
5 -> 4 -> 1 -> 3-> 2
So far so good.And then, after the loop of the second iteration
5 -> 4 -> 3-> 2 // prev still pointing at 4, 1 dissapears
5 -> 4 -> 4. // tempMax = toHere so tempMax->tempMax, and the other elements are gone
So the manifestation is that prev is invalid in some way.
There is a quick fix, like skipping the repositioning when toHere is the maximum,
but quick fixes are not what you need. You should :
Rethink about some special cases. Empty list, one element, list already sorted, list sorted in reverse, random order, (duplicate element??)
Write unit test for each case
Rewrite your algorithm, and avoid Oh yes, I forgot the case... . Dumb it down, you only need to replace the first element at each given step with the maximum found at this step.
Avoid variables which have redundant information. For example, tempMax should always be the next of prev, so prev alone is enough. Otherwise you are spending brain cells preserving consistency.
Test again the suite case.

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