2023-08-07 15:52:35 -07:00
..
2023-08-07 15:51:56 -07:00
2023-08-07 15:45:14 -07:00
2023-08-02 19:19:38 -07:00
2023-08-03 18:06:53 -07:00
2023-08-07 15:48:20 -07:00
2023-08-02 16:41:10 -07:00
2023-08-07 15:48:48 -07:00
2023-08-02 19:14:44 -07:00
2023-08-02 19:10:37 -07:00
2023-08-02 17:46:34 -07:00
2023-08-03 18:53:47 -07:00

Linked Lists


  • each node in a singly-linked list contains a value and a reference field to link to the next node. the head node (first node) usually represents the whole list.

  • unlike an array, a linked list does not provide constant time access to an index (as it needs to interact through all k elements), however addition and removal of elements are constant time.

    • if you need to add or delete a node frequently, a linked list could be a good choice.
    • if you need to access an element by index often, an array might be a better choice than a linked list.
  • nodes can be added at the beginning, head needs to be updated (current -> head and head = current).

  • to remove a node you set prev.next equal to node.next. if it's a double list, you also update node.next with node.next.prev to node.prev (and deallocate the memory).




detecting cycles


  • cycles in a linked list can be detected by using a hash table or with the two-pointer technique.

  • if there is no cycle, the faster pointer (going 2 steps) will stop at the end of the list, but if there is a cycle, the fast pointer will eventually meet the slow pointer (going 1 step).

  • if there is no cycle, the faster pointer takes N/2 to reach the end of the list (N being the length).



reversing the list


  • keep track of the original head node and the new head node (for instance, with two pointers).

def reverse_list(head):

  if head is None:
      return head
        
  final_head = head
        
  while head.next:
      
      new_node = head.next
      head.next = new_node.next
      new_node.next = final_head
      final_head = new_node
        
  return final_head


removing elements


  • given a head of a linked list and a value, how to remove all the nodes of the list that have that value?

  • this problem is easy if one has to delete a node in the middle, as all you need to do is loop until the predecessor node and change the pointers.

  • however, if the node to be deleted is in the head of the list, the best way is to use a sentinel node. sentinel nodes are widely used in trees and linked lists as pseudo-heads, pseudo-tails, markers of level end, etc. they are purely functional and usually do not hold any data. their main purpose is to standardize the process (by making the list never empty or headless).

  • examples are LRU cache (where sentinel nodes are used as pseudo-head and pseudo-tail) and tree level order traversal (where sentinel nodes are used to mark level end).


def remove_elements(head, val):
        
        sentinel = ListNode(0)
        sentinel.next = head
        prev, current = sentinel, head

        while current:
            if current.val == val:
                prev.next = current.next
            else:
                prev = current
            current = current.next
                
        return sentinel.next


doubly linked lists


  • in doubly linked lists, a node has a previous field.

  • when it comes to add and delete operations, extra attention is needed when you want to delete or insert at the beginning or at the end of the list.