Search code examples
pythonloopslinked-listsingly-linked-list

Tail-Loop Puzzle with a Linked list - My Python solution is Failing when tail is longer than loop


I have been working on a puzzle on codewars that can be found here:

http://www.codewars.com/kata/can-you-get-the-loop

Basically, the input is the first node of a linked list which is guaranteed to have a tail of some length and a loop of some length. (For a picture see the link.)

My solution is to have two iterators step through the list, one that visits every node and one that skips every node. Once they hit, I know I am inside the loop, so I just count one cycle and return the count.

Here is my code:

def loop_size(node):
    size = 1
    onestep = node
    twostep = node.next
    while(onestep != twostep):
        twostep = twostep.next.next
        onestep = onestep.next
    #we are inside the loop
    #onestep == twostep
    onestep = node.next
    size += 1
    while(onestep != twostep):
        size += 1
        onestep = onestep.next
    return size

For some reason I am getting odd results. Whenever the tail is smaller than the loop, I get the correct result. But whenever the tail is longer than or equal to the size of the loop, my function gets a way higher count.

Here are some examples:

Tail length = 1 Loop Length = 3
###result 3 - correct

Tail length = 999 Loop Length = 1000
###result 1000 - correct

Tail length = 1000 Loop Length = 999
###result 1998 - incorrect

Tail length = 50 Loop Length = 3
###result 51 - incorrect

Tail length = 3 Loop Length = 3
###result 6 - incorrect

Tail length = 3 Loop Length = 4
###result 4 - correct

Solution

  • Line

    onestep = node.next
    

    should be

    onestep = onestep.next
    

    Otherwise you are starting from the head again and re-entering the loop so your results will be the tail length too long. Also, I believe your size should be started as 1, not 2 as you have it (size = 1, size += 1 before the second loop starts).

    This code works for all of your examples:

    class Node(object):
    
        def __init__(self, next_node=None):
            self.next_node = next_node
    
        @property
        def next(self):
            return self.next_node
    
        def set_next(self, new_next):
            self.next_node = new_next
            return new_next
    
    def loop_size(node):
        onestep = node
        twostep = node.next
        while(onestep != twostep):
            twostep = twostep.next.next
            onestep = onestep.next
        onestep = onestep.next
        size = 1
        while(onestep != twostep):
            size += 1
            onestep = onestep.next
        return size
    
    def test_ll(tail, loop):
        head = Node()
        nodes = [head]
        for i in range(2, tail+loop+1):
            head = head.set_next(Node())
            nodes.append(head)
        nodes[-1].set_next(nodes[tail])
        size = loop_size(nodes[0])
        print "Tail: {}, Loop: {}, Size: {}".format(tail, loop, size)
    
    test_ll(1, 3)
    test_ll(999, 1000)
    test_ll(1000, 999)
    test_ll(50, 3)
    test_ll(3, 3)
    test_ll(3, 4)
    

    OUTPUT

    Tail: 1, Loop: 3, Size: 3
    Tail: 999, Loop: 1000, Size: 1000
    Tail: 1000, Loop: 999, Size: 999
    Tail: 50, Loop: 3, Size: 3
    Tail: 3, Loop: 3, Size: 3
    Tail: 3, Loop: 4, Size: 4