def sum_lists_reverse(a: LinkedListNode, b: LinkedListNode) -> LinkedListNode: """Add two integers stored as a linked list. Each digit of the integer is a node, and the 1's place is the head node. Runtime: O(n) Memory: O(1) """ carry = 0 head = curr = None while a is not None or b is not None or carry == 1: x = 0 if a is None else a.data y = 0 if b is None else b.data if a is not None: a = a.next if b is not None: b = b.next digit = x + y + carry carry = digit // 10 digit %= 10 if head is None: head = LinkedListNode(digit) curr = head else: curr.next = LinkedListNode(digit) curr = curr.next return head
def sum_lists_forward_stack(a: LinkedListNode, b: LinkedListNode) -> LinkedListNode: """Add two integers stored as a linked list using a stack. Each digit of the integer is a node, and the 1's place is the tail. Runtime: O(n) Memory: O(n) """ stack_a = [] stack_b = [] while a is not None: stack_a.append(a.data) a = a.next while b is not None: stack_b.append(b.data) b = b.next head = None carry = 0 while len(stack_a) > 0 or len(stack_b) > 0 or carry == 1: x = stack_a.pop() if len(stack_a) > 0 else 0 y = stack_b.pop() if len(stack_b) > 0 else 0 digit = x + y + carry carry = digit // 10 digit %= 10 curr = LinkedListNode(digit) if head is None: head = curr else: curr.next = head head = curr return head
def sum_lists_forward(a: LinkedListNode, b: LinkedListNode) -> LinkedListNode: """Add two integers stored as a linked list. Each digit of the integer is a node, and the 1's place is the tail. Runtime: O(n) Memory: O(1) """ x = y = 0 while a is not None: # Could refactor to another function x += a.data if a.next is not None: x *= 10 a = a.next while b is not None: y += b.data if b.next is not None: y *= 10 b = b.next total = x + y head = None while total > 0: digit = total % 10 total = total // 10 curr = LinkedListNode(digit) if head is None: head = curr else: curr.next = head head = curr return head
def partition(head: LinkedListNode, x: int) -> LinkedListNode: """Partition a singly linked list around a value x. All nodes less than x come before nodes greater than or equal to x. The partition element x can appear anywhere in second grouping. Runtime: O(n) Memory: O(n) """ new_head = left_tail = None right_curr = head right_prev = None while right_curr is not None: d = right_curr.data if d < x: if new_head is None: new_head = left_tail = LinkedListNode(d) else: left_tail.next = LinkedListNode(d) left_tail = left_tail.next if right_prev is None: # i.e., haven't found anything >= x head = right_curr = right_curr.next else: right_prev.next = right_curr.next right_curr = right_curr.next else: right_prev = right_curr right_curr = right_curr.next left_tail.next = head return new_head
def palindrome_reverse_list(head: LinkedListNode) -> bool: """Check if a linked list is a palindrome. Ignores punctuation, whitespace, and case. Runtime: O(n) Memory: O(n) """ reverse_list = LinkedListNode(head.data) runner = head.next while runner is not None: new_node = LinkedListNode(runner.data) new_node.next = reverse_list reverse_list = new_node runner = runner.next while head is not None: h = chr(head.data) r = chr(reverse_list.data) if not h.isalpha(): head = head.next elif not r.isalpha(): reverse_list = reverse_list.next elif h.lower() != r.lower(): return False else: head = head.next reverse_list = reverse_list.next return True
def sum_lists_forward_recursive(a: LinkedListNode, b: LinkedListNode) -> LinkedListNode: """Add two integers stored as a linked list using recursion. Each digit of the integer is a node, and the 1's place is the tail. Runtime: O(n) Memory: O(n) """ curr_a, curr_b = a, b len_a = len_b = 0 while curr_a is not None: len_a += 1 curr_a = curr_a.next while curr_b is not None: len_b += 1 curr_b = curr_b.next if len_a > len_b: b = pad_zeroes(b, len_a - len_b) elif len_b > len_a: a = pad_zeroes(a, len_b - len_a) head, carry = sum_lists_forward_recursive_helper(a, b) if carry > 0: curr = LinkedListNode(1) curr.next = head head = curr return head
def setUp(self): self.node_a = LinkedListNode(ord('a')) self.node_c = LinkedListNode(ord('c')) self.node_f = LinkedListNode(ord('f')) self.node_t = LinkedListNode(ord('t')) self.node_c.next = self.node_a self.node_f.next = self.node_a self.node_a.next = self.node_t
def setUp(self): self.head_a = LinkedListNode(ord('a')) for i in range(5): self.head_a.append_to_tail(ord('b') + i) self.head_b = LinkedListNode(1) self.head_b.append_to_tail(5) self.head_b.append_to_tail(9) self.head_b.append_to_tail(12)
def append(self, key, value): new_node = LinkedListNode(key, value) self.tail.prev.next = new_node new_node.prev = self.tail.prev new_node.next = self.tail self.tail.prev = new_node return new_node
def convert_to_reverse_linked_list(number: int) -> LinkedListNode: head = None while number > 0: digit = number % 10 if head is None: head = LinkedListNode(digit) else: head.append_to_tail(digit) number //= 10 return head
def add_to_front(self, value): """ O(1) """ temp_node = self.head self.head = LinkedListNode(value) self.head.set_next_node(temp_node) self.length += 1 if self.length == 1: self.tail = self.head
def append(self, data): node = LinkedListNode(data) if self.head == None: self.head = node else: node.next_node = self.tail self.tail = node self.list.append(node)
def delete_middle_node(node: LinkedListNode) -> None: """Delete a node from anywhere in the middle of a singly linked list. Runtime: O(n) Memory: O(1) """ while node.next.next is not None: node.data = node.next.data node = node.next node.data = node.next.data node.next = None
def convert_to_forward_linked_list(number: int) -> LinkedListNode: # https://stackoverflow.com/questions/2189800/length-of-an-integer-in-python n = 1 if number == 0 else int(log10(number)) + 1 head = None while n > 0: n -= 1 digit = number // 10**n % 10 if head is None: head = LinkedListNode(digit) else: head.append_to_tail(digit) return head
def enqueue(self, value): newNode = LinkedListNode(value) if (len(self) == 0): self.first = newNode self.last = newNode else: self.last.next = newNode newNode.prev = self.last self.last = newNode self.size += 1
class TestQ02_02ReturnKthToLast(unittest.TestCase): def setUp(self): self.head = LinkedListNode(20) for i in range(19, -1, -1): self.head.append_to_tail(i) def test_return_kth_to_last(self): self.assertEqual(return_kth_to_last(self.head, 5), 5) self.assertEqual(return_kth_to_last(self.head, 15), 15) def test_return_kth_to_last_recursive(self): self.assertEqual(return_kth_to_last_recursive(self.head, 5), 5) self.assertEqual(return_kth_to_last_recursive(self.head, 15), 15)
def convert_str_to_linked_list(s: str) -> LinkedListNode: if s is None or len(s) == 0: return None head = curr = None for i in range(len(s)): ch = ord(s[i]) if head is None: head = LinkedListNode(ch) curr = head else: curr.next = LinkedListNode(ch) curr = curr.next return head
def sum_lists_forward_recursive_helper( a: LinkedListNode, b: LinkedListNode) -> (LinkedListNode, int): if a.next is None and b.next is None: digit = a.data + b.data carry = digit // 10 digit %= 10 curr = LinkedListNode(digit) return curr, carry next_node, carry = sum_lists_forward_recursive_helper(a.next, b.next) digit = a.data + b.data + carry carry = digit // 10 digit %= 10 curr = LinkedListNode(digit) curr.next = next_node return curr, carry
class TestQ02_04Partition(unittest.TestCase): def setUp(self): self.head = LinkedListNode(3) elements = [5, 8, 5, 10, 2, 1] for e in elements: self.head.append_to_tail(e) def test_partition(self): partition_element = 5 curr = partition(self.head, partition_element) count = 0 while curr.data < partition_element: count += 1 curr = curr.next while curr is not None: count += 1 self.assertGreaterEqual(curr.data, partition_element) curr = curr.next self.assertEqual(count, 7)
def test_loop_detection(self): loop_list = LinkedList(None) for value in self.loop_values: loop_list.append_to_tail(value) loop_list.head = loop_list.head.next node = LinkedListNode('C') current = loop_list.head while current.value != 'B': current = current.next node.next = current.next.next current.next = node while current.next is not None: current = current.next current.next = node self.assertEqual(self.expected, loop_detection(loop_list)) self.assertEqual(self.expected, loop_detection_no_extra_space(loop_list))
def add_to_back(self, value): """ O(1) """ node = LinkedListNode(value) if self.is_empty(): self.head = node else: self.tail.set_next_node(node) self.tail = node self.length += 1
class TestQ02_01RemoveDups(unittest.TestCase): def setUp(self): self.head = LinkedListNode(0) for i in range(20): self.head.append_to_tail(i % 5) def test_remove_dups(self): remove_dups(self.head) self.assertEqual(self.count_nodes(), 5) def test_remove_dups_no_buffer(self): remove_dups_no_buffer(self.head) self.assertEqual(self.count_nodes(), 5) def count_nodes(self) -> int: curr = self.head count = 0 while curr is not None: count += 1 curr = curr.next return count
class TestQ02_03DeleteMiddleNode(unittest.TestCase): def setUp(self): self.head_a = LinkedListNode(ord('a')) for i in range(5): self.head_a.append_to_tail(ord('b') + i) self.head_b = LinkedListNode(1) self.head_b.append_to_tail(5) self.head_b.append_to_tail(9) self.head_b.append_to_tail(12) def test_delete_middle_node(self): curr = self.head_a while curr.data != ord('c'): curr = curr.next delete_middle_node(curr) self.assertEqual(self.convert_linked_list_to_string(), 'abdef') curr = self.head_b while curr.data != 9: curr = curr.next delete_middle_node(curr) curr = self.head_b self.assertEqual(curr.data, 1) curr = curr.next self.assertEqual(curr.data, 5) curr = curr.next self.assertEqual(curr.data, 12) def convert_linked_list_to_string(self) -> str: curr = self.head_a letters = [] while curr is not None: letters.append(chr(curr.data)) curr = curr.next return ''.join(letters)
def remove_dups(n: LinkedListNode) -> None: """Remove duplicates from an unsorted singly linked list. Runtime: O(n) Memory: O(n) """ uniques = {n.data} while n is not None and n.next is not None: if n.next.data in uniques: n.next = n.next.next else: uniques.add(n.next.data) n = n.next
def setUp(self): self.node_a = LinkedListNode(ord('a')) self.node_b = LinkedListNode(ord('b')) self.node_c = LinkedListNode(ord('c')) self.node_d = LinkedListNode(ord('d')) self.node_e = LinkedListNode(ord('e')) self.node_f = LinkedListNode(ord('f')) self.node_g = LinkedListNode(ord('g')) self.node_h = LinkedListNode(ord('h')) self.node_i = LinkedListNode(ord('i')) self.node_a.next = self.node_b self.node_b.next = self.node_c self.node_c.next = self.node_d self.node_d.next = self.node_e self.node_e.next = self.node_f self.node_f.next = self.node_g self.node_g.next = self.node_h self.node_h.next = self.node_i self.node_i.next = self.node_d
def sum_lists_reverse_recursive(a: LinkedListNode, b: LinkedListNode, carry: int = 0) -> LinkedListNode: """Add two integers stored as a linked list without intermediate conversion. Each digit of the integer is a node, and the 1's place is the head node. Runtime: O(n) Memory: O(n) """ if a is None and b is None: return None x = 0 if a is None else a.data y = 0 if b is None else b.data digit = x + y + carry carry = digit // 10 digit %= 10 head = LinkedListNode(digit) if a is not None: a = a.next if b is not None: b = b.next head.next = sum_lists_reverse_recursive(a, b, carry) return head
def is_palindrome(linked_list): reverse_list = LinkedList(None) current = linked_list.head while current is not None: head = reverse_list.head reverse_list.head = LinkedListNode(current.value) reverse_list.head.next = head current = current.next while reverse_list.head.value is not None: if reverse_list.head.value != linked_list.head.value: return False reverse_list.head = reverse_list.head.next linked_list.head = linked_list.head.next return True
def append_to_tail(self, node_value): current = self.head while current.next is not None: current = current.next current.next = LinkedListNode(node_value)
def __init__(self, value): self.head = LinkedListNode(value)
def pad_zeroes(node: LinkedListNode, n: int) -> LinkedListNode: for _ in range(n): new_node = LinkedListNode(0) new_node.next = node node = new_node return node
class LinkedList(object): def __init__(self): self.head = None self.tail = None self.length = 0 def is_empty(self): return self.length == 0 def add_to_front(self, value): """ O(1) """ temp_node = self.head self.head = LinkedListNode(value) self.head.set_next_node(temp_node) self.length += 1 if self.length == 1: self.tail = self.head def add_to_back(self, value): """ O(1) """ node = LinkedListNode(value) if self.is_empty(): self.head = node else: self.tail.set_next_node(node) self.tail = node self.length += 1 def add(self, value): """ O(1) """ self.add_to_back(value) def remove_front(self): if not self.is_empty(): self.head = self.head.get_next_node() self.length -= 1 if self.length == 0: self.tail = None def remove_back(self): """ O(n) """ if not self.is_empty(): if self.length == 1: self.tail = None self.head = None else: current = self.head while current.get_next_node() is not self.tail: current = current.get_next_node() current.next_node = None self.tail = current self.length -= 1 def remove(self, item): """ O(n) :param item: :return: """ if not self.is_empty(): previous = None current_node = self.head while current_node is not None: if current_node.get_value() == item: if previous is not None: previous.set_next_node(current_node.get_next_node()) if current_node.get_next_node() is None: self.tail = previous else: self.remove_front() self.length -= 1 return True previous = current_node current_node = current_node.get_next_node() return False def clear(self): """ O(1) :return: """ self.head = None self.tail = None self.length = 0 def enumerate(self): """ O(n) :return: """ current_node = self.head while current_node != None: yield current_node current_node = current_node.get_next_node() def __str__(self): """ O(n) """ if not self.is_empty(): buffer_array = [] for node in self.enumerate(): buffer_array.append(str(node)) buffer_array.append(str('=>')) buffer_array.append('None') return ''.join(buffer_array) else: return 'This Linked List is empty!'
def setUp(self): self.head = LinkedListNode(20) for i in range(19, -1, -1): self.head.append_to_tail(i)