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        
Asked By: Jose Alberto

||

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
Answered By: Tim Peters

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.

Answered By: trincot
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.