LinkedList merge_sorted infinite loop
Question:
I keep getting into an infinite loop when using my function merge_sorted for my linked lists. Please help. Thanks 🙂
I went through my merge_sort function and it seems okay. It updates the curr1 and curr2 inside the loop but I am not sure why I get into the infinite loop.
def merge_sorted(root1,root2):
"""Ascending Order"""
if root1 == None:
return root2
if root2 == None:
return root1
curr1 = root1
curr2 = root2
new_root = None
new_tail = None
# init r and t
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
else: # curr1.val <= curr2.val
new_root = curr1
new_tail = curr1
# merge
while curr1 and curr2:
if curr1.val > curr2.val:
# curr2 goes first
new_tail.nxt = curr2
new_tail = new_tail.nxt
curr2 = curr2.nxt
else: # curr1.val <= curr2.val
# curr1 goes first
new_tail.nxt = curr1
new_tail = new_tail.nxt
curr1 = curr1.nxt
if curr1:
new_tail.nxt = curr1
if curr2:
new_tail.nxt = curr2
return new_root
Answers:
In future, please post complete code, enough for people to run to reproduce your problem. In this case, we’re missing the class wrapping .val
and .nxt
, and have no clue about what the inputs may have been when you got your infinite loop.
In any case, this looks shallow, getting off track near the start: after your very first compare, you need to advance whichever of curr1
and curr2
was the winner. Suppose, e.g., that curr1.val
won. Then you need to do curr1 = curr1.nxt
so you don’t compare it again. But the code doesn’t. So curr1
wins the second (identical!) compare too, and, in effect, curr1.nxt = curr1
is done. That’s where the infinite loop comes from: the linked list has been turned into an endless cycle.
Alternative
"The problem" with a linked list merge is that you want to plant a link to the winner in the "next" field of the current tail. But, at the start there is no "current" tail. So, as you did, we typically see a special case made out of the very first compare. Which opens the door to problems unique to that special case.
An alternative is to create an artificial node at the start, and call it "the current tail". Then there are no special cases, and at the end just return the value of that artificial node’s "next" link:
def merge_sorted(root1, root2):
if root1 is None:
return root2
if root2 is None:
return root1
head = winner = Node("dummy")
while True:
if root1.val > root2.val:
# 2 goes first
winner.nxt = root2
winner = root2
root2 = root2.nxt
if root2 is None:
winner.nxt = root1
break
else:
# 1 goes first
winner.nxt = root1
winner = root1
root1 = root1.nxt
if root1 is None:
winner.nxt = root2
break
return head.nxt
When you append a node to become the new tail of the merged list, you should move the corresponding curr
reference one step ahead. You do this in the loop, but forgot doing this in the initial case (before the loop).
So change this:
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
else:
new_root = curr1
new_tail = curr1
to this:
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
curr2 = curr2.nxt # <---
else:
new_root = curr1
new_tail = curr1
curr1 = curr1.nxt # <---
Why the infinite loop?
Without this correction, you’ll create a loop in the list. Let’s assume that curr1.val > curr2.val
at the very start, then new_tail
will be made to reference the same node as curr2
(the if
case).
Then in the loop, this condition will still be true: curr1.val > curr2.val
and then in the if
block we set new_tail.nxt = curr2
. But as new_tail
was the same as curr2
, we actually do curr2.nxt = curr2
. This introduces a cycle in the linked list. The next statement new_tail = new_tail.nxt
will follow that cycle so that new_tail
doesn’t actually change, and the same fate befalls curr2
which also doesn’t advance with curr2 = curr2.nxt
… and so things go very wrong from that point onwards: nothing changes, and the next iteration of the loop will get into the same if
block with the same nodes referenced by new_tail
and curr2
and these references fail to get anywhere else.
It is crucial that at the start of every iteration of the loop, new_tail
is not equal to curr1
nor to curr2
. new_tail
is intended to reference the last node in the merged part of the list, while curr1
and curr2
are intended to reference the first nodes of the non-merged parts of the original lists.
I keep getting into an infinite loop when using my function merge_sorted for my linked lists. Please help. Thanks 🙂
I went through my merge_sort function and it seems okay. It updates the curr1 and curr2 inside the loop but I am not sure why I get into the infinite loop.
def merge_sorted(root1,root2):
"""Ascending Order"""
if root1 == None:
return root2
if root2 == None:
return root1
curr1 = root1
curr2 = root2
new_root = None
new_tail = None
# init r and t
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
else: # curr1.val <= curr2.val
new_root = curr1
new_tail = curr1
# merge
while curr1 and curr2:
if curr1.val > curr2.val:
# curr2 goes first
new_tail.nxt = curr2
new_tail = new_tail.nxt
curr2 = curr2.nxt
else: # curr1.val <= curr2.val
# curr1 goes first
new_tail.nxt = curr1
new_tail = new_tail.nxt
curr1 = curr1.nxt
if curr1:
new_tail.nxt = curr1
if curr2:
new_tail.nxt = curr2
return new_root
In future, please post complete code, enough for people to run to reproduce your problem. In this case, we’re missing the class wrapping .val
and .nxt
, and have no clue about what the inputs may have been when you got your infinite loop.
In any case, this looks shallow, getting off track near the start: after your very first compare, you need to advance whichever of curr1
and curr2
was the winner. Suppose, e.g., that curr1.val
won. Then you need to do curr1 = curr1.nxt
so you don’t compare it again. But the code doesn’t. So curr1
wins the second (identical!) compare too, and, in effect, curr1.nxt = curr1
is done. That’s where the infinite loop comes from: the linked list has been turned into an endless cycle.
Alternative
"The problem" with a linked list merge is that you want to plant a link to the winner in the "next" field of the current tail. But, at the start there is no "current" tail. So, as you did, we typically see a special case made out of the very first compare. Which opens the door to problems unique to that special case.
An alternative is to create an artificial node at the start, and call it "the current tail". Then there are no special cases, and at the end just return the value of that artificial node’s "next" link:
def merge_sorted(root1, root2):
if root1 is None:
return root2
if root2 is None:
return root1
head = winner = Node("dummy")
while True:
if root1.val > root2.val:
# 2 goes first
winner.nxt = root2
winner = root2
root2 = root2.nxt
if root2 is None:
winner.nxt = root1
break
else:
# 1 goes first
winner.nxt = root1
winner = root1
root1 = root1.nxt
if root1 is None:
winner.nxt = root2
break
return head.nxt
When you append a node to become the new tail of the merged list, you should move the corresponding curr
reference one step ahead. You do this in the loop, but forgot doing this in the initial case (before the loop).
So change this:
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
else:
new_root = curr1
new_tail = curr1
to this:
if curr1.val > curr2.val:
new_root = curr2
new_tail = curr2
curr2 = curr2.nxt # <---
else:
new_root = curr1
new_tail = curr1
curr1 = curr1.nxt # <---
Why the infinite loop?
Without this correction, you’ll create a loop in the list. Let’s assume that curr1.val > curr2.val
at the very start, then new_tail
will be made to reference the same node as curr2
(the if
case).
Then in the loop, this condition will still be true: curr1.val > curr2.val
and then in the if
block we set new_tail.nxt = curr2
. But as new_tail
was the same as curr2
, we actually do curr2.nxt = curr2
. This introduces a cycle in the linked list. The next statement new_tail = new_tail.nxt
will follow that cycle so that new_tail
doesn’t actually change, and the same fate befalls curr2
which also doesn’t advance with curr2 = curr2.nxt
… and so things go very wrong from that point onwards: nothing changes, and the next iteration of the loop will get into the same if
block with the same nodes referenced by new_tail
and curr2
and these references fail to get anywhere else.
It is crucial that at the start of every iteration of the loop, new_tail
is not equal to curr1
nor to curr2
. new_tail
is intended to reference the last node in the merged part of the list, while curr1
and curr2
are intended to reference the first nodes of the non-merged parts of the original lists.