Amazon Interview Question
Software Engineer / DevelopersCountry: India
Interview Type: Written Test
Read the question carefully. It has been clearly mentioned that
"Given a singly linked list which may or may not contain loop".
How will you come to know that the linked list is not having loop?
shodnik:
When I take a linked list head->A->B->C->D->C (i.e. D loops back to C) and try to apply your algorithm, I get stuck at line #4 of your algorithm.
If I set both pointers initially at head then by line#2 I see the pointers meet at node D.
Now I keep the hare pointer at D and reset the tortoise to head as mentioned in line#3. As you've mentioned I move tortoise pointer till I reach hare at node D. Anyway this is of no use since here is not pointing NULL.
So following line#4, we hit the 'else' part. The pointer tortoise is set to head again while here is still pointing to D.
Now moving both pointers takes me into an infinite loop.
Please help me understand what's wrong is happening here!!
@buckCherry, you are missing a key point. A cycle must contain at least 3 nodes. The example you are using fails because it contains a cycle of two nodes. In fact, this is not a cycle, rather it becomes a partial double linked list.
Hope you got it.
shodnik:
If 3 elements/nodes were mandatory for a loop/cycle then the term 'self-loop' would have never existed!!!
I can neither agree with your requirement of at least 3 nodes for a cycle nor could understand why you're calling the above example's linked-list as partial doubly linked list.
Here is my versions of definitions and would love to see your version of the definitions.
Cycle/Loop: In a generic sense a cyclic path or loop is a path where starting from a point if you keep moving in same direction, you come back to the same point. Extending the same idea, in a linked-list if there exists any node where we can come back again by moving in one direction from that node, we call the linked list to have a loop/cycle. This definition doesn't mandate any node requirements. And using this definition, the example list Head->A->B->C->D->C is a linked list with cycle/loop since starting from C we can come back to C.
In addition, you might like to check cycle definitions in Graph theory as they are also CS data structure and we can expect certain similarity with linked-list.
Link: mathworld.wolfram.com/CycleGraph.html
Doubly Link list: If each node of a linked list stores two references - one for forward/next node and other for previous/backward node then it's a doubly linked list.
Since the example linked list I provided are neither capable of nor have stored two pointers per node they are not doubly linked list. Not sure why you call it 'PARTIAL' doubly linked list as not a single node in my example has two references.
Unfortunately, one vote down from me and am looking forward to your response
@buckCherry, Apologies. The concept of Double Linked List is not correct, same for cycle concepts.
Coming back to your initial doubt, how my algo works in your example?
In step#2, hare and tortoise meet at node C. Now move tortoise to node A. So, step#3 outputs 3 nodes( A, B and C).
In step#4, fixing hare at node C and moving tortoise at node A, move hare and tortoise one step until next of both are not equal. So, move hare to node D, and tortoise to node B. Now, next of both are equal, so stop here. The total number of nodes is 4 now.
NOTE: The above algo is inspired from Floyd's Cycle detection algo.
Please let me know if you have still any doubts.
shodnik:
Thanks for taking time to explain all my doubts. It helps big time.
couple of concerns though:
1) The line#4 of your algo says that tortoise is set to head while hare is fixed. So #4 should start with tortoise=Head and hare=C and with this if they move a step each time then they never meets. I didn't get why you've started your line#4 setting tortoise to A which is first element of the list and not the head!!
2)Even if you change your algorithm to indicate that in line#4 tortoise should start from first element then we get a count 4 when both of them meet at D as you've explain on Nov 17th's post. However your algorithm says 'Find sum of the counts if hare was not at NULL' and based on this the answer your algo yields is 3+4=7 which is obviously wrong!!
@buckCherry,,
1) By head, i mean start/first element of the list only.
2) Sum of counts yield 4 nodes( 3 + 1 ).Step#4 outputs value of count as 1.
Explaining once again: Fix hare at node C and move tortoise to node A. Move both hare and tortoise one step. Now, hare is at node D and tortoise is at node B. Now, next of B and next of D are same node C. So, the algo stops here.
Let me know you are facing any problem.
Thank you.
I'll write down three cases and to me I couldn't find out what algorithm I should use for counting so that sum of the two counts becomes the answer.
1) A->B->C->D->C
algo line #2: they meet at C
algo line #3: The path tortoise make is A-B-C
Considering we count number of nodes we get 3
algo line #4: The path tortoise make is A-B-C (The hare's path
is C-D-C)
Considering the node excluding start and end of
tortoise's move we get count 1
Note your algorithm says .."meanwhile keep
counting number of nodes" which doesn't fit here though!!
So by summing 3+1=4 we get our desired answer
Now let's verify these counting algorithm with two
more examples
2) A->B->C->D->E->D
#2: they meet at E (Tortoise: A-B-C-D-E and Hare: A-C-E-E-E)
#3: The path tortoise make is A-B-C-D-E
Considering we count number of nodes we get 5
#4: The path tortoise make is A-B-C-D (The hare's path
is E-D-E-D)
Considering the node excluding start and end of tortoise's
move we get count 2
So by summing 5+2=7 we get a wrong answer
3) A->B->C->A
#2: they meet at A (Tortoise: A-B-C-A and Hare: A-C-B-A)
#3: The path tortoise make is A-B-C-A (even thoug your algo is
unclear here and alternate path could be A )
Considering we count number of nodes we get 4 (or
alternately 1)
#4: The path tortoise make is A-B (alternately A)
Considering the node excluding start and end of tortoise's
move we get count 0
So by summing 4+0=4 (alternately 1+0=1) we get wrong
answers in both ways
@ shondik
Your algo (specially step#4) fails for following input :
A -> B -> C -> D -> E -> D
Step#4 would create infinite look.
Algo for finding the number of nodes in loop is correct. In order to find the starting node of loop ( in my example it is D) , here is my solution.
[Note : Although this also works for self-loop as well, but its efficient to detect the self-loop as a part of finding loop in list and then count the number of nodes before self-loop nodes . ]
1. Keep pointer ptr-1 at head of the list
2. Keep second pointer ptr-2 at (k+1)th node of the list , where k = number of nodes in the loop.
3. Check if both the pointers are pointing to same node , if yes then head is the starting point and thus size of the loop is the size of the list.
4. Else move both pointers ptr-1 and ptr-2 by one step until they meet at some point . Node at which both pointers meet is the start node of the loop.
Here onwards , counting the number of nodes before loop is straight forward since you now you know the start node of the loop.
Traverse through the linked list. while traversing the linked list insert address(key) of each node into a hashmap.If it already exists in the hashmap then the linked list contains the loop. Maintain a counter to know the number of elements of the list while traversing..
Timecomplexity-O(n)
you can first find if there is cycle in linklist or not(Using 2 pointer floyd theorm) in O(n) time
(i)If it is present then that node(ie. start node for cycle) will be your check point.
while counting you need to look at.
otherwise it is a normal link list
first check if der is a loop wid o(n) complexity floyds theorem..
if loop is der,mark the starting node of loop wid pointr p.now tak a pointr k pointing to first node of lnkd list.traverse it until u rech p,increment countr til dat.after dis loop,point k to the next sequential node.. again run loop and increment counter untill p is rechd.. counter is the no. of nodes we wanted..
@nalini can you tell me why wont the pointers meet? rockstar is correct in his approach.
such looping in list is solved using 2 pointers. one is ptr->next and the other is ptr->next->next. at 1 point they both meet and there your counting ends. when there is no looping you can just go till the end of the list incrementing the count.
in the question it is specified that we may be given a link list which may or may not have a loop , and it may not start from the head.
Now consider that we were given a list which does not cointain loop (singly linked list) and we did not start form the head , then starting from the point or node given to us there might be a possibility that there were nodes behind then how can we count them when finding the no. of elements in the list.
in the question it is specified that we may be given a link list which may or may not have a loop , and it may not start from the head.
Now consider that we were given a list which does not cointain loop (singly linked list) and we did not start form the head , then starting from the point or node given to us there might be a possibility that there were nodes behind then how can we count them when finding the no. of elements in the list.
LinkedListNode FindBeginning(LinkedListNode head) {
LinkedListNode n1 = head;
LinkedListNode n2 = head;
// Find meeting point
while (n2.next != null) {
n1 = n1.next;
n2 = n2.next.next;
if (n1 == n2) {
break;
}
}
// Error check - there is no meeting point, and therefore no loop
if (n2.next == null) {
return null;
}
/* Move n1 to Head. Keep n2 at Meeting Point. Each are k steps
/* from the Loop Start. If they move at the same pace, they must
* meet at Loop Start. */
n1 = head;
while (n1 != n2) {
n1 = n1.next;
n2 = n2.next;
}
// Now n2 points to the start of the loop.
return n2;
}
Here is an idea --
maintain three pointers:
a) head -- start of the given list
b) first -- jumps one step in iteration
c) second -- jumps two step in iteration
Start first and second pointers from the head and continue till both become equal (in case of loop) or second traverses the list completely and reaches the end(in case of linear list).
If the list is linear, number of nodes can be counted by traversing the list starting from head.
If the list contains a loop, notice the node where first and second becomes equal, store this new pointer tempPointer.
At this point, break the loop between tempPointer and tempPointer->next by using code below:
head2 = tempPointer->next;
tempPointer->next = null
Suppose the original list with loop was like:
head - a - b- c- d- e - f - g - h
| |
l -k - j - i
Suppose the loop is broken between f and g and null pointer is introduced:
then g becomes head2 and f->next is set to null:
head - a - b- c- d- e - f --> null
|
l - k - j - i - h - g (head2)
now we can traverse from head to null (first list)
and head2 to null (second list)
These two lists are merged at node e.
Count the number of nodes in first list -- count1
Count the number of nodes in second list -- count2
suppose count1 is greater than count2 (take whichever is greater)
then traverse first list by (count1 - count2) starting from head.
Then start traversing two lists together till you reach at merging point. Suppose you traverse M nodes together to reach merging point.
Then traverse the merged list.
The sum of nodes in both lists = number of nodes in first list + M
Simply awesome , but only problem with your approach is you need to change a next pointer to null ( u need to modify the list ).
'Anonymous' & 'Learner'
Can any one of you please elaborate this algorithm a bit more with below two cases!
Case 1) Head ->A->B->C->D->B i.e. D loops back to B
Case 2) Head ->A->B->C->D->C i.e. D loops back to C
For "case 1" when I applied your algorithm , I see both 'first' and 'second' pointers meet at C.
By splitting I get two list i.e breaking link C -> D, we get
Head->A->B->C
head2->D->B->C
Counting both lists yield, count1=3 and count2=3.
After this, I'm lost when you say "..Then start traversing two lists together till you reach at merging point. "
Can you explain what is the merging point in this case? Also didn't get what do you mean by "Then traverse the merged list." ! Which list is the merged list here?
One vote down since it doesn't seem to work and the author didn't explain it enough to clarify all the doubts!
Find the loop using floyd's algo,
then clacualte thenumber of nodes in the loop by fixing one pointer inside the loop and shifting another pointer in the loop untill bth of them become equal again, lets the number be m
now take a pointer pointing to the head and go to mth node now move the pinter to the head and the pointer pointing to the mth node step by step until the bcome equal and now you the node at which the loop starts
Using double pointers only, will not solve this problem. There two types elements in the list; first are say, X which are not in loop and other say, Y, which belongs to loop.
let number of X are x. and number of Y are y.
And we need to calculate the x + y.
I will go with this approach:
First reverse the linked list and increment a counter, counter will be give you the value of 2x + y.
Now, using floyd cycle detection algo to find any Y. Calculate the value of y by just traversing the loop once starting from Y.
Calculate x + y, using prior equations.
Reverse again the linked list to give its original shape.
Total complexity should be O(n).
'shodnik' and 'Anonymous' have already provided solutions which I'm not fully convinced untill I receive some response against my asks to them.
In the mean time here is my solution in O(n) time and with fixed additional memory.
The only drawbacks of my algorithm is that it requires changing the structure of the list whereas both of the solutions from them does not. Note that the solution given by 'Anonymous' does a change of list structure but it can be avoided.
Anyway here is mine.
Use Robert W. Floyd's cycle detection algorithm with slight augmentation to determine if there exists any loop and if so then is there any self loop
If there is no loop
count length of the list
return length
else if there is any self loop
break the self loop by pointing to null
//i.e. a list head->A->B->B wud become head->A->B
count length of the list
return length
else
invoke countNode with head of the list
end-if
countNode(Node head)
if h==null or h->next null
return 0;
end-if
Node firstNode=null;
Node t=h->next;
while t!= null
Node t_dash=t;
t=t->next;
t_dash->next=null;
prepend(firstNode, t_dash);
end-while
return length(firstNode) -1 ;
end-function
prepend(Node firstNode, Node toAdd)
if firstNode==null
firstNode=toAdd;
return ;
end-if
Node temp=firstNode;
firstNode=toAdd;
firstNode->next=temp;
end-function
Node slow = head;
Node fast = head;
while (slow != null && fast != null && slow != fast){
slow = slow.next;
if (fast.next== null)
break;
fast = fast.next.next;
}
int size = 0;
if (slow == null ||fast == null || fast.next == null){ // there is no loop
Node temp = head;
while (temp != null)
count++;
return count;
}
//now we want to find the head of the cycle
slow = head;
while (slow != fast){
slow = slow.next;
fast = fast.next;
}
//now fast/slow is the head of the loop
Node current1 = head;
Node current2 = slow.next;
while (current1 != slow){
size++;
current1 = current1.next;
}
while (current2 != slow){
size++;
current2 = current2.next;
}
return size + 1;
}
EDIT:
- Aashish June 25, 2012The previous algo failed for some input cases. Thanks to @ buckCherry for pointing it out. Below is the fresh algo.
Find the start of the cycle through Floyd cycle detection algorithm. The total number of nodes is the count of nodes that form cycle and count of nodes that are not part of cycle. If the list doesn't contain cycle, count of nodes that are part of cycle is zero.
1. Maintain two pointers called hare & tortoise.
2. move hare two steps & tortoise one step until either both become equal or hare reaches NULL.
3. Move tortoise to the start of the list. Move tortoise and hare step by step until their next node don't point to the same node( in case there is no cycle , hare will point to NULL. Ignore step#4 in this case). This next node is the starting node of the cycle. Meanwhile count the number of nodes that are not part of the cycle.
e.g. A->B->C->D->E->D
The number of nodes that don't form cycle is 3.
4. Count the number of nodes in the cycle. Start tortoise from the starting node of the cycle and keep on moving until it doesn't reach its initial position.
In the example explained in step#3, the number of nodes in the cycle is 2.
5. The answer is count in step#3 + count in step#4.