go.charczuk.com@v0.0.0-20240327042549-bc490516bd1a/sdk/collections/linked_list.go (about)

     1  /*
     2  
     3  Copyright (c) 2023 - Present. Will Charczuk. All rights reserved.
     4  Use of this source code is governed by a MIT license that can be found in the LICENSE file at the root of the repository.
     5  
     6  */
     7  
     8  package collections
     9  
    10  // LinkedList is an implementation of a fifo buffer using nodes and poitners.
    11  // Remarks; it is not threadsafe. It is constant(ish) time in all ops.
    12  type LinkedList[T any] struct {
    13  	// head is the "first" element in the list
    14  	head *LinkedListNode[T]
    15  	// tail is the "last" element in the list
    16  	tail *LinkedListNode[T]
    17  	// len is the overall length of the list
    18  	len int
    19  }
    20  
    21  // Len returns the length of the list in constant time.
    22  func (l *LinkedList[T]) Len() int {
    23  	return l.len
    24  }
    25  
    26  // PushBack adds a new value to the front of the list.
    27  func (l *LinkedList[T]) PushFront(value T) *LinkedListNode[T] {
    28  	item := &LinkedListNode[T]{Value: value}
    29  	l.len++
    30  	if l.head == nil {
    31  		l.head = item
    32  		l.tail = item
    33  		return item
    34  	}
    35  
    36  	l.head.Previous = item
    37  	item.Next = l.head
    38  	item.Previous = nil
    39  	l.head = item
    40  	return item
    41  }
    42  
    43  // Push appends a node to the end, or tail, of the list.
    44  func (l *LinkedList[T]) Push(value T) *LinkedListNode[T] {
    45  	item := &LinkedListNode[T]{
    46  		Value: value,
    47  	}
    48  
    49  	l.len++
    50  	if l.head == nil {
    51  		l.head = item
    52  		l.tail = item
    53  		return item
    54  	}
    55  
    56  	l.tail.Next = item
    57  	item.Previous = l.tail
    58  	item.Next = nil
    59  	l.tail = item
    60  	return item
    61  }
    62  
    63  // Pop removes the head element from the list.
    64  func (l *LinkedList[T]) Pop() (out T, ok bool) {
    65  	if l.head == nil {
    66  		return
    67  	}
    68  
    69  	out = l.head.Value
    70  	ok = true
    71  
    72  	l.len--
    73  	if l.head == l.tail {
    74  		l.head = nil
    75  		l.tail = nil
    76  		return
    77  	}
    78  
    79  	next := l.head.Next
    80  	next.Previous = nil
    81  	l.head = next
    82  	return
    83  }
    84  
    85  // PopBack removes the last, or tail, element of the list.
    86  func (l *LinkedList[T]) PopBack() (out T, ok bool) {
    87  	if l.tail == nil {
    88  		return
    89  	}
    90  
    91  	out = l.tail.Value
    92  	ok = true
    93  
    94  	l.len--
    95  	if l.tail == l.head {
    96  		l.head = nil
    97  		l.tail = nil
    98  		return
    99  	}
   100  
   101  	previous := l.tail.Previous
   102  	previous.Next = nil
   103  	l.tail = previous
   104  	return
   105  }
   106  
   107  // PopAll removes all the elements, returning a slice.
   108  func (l *LinkedList[T]) PopAll() (output []T) {
   109  	ptr := l.head
   110  	for ptr != nil {
   111  		output = append(output, ptr.Value)
   112  		ptr = ptr.Next
   113  	}
   114  	l.head = nil
   115  	l.tail = nil
   116  	l.len = 0
   117  	return
   118  }
   119  
   120  // Peek returns the first element of the list but does not remove it.
   121  func (q *LinkedList[T]) Peek() (out T, ok bool) {
   122  	if q.head == nil {
   123  		return
   124  	}
   125  	out = q.head.Value
   126  	ok = true
   127  	return
   128  }
   129  
   130  // PeekBack returns the last element of the list.
   131  func (q *LinkedList[T]) PeekBack() (out T, ok bool) {
   132  	if q.tail == nil {
   133  		return
   134  	}
   135  	out = q.tail.Value
   136  	ok = true
   137  	return
   138  }
   139  
   140  // Clear clears the linked list.
   141  func (l *LinkedList[T]) Clear() {
   142  	l.tail = nil
   143  	l.head = nil
   144  	l.len = 0
   145  }
   146  
   147  // Each calls the consumer for each element of the linked list.
   148  func (q *LinkedList[T]) Each(consumer func(value T)) {
   149  	if q.head == nil {
   150  		return
   151  	}
   152  
   153  	nodePtr := q.head
   154  	for nodePtr != nil {
   155  		consumer(nodePtr.Value)
   156  		nodePtr = nodePtr.Next
   157  	}
   158  }
   159  
   160  func (l *LinkedList[T]) Remove(i *LinkedListNode[T]) {
   161  	l.len--
   162  
   163  	// three possibilities
   164  	// - i is both the head and the tail
   165  	// 		- nil out both
   166  	// - i is the head
   167  	// 		- set the head to i's next
   168  	// - i is the tail
   169  	//		- set the tail to i's previous
   170  	// - i is neither
   171  	//		- if i has a next, set its previous to i's previous
   172  	//		- if i has a previous, set its previous to i's next
   173  
   174  	if l.head == i && l.tail == i {
   175  		l.head = nil
   176  		l.tail = nil
   177  		return
   178  	}
   179  	if l.head == i {
   180  		l.head = i.Next
   181  		return
   182  	}
   183  	if l.tail == i {
   184  		l.tail = i.Previous
   185  		return
   186  	}
   187  
   188  	next := i.Next
   189  	if next != nil {
   190  		next.Previous = i.Previous
   191  	}
   192  	previous := i.Previous
   193  	if previous != nil {
   194  		previous.Next = i.Next
   195  	}
   196  }
   197  
   198  // LinkedListNode is a linked list node.
   199  type LinkedListNode[T any] struct {
   200  	// Value holds the value of the node.
   201  	Value T
   202  	// Next points towards the tail.
   203  	Next *LinkedListNode[T]
   204  	// Previous points towards the head.
   205  	Previous *LinkedListNode[T]
   206  }