def test__align__long_list2(self): d1 = ListNode.of([1]) d2 = ListNode.of([1, 2, 3]) (res1, res2) = SumLists()._align(d1, d2) self.assertEqual("0,0,1", str(res1)) self.assertEqual("1,2,3", str(res2))
def test_is_palindrome(self): res = PalindromeLinkedList().isPalindrome(ListNode(1, ListNode(2))) self.assertFalse(res) res = PalindromeLinkedList().isPalindrome( ListNode(1, ListNode(2, ListNode(1)))) self.assertTrue(res)
def delete_middle(middle: ListNode): """ Solution: copy data and next ref from next node to given. It does not work for the last node """ next_node = middle.next middle.next = next_node.next middle.val = next_node.val
def test_add_two_numbers(self): """ Example: Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 8 -> 0 -> 7 """ res = AddTwoNumbers2().addTwoNumbers(ListNode.of([7, 2, 4, 3]), ListNode.of([5, 6, 4])) self.assertEqual("7,8,0,7", str(res))
def test_sum_of_reversed_with_carry(self): d1 = ListNode.of([9]) d2 = ListNode.of([1]) res = SumLists().sum_of_reversed(d1, d2).str_with_tail() self.assertEqual("0,1", res) d1 = ListNode.of([9, 9, 9]) d2 = ListNode.of([5]) res = SumLists().sum_of_reversed(d1, d2).str_with_tail() self.assertEqual("4,0,0,1", res)
def test_merge_two_lists__same_len(self): # Init l1 = ListNode.of([1, 3, 5]) l2 = ListNode.of([2, 4, 6]) # Call and check l3 = self.alg.mergeTwoLists(l1, l2).str_with_tail() self.assertEqual('1,2,3,4,5,6', l3) l3 = self.alg.mergeTwoLists(l2, l1).str_with_tail() self.assertEqual('1,2,3,4,5,6', l3)
def test_merge_two_lists__one_empty(self): # Init l1 = ListNode.of([]) l2 = ListNode.of([1, 3, 4, 10]) # Call and check l3 = self.alg.mergeTwoLists(l1, l2).str_with_tail() self.assertEqual('1,3,4,10', l3) l2 = ListNode.of([]) l1 = ListNode.of([1, 3, 4, 10]) # Call and check l3 = self.alg.mergeTwoLists(l1, l2).str_with_tail() self.assertEqual('1,3,4,10', l3)
def _traverse(self, node: TreeNode, level): """ Traverse node's children adding them to hash map by level """ if node is None: return if level not in self._heads: # First node of this level, create linked list self._heads[level] = self._tails[level] = ListNode(node.val, None) else: # Add this node to the tail of this level self._tails[level].next = ListNode(node.val, None) self._tails[level] = self._tails[level].next self._traverse(node.left, level + 1) self._traverse(node.right, level + 1)
def _adjust_nodes(self, l1: ListNode, l2: ListNode) -> (ListNode, ListNode): """ Add leading zeroes to a smaller list """ l1len, l2len = self._list_len(l1), self._list_len(l2) if l1len == l2len: return l1, l2 zroot = ListNode(0) znode = ztail = zroot for i in range(0, abs(l1len - l2len)): ztail = znode znode.next = znode = ListNode(0) if l1len < l2len: ztail.next, l1 = l1, zroot else: ztail.next, l2 = l2, zroot return l1, l2
def sum(self, head1: ListNode, head2: ListNode): """ Sum of two numbers, represented by linked lists in direct order Idea: alight the lists, go from left to right, store sum from 0 to 18 without carry, sum list is reversed Then go through sum list from right to left, calculating sum from 0 to 9 using carry, reversing the list As a result we have sum list from left to right """ head1, head2 = self._align(head1, head2) node1, node2 = head1, head2 head_res = node_sum = node_sum_left = None # Calc reversed sum list, numbers from 0 to 18 without carry while node1 is not None and node2 is not None: node_sum = ListNode(node1.val + node2.val, node_sum_left) # First step if head_res is None: head_res = node_sum # Step right node_sum_left = node_sum node1 = node1.next node2 = node2.next # Apply carry, reverse sum list carry: int = 0 node_sum_left = node_sum.next node_sum.next = None while node_sum_left is not None: # Apply carry carry = (node_sum.val + carry) // 10 node_sum.val = (node_sum.val + carry) % 10 # Reverse the node new_left = node_sum_left.next node_sum_left.next = node_sum node_sum = node_sum_left node_sum_left = new_left # If carry, add new head node_sum.val += carry carry = node_sum.val // 10 node_sum.val %= 10 if carry > 0: head_res = ListNode(1, node_sum) return head_res
def _add_with_hold(self, l1: ListNode, l2: ListNode): """Sum considering hold from recursive rest""" if not l1: return None, 0 # Recursively add the rest of the list considering hold nextnode, nexthold = self._add_with_hold(l1.next, l2.next) summ = (l1.val + l2.val + nexthold) node, hold = ListNode(summ % 10, nextnode), summ // 10 return node, hold
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode: """ Recursive approach """ # Addjust the length with leading zeroes l1, l2 = self._adjust_nodes(l1, l2) node, hold = self._add_with_hold(l1, l2) root = ListNode(1, node) if hold else node return root
def test_reverse_between__example1(self): """ Example: Input: 1->2->3->4->5->NULL, m = 2, n = 4 Output: 1->4->3->2->5->NULL """ res = ReverseLinkedList2().reverseBetween(head=ListNode.of( [1, 2, 3, 4, 5]), m=2, n=4) self.assertEqual("1,4,3,2,5", str(res))
def sum_of_reversed(head1: ListNode, head2: ListNode): """ Sum of two numbers, represented by linked lists in reverse order """ node1 = head1 node2 = head2 # Sum head_res = None prev_node_res = None carry = 0 while node1 is not None \ or node2 is not None: val1 = node1.val if node1 is not None else 0 val2 = node2.val if node2 is not None else 0 s = val1 + val2 + carry # Calc carry if s >= 10: carry = 1 s %= 10 else: carry = 0 # Calc sum and add to a new node if prev_node_res is not None: newnode3 = ListNode(s, None) prev_node_res.next = newnode3 prev_node_res = newnode3 else: # Initial step prev_node_res = head_res = ListNode(s, None) node1 = node1.next if node1 is not None else None node2 = node2.next if node2 is not None else None # Process remained carry if needed if carry > 0: prev_node_res.next = ListNode(carry, None) return head_res
def _align(head1: ListNode, head2: ListNode): """ Left pad smaller linked lists with zeroes to make sizes equal :return: aligned_head1, aligned_head2 """ node1 = head1 node2 = head2 # Go to the tail of list1 or list2 while node1.next is not None and node2.next is not None: node1 = node1.next node2 = node2.next if node1.next is None and node2.next is None: # If lists are of equal size, no align return head1, head2 # Go to the tail of longer list and calculate delta size (longer_head, longer_node) = (head1, node1) if node1.next is not None else (head2, node2) pad_head = None pad_node = None while longer_node.next is not None: if pad_head is None: # First pad_head = pad_node = ListNode(0, None) else: # Add next node to pad list pad_node.next = ListNode(0, None) pad_node = pad_node.next longer_node = longer_node.next # Insert paddings before the head of shorter list pad_node.next = head2 if longer_head == head1 else head1 return (pad_head, head2) if longer_head == head2 else (head1, pad_head)
def as_linked_list(arr): """ Converts array of values to linked list :param arr: array of values :return: head node of linked list """ if arr is None or len(arr) == 0: return None head = None last_node = None for value in arr: node = ListNode(value, None) if head is None: # First node head = node last_node = node else: # Middle or last node last_node.next = node last_node = node return head
def test_sum__with_diff_size(self): d1 = ListNode.of([1]) d2 = ListNode.of([3, 2, 1]) res = SumLists().sum(d1, d2).str_with_tail() self.assertEqual("3,2,2", res)
def test_align__empty(self): d1 = ListNode.of([1]) d2 = ListNode.of([2]) (res1, res2) = SumLists()._align(d1, d2) self.assertEqual("1", str(res1)) self.assertEqual("2", str(res2))
def test_add_two_numbers_999_1(self): res = AddTwoNumbers2().addTwoNumbers(ListNode.of([9, 9, 9]), ListNode.of([1])) self.assertEqual("1,0,0,0", str(res))
def test_getlen(self): l = PalindromeLinkedList().getlen(ListNode(1, ListNode(2))) self.assertEqual(2, l)
def test_reverse(self): n = PalindromeLinkedList().reverse(ListNode(1, ListNode(2))) self.assertEqual(2, n.val) self.assertEqual(1, n.next.val)
def test_reverse_between__emptyleft(self): res = ReverseLinkedList2().reverseBetween(head=ListNode.of( [1, 2, 3, 4, 5]), m=1, n=2) self.assertEqual("2,1,3,4,5", str(res))
def test_sum_of_reversed__with_carry(self): d1 = ListNode.of([3]) d2 = ListNode.of([9]) res = SumLists().sum_of_reversed(d1, d2).str_with_tail() self.assertEqual("2,1", res)
def test_sum_of_reversed(self): d1 = ListNode.of([3, 2, 1]) d2 = ListNode.of([3, 2, 1]) res = SumLists().sum_of_reversed(d1, d2).str_with_tail() self.assertEqual("6,4,2", res)
def test_reverse_between__emptyright(self): res = ReverseLinkedList2().reverseBetween(head=ListNode.of( [1, 2, 3, 4, 5]), m=4, n=5) self.assertEqual("1,2,3,5,4", str(res))
def test_sum__with_equal_size(self): d1 = ListNode.of([3, 2, 1]) d2 = ListNode.of([3, 2, 1]) res = SumLists().sum_of_reversed(d1, d2) self.assertEqual("6,4,2", str(res))
def test_reverse_single(self): n = PalindromeLinkedList().reverse(ListNode(1)) self.assertEqual(1, n.val)
def test_reverse_between__12345_5_5(self): res = ReverseLinkedList2().reverseBetween(head=ListNode.of( [1, 2, 3, 4, 5]), m=5, n=5) self.assertEqual("1,2,3,4,5", str(res))
def test_is_palindrome_single(self): res = PalindromeLinkedList().isPalindrome(ListNode(1)) self.assertTrue(res)
def test_reverse_between__15(self): res = ReverseLinkedList2().reverseBetween(head=ListNode.of([1]), m=1, n=1) self.assertEqual("1", str(res))