def test_intersects__yes(): left = LinkedList([1, 2, 3, 4, 5]) element = left[3] right = LinkedList([1, 2]) right.append(element) assert intersects(left, right) == element
def test_has_loop__yes(): ll = LinkedList([1, 2, 3, 4, 5]) element = ll[4] element.next = ll[1] assert has_loop(ll) == ll[1] assert ll[5] == ll[1]
def test_remove_dupes__with_dupes(): s = set([1, 2, 3, 4, 5, 1, 2, 3, 4, 5]) ll = LinkedList(s) s_no_dupes = set([1, 2, 3, 4, 5]) assert set(remove_dupes__using_set(ll).values) == s_no_dupes assert set(remove_dupes__no_buffer(ll).values) == s_no_dupes
def remove_dupes__no_buffer(ll): """ Without using the hash, sorting in-place is the way to do it. Just using built-in sort here, O(n log n) overall. """ sll = LinkedList(sorted(ll.values)) current = sll.head while current.next is not None: if current.value == current.next.value: current.next = current.next.next else: current = current.next return sll
def partition(ll, p): """ Admittedly, much of the implementation magic here is making sure that the basic dunder method __add__ to join two linked lists works correctly. I also rewrote the LinkedList initializer slightly to make use of a new append method that appends an Element to the end of a LinkedList and adjusts the prev/next pointers and the tail pointer correctly. """ left = LinkedList([]) right = LinkedList([]) for value in ll.values: element = Element(value) if value < p: left.append(element) else: right.append(element) return left + right
def test_sum__not_reversed_zero(): left = LinkedList([0]) right = LinkedList([0]) assert list(sum(left, right, reverse=False).values) == [0]
def test_partition__one(): ll = partition(LinkedList([1]), 1) assert validate(ll, 1)
def test_partition__no_left_side(): ll = partition(LinkedList([5, 6, 7, 8, 9, 10]), 4) assert validate(ll, 4)
def test_is_palindrome__solo(): assert is_palindrome(LinkedList('z'))
def test_is_palindrome__no(): assert not is_palindrome(LinkedList('this is a palindrome'))
def test_sum__reverse_nothing(): left = LinkedList([]) right = LinkedList([]) assert list(sum(left, right).values) == []
def sum(left, right, reverse=True): """ Summation of digits may "carry over" a value to the next digit place. When evaluating the linked list, it's simple to just take the carry along the traversal of both left and right in tandem, and apply it to the next digit place. If one list terminates before the other, the carry gets applied just the same until the traversal ends for the longer list. This algorithm runs in O(M+N) time where M, N is the length of left, right respectively. The sum of each individual element can be applied into a new element and just appended to a new linked list. The non-reversed case is a bit trickier, and requires special handling for the possibility of non-equal list lengths, as we can't assume the first elements are the same digit place. Once we determine the maximum length, we can use a hash to store each digit place keyed by power of 10. Using the hash allows us to keep the runtime to O(M+N) at the cost of O(max(M, N)) space. Other methods involve complicated back-tracking with pointers, or using a stack which is essentially the first method. The non-reserved solution avoids fun workarounds like reversing the linked list and running the first algorithm, or converting the LinkedList into numbers, summing those, and then converting the number into a string and then passing it directly into LinkedList... :rolling_on_the_floor_laughing: """ result = LinkedList([]) if reverse: current_left = left.head current_right = right.head carry = 0 while current_left is not None and current_right is not None: left_value = 0 if current_left is not None: left_value = current_left.value current_left = current_left.next right_value = 0 if current_right is not None: right_value = current_right.value current_right = current_right.next total = left_value + right_value + carry carry = total // 10 digit = total % 10 result.append(Element(digit)) if carry > 0: result.append(Element(carry)) else: # this is needed here in case there is carry past the max digit place places = defaultdict(lambda: 0) for ll in [left, right]: for index, element in enumerate(ll): place = len(ll) - index - 1 places[place] += element.value for place in sorted(places.keys()): carry = places[place] // 10 places[place] %= 10 if carry > 0: places[place + 1] += carry for place in sorted(places.keys(), reverse=True): result.append(Element(places[place])) return result
def test_delete__next_to_head(): s = set([1, 2, 3, 4, 5]) ll = LinkedList(s) element = ll[1] delete(ll, element) assert set(ll.values) == set([1, 3, 4, 5])
def test_delete__middle(): s = set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) ll = LinkedList(s) element = ll[6] delete(ll, element) assert set(ll.values) == set([1, 2, 3, 4, 5, 6, 8, 9, 10])
def test_intersects__no(): left = LinkedList([1, 2, 3, 4, 5]) right = LinkedList([1, 2, 3, 4, 5]) assert intersects(left, right) is None
def test_sum__not_reversed_example(): left = LinkedList([6, 1, 7]) right = LinkedList([2, 9, 5]) assert list(sum(left, right, reverse=False).values) == [9, 1, 2]
def test_sum__not_reversed_plus_carry(): left = LinkedList([6, 1, 9]) right = LinkedList([9, 9, 5]) assert list(sum(left, right, reverse=False).values) == [1, 6, 1, 4]
def test_remove_dupes__no_dupes(): s = set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) ll = LinkedList(s) assert set(remove_dupes__using_set(ll).values) == s assert set(remove_dupes__no_buffer(ll).values) == s
def test_is_palindrome__yes(): assert is_palindrome(LinkedList('doomanevildeedlivenamood')) assert is_palindrome(LinkedList('drabasafoolaloofasabard'))
def remove_dupes__using_set(ll): """ The naive approach is to use a set to add values and then just return the values from the set. This is O(n). """ return LinkedList(set(ll.values))
def test_is_palindrome__empty(): assert is_palindrome(LinkedList(''))
def test_sum__reverse_zero(): left = LinkedList([0]) right = LinkedList([0]) assert list(sum(left, right).values) == [0]
def test_partition__example(): ll = partition(LinkedList([3, 5, 8, 5, 10, 2, 1]), 5) assert validate(ll, 5)
def test_sum__reverse_example(): left = LinkedList([7, 1, 6]) right = LinkedList([5, 9, 2]) assert list(sum(left, right).values) == [2, 1, 9]
def test_partition__no_right_side(): ll = partition(LinkedList([1, 2, 3, 4, 5, 6]), 7) assert validate(ll, 7)
def test_sum__reverse_plus_carry(): left = LinkedList([9, 1, 6]) right = LinkedList([5, 9, 9]) assert list(sum(left, right).values) == [4, 1, 6, 1]
def test_partition__none(): ll = partition(LinkedList([]), 0) assert len(ll) == 0
def test_has_loop__no(): ll = LinkedList([1, 2, 3, 4, 5]) assert has_loop(ll) is None