1
\$\begingroup\$
class Link:
    def __init__(self, value):
        self.val = value
        self.next = None

    def __repr__(self):
        return f"{self.val} "

    def __str__(self):
        return self.__repr__()

# TODO : Implement Non-recursive solutions and string representations in recursive solutions.


class LinkedList:
    def __init__(self):
        self.head = None
        self.tail = None
        self.length = 0

    def __str__(self):
        curr = self.head
        linklist = ""
        while curr:
            linklist += str(curr) + ' '
            curr = curr.next
        return 'Linklist : ' + linklist

    def __len__(self):
        curr = self.head
        next = self.head
        size = 0
        while curr and next:
            if not next.next:
                size += 1
                return size
            else:
                size += 2
            curr = curr.next
            next = next.next.next
        return size

    def insert(self, key):
        if not self.head:
            self.head = Link(key)
            self.tail = self.head
        else:
            node = Link(key)
            self.tail.next = node
            self.tail = node
        self.length += 1

    def delete(self, key):
        if not self.head:
            return False
        elif self.head.val == key:
            self.head = self.head.next
        else:
            curr = self.head
            prev = None
            while curr and curr.val != key:
                prev = curr
                curr = curr.next
            if curr:
                prev.next = curr.next
                self.length -= 1
                return True
        return False

    def print_reverse(self, node):
        if node:
            self.print_reverse(node.next)
            print(str(node))

    def do_reverse(self):
        prev = None
        curr = self.head
        n = None
        while curr:
            n = curr.next
            curr.next = prev
            prev = curr
            curr = n
        self.head = prev

    def delete_a_node_pointer(self):
        pass

    def find_middle_element(self):
        curr = self.head
        next = self.head
        while curr and next:
            if not next.next:
                return curr.val,
            elif next.next and not next.next.next:
                return curr.val, curr.next.val
            curr = curr.next
            next = next.next.next

    def hascycle(self):
        slow = self.head
        fast = self.head
        while slow and fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if fast is slow:
                return True
        return False

    def create_a_cycle(self):
        index = 0
        curr = self.head
        while curr:
            if index == 4:
                break
            curr = curr.next
            index += 1
        self.tail.next = curr

    def find_start_of_the_cycle(self):
        slow = self.head
        fast = self.head
        cycle = False
        while slow and fast and fast.next:
            slow = slow.next
            fast = fast.next.next
            if fast is slow:
                cycle = True
                break
        if cycle:
            curr = self.head
            while curr and fast and curr is not fast:
                curr = curr.next
                fast = fast.next
            return curr
        else:
            return None

    def removeNthFromEnd(self, n):
        pass


def test_main():
    linklist = LinkedList()
    linklist.insert(2)
    # linklist.insert(4)
    # linklist.insert(3)
    # linklist.insert(-3)
    #linklist.insert(-92)
    print(str(linklist))
    # linklist.delete(2)
    # linklist.delete(3)
    # linklist.delete(4)
    # Don't print once the list has a cycle as it will loop for forever
    #linklist.create_a_cycle()
    # print(str(linklist))
    print('HasCycle : ' , linklist.hascycle())
    print('Cycle : ', str(linklist.tail), '->', linklist.find_start_of_the_cycle())
    print('Middle Element : ', linklist.find_middle_element())
    linklist.do_reverse()
    print('Reversed', str(linklist))
    print('Length LinkedList : ', str(len(linklist)))


if __name__ == '__main__':
    test_main()
\$\endgroup\$
2
  • \$\begingroup\$ (Please tag reinventing-the-wheel.) \$\endgroup\$ Commented Feb 16, 2019 at 7:41
  • 1
    \$\begingroup\$ @greybeard You can do that yourself too... \$\endgroup\$ Commented Feb 16, 2019 at 12:04

2 Answers 2

3
\$\begingroup\$

A few things I'd add to @greybeard's answer (though I'll first emphasize: Add Docstrings):

  • It's a LinkedList, I'd expect to be able to iterate over it. There should be an __iter__ function here.

  • You rely heavily on knowing what your values are (head and tail), so you should hide them as private variables (_head and _tail) to indicate that external code should not access or modify them.

  • You keep a length attribute, but when __len__ is called, you go through the expensive task of re-computing this anyway. If you trust this value, return it; if not, then don't bother keeping it.

  • You have functions to detect and manage cycles in your linked list, but many of your other functions (including __len__) don't check if they're trapped in one. This creates a ripe field for getting code locked in an infinite loop.

  • print_reverse relies on recursion, which won't work for lists of more than a few thousand items.

  • do_reverse is really vague, but seems to reverse the list; in Python, this is usually defined as __reversed__

  • delete_a_node_pointer... does nothing, throws no errors, and takes no argument. Delete this, or at least raise NotImplementedError()

  • create_a_cycle goes to element 4... for no explicable reason. This should be an argument.

  • You support creating a cycle mid-list (that is, tail points to somewhere in the middle of the list), but then elsewhere in your code treat tail.next as properly pointing to head (particularly in insert... and it should be used in delete, that's probably a bug that it's not there). Either keep your linked list as a single ring or support middle-cycles, you can't really do both.

\$\endgroup\$
1
  • \$\begingroup\$ About creating cycle's mid-list : I needed it that way. Also there's no way to delete the list if it has cycles. The strategy would be to remove the loop and then delete any node. \$\endgroup\$ Commented Feb 16, 2019 at 22:56
1
\$\begingroup\$

The code presented lacks doc strings.
This has consequences: How is anyone supposed to form an opinion on, say, create_a_cycle() (or even removeNthFromEnd())?

  • interface
    (I'm preoccupied by exposure to SIMSET at an impressible age link looks almost as good as succ.)
    after a while, I see test support mixed freely with class interface - don't
  • use of a reserved built-in symbol (next) as a variable name - don't
    (In create_a_cycle(), you manage to walk a list with a single reference. You don't even use both in __len__() and find_middle_element(). (In delete(, key), there is the less readable alternative of using succ.succ (see next avoided).))
  • __len__():
    It looks possible to do source level loop unrolling - but almost never a good idea.
    First of all, it impedes readability, especially if not commented properly.

A stripped-down shot at an unrolled __len__() (if not implementing a built-in, there would need to be a docstring…):

def __len__(self):
    # unrolled to follow two links per trip
    curr = self.head
    size = 0
    while curr:
        succ = curr.succ
        if not succ:
            return size + 1
        size += 2
        curr = succ.succ
    return size
\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.