adt: implement a doubly linked list (#12950)

pull/12957/head
Miccah 2021-12-24 03:19:40 -06:00 committed by GitHub
parent c0dcd1a9a5
commit d69d2c600b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 444 additions and 0 deletions

View File

@ -0,0 +1,284 @@
module adt
struct DoublyListNode<T> {
mut:
data T
next &DoublyListNode<T> = 0
prev &DoublyListNode<T> = 0
}
pub struct DoublyLinkedList<T> {
mut:
head &DoublyListNode<T> = 0
tail &DoublyListNode<T> = 0
// Internal iter pointer for allowing safe modification
// of the list while iterating. TODO: use an option
// instead of a pointer to determine it is initialized.
iter &DoublyListIter<T> = 0
len int
}
// is_empty checks if the linked list is empty
pub fn (list DoublyLinkedList<T>) is_empty() bool {
return list.len == 0
}
// len returns the length of the linked list
pub fn (list DoublyLinkedList<T>) len() int {
return list.len
}
// first returns the first element of the linked list
pub fn (list DoublyLinkedList<T>) first() ?T {
if list.is_empty() {
return error('Linked list is empty')
}
return list.head.data
}
// last returns the last element of the linked list
pub fn (list DoublyLinkedList<T>) last() ?T {
if list.is_empty() {
return error('Linked list is empty')
}
return list.tail.data
}
// push_back adds an element to the end of the linked list
pub fn (mut list DoublyLinkedList<T>) push_back(item T) {
mut new_node := &DoublyListNode{
data: item
}
if list.is_empty() {
// first node case
list.head = new_node
list.tail = new_node
} else {
list.tail.next = new_node
new_node.prev = list.tail
list.tail = new_node
}
list.len += 1
}
// push_front adds an element to the beginning of the linked list
pub fn (mut list DoublyLinkedList<T>) push_front(item T) {
mut new_node := &DoublyListNode{
data: item
}
if list.is_empty() {
// first node case
list.head = new_node
list.tail = new_node
} else {
list.head.prev = new_node
new_node.next = list.head
list.head = new_node
}
list.len += 1
}
// pop_back removes the last element of the linked list
pub fn (mut list DoublyLinkedList<T>) pop_back() ?T {
if list.is_empty() {
return error('Linked list is empty')
}
defer {
list.len -= 1
}
if list.len == 1 {
// head == tail
value := list.tail.data
list.head = voidptr(0)
list.tail = voidptr(0)
return value
}
value := list.tail.data
list.tail.prev.next = voidptr(0) // unlink tail
list.tail = list.tail.prev
return value
}
// pop_front removes the last element of the linked list
pub fn (mut list DoublyLinkedList<T>) pop_front() ?T {
if list.is_empty() {
return error('Linked list is empty')
}
defer {
list.len -= 1
}
if list.len == 1 {
// head == tail
value := list.head.data
list.head = voidptr(0)
list.tail = voidptr(0)
return value
}
value := list.head.data
list.head.next.prev = voidptr(0) // unlink head
list.head = list.head.next
return value
}
// insert adds an element to the linked list at the given index
pub fn (mut list DoublyLinkedList<T>) insert(idx int, item T) ? {
if idx < 0 || idx > list.len {
return error('Index out of bounds')
} else if idx == 0 {
// new head
list.push_front(item)
} else if idx == list.len {
// new tail
list.push_back(item)
} else if idx <= list.len / 2 {
list.insert_front(idx, item)
} else {
list.insert_back(idx, item)
}
}
// insert_back walks from the tail and inserts a new item at index idx
// (determined from the forward index). This function should be called
// when idx > list.len/2. This helper function assumes idx bounds have
// already been checked and idx is not at the edges.
fn (mut list DoublyLinkedList<T>) insert_back(idx int, item T) {
mut node := list.node(idx + 1)
mut prev := node.prev
// prev node
// ------ ------
// |next|---->|next|
// |prev|<----|prev|
// ------ ------
new := &DoublyListNode{
data: item
next: node
prev: prev
}
// prev new node
// ------ ------ ------
// |next|---->|next|---->|next|
// |prev|<----|prev|<----|prev|
// ------ ------ ------
node.prev = new
prev.next = new
list.len += 1
}
// insert_front walks from the head and inserts a new item at index idx
// (determined from the forward index). This function should be called
// when idx <= list.len/2. This helper function assumes idx bounds have
// already been checked and idx is not at the edges.
fn (mut list DoublyLinkedList<T>) insert_front(idx int, item T) {
mut node := list.node(idx - 1)
mut next := node.next
// node next
// ------ ------
// |next|---->|next|
// |prev|<----|prev|
// ------ ------
new := &DoublyListNode{
data: item
next: next
prev: node
}
// node new next
// ------ ------ ------
// |next|---->|next|---->|next|
// |prev|<----|prev|<----|prev|
// ------ ------ ------
node.next = new
next.prev = new
list.len += 1
}
// node walks from the head or tail and finds the node at index idx.
// This helper function assumes the list is not empty and idx is in
// bounds.
fn (list &DoublyLinkedList<T>) node(idx int) &DoublyListNode<T> {
if idx <= list.len / 2 {
mut node := list.head
for h := 0; h < idx; h += 1 {
node = node.next
}
return node
}
mut node := list.tail
for t := list.len - 1; t >= idx; t -= 1 {
node = node.prev
}
return node
}
// index searches the linked list for item and returns the forward index
// or none if not found.
pub fn (list &DoublyLinkedList<T>) index(item T) ?int {
mut hn := list.head
mut tn := list.tail
for h, t := 0, list.len - 1; h <= t; {
if hn.data == item {
return h
} else if tn.data == item {
return t
}
h += 1
hn = hn.next
t -= 1
tn = tn.prev
}
return none
}
// delete removes index idx from the linked list and is safe to call
// for any idx.
pub fn (mut list DoublyLinkedList<T>) delete(idx int) {
if idx < 0 || idx >= list.len {
return
} else if idx == 0 {
list.pop_front() or {}
return
} else if idx == list.len - 1 {
list.pop_back() or {}
return
}
// node should be somewhere in the middle
mut node := list.node(idx)
node.prev.next = node.next
node.next.prev = node.prev
list.len -= 1
}
// str returns a string representation of the linked list
pub fn (list DoublyLinkedList<T>) str() string {
mut result_array := []T{}
mut node := list.head
for node != 0 {
result_array << node.data
node = node.next
}
return result_array.str()
}
// next implements the iter interface to use DoublyLinkedList with
// V's for loop syntax.
pub fn (mut list DoublyLinkedList<T>) next() ?T {
if list.iter == voidptr(0) {
// initialize new iter object
list.iter = &DoublyListIter<T>{
node: list.head
}
return list.next()
}
if list.iter.node == voidptr(0) {
list.iter = voidptr(0)
return none
}
defer {
list.iter.node = list.iter.node.next
}
return list.iter.node.data
}
struct DoublyListIter<T> {
mut:
node &DoublyListNode<T> = 0
}

View File

@ -0,0 +1,160 @@
module adt
fn test_is_empty() {
mut list := DoublyLinkedList<int>{}
assert list.is_empty() == true
list.push_back(1)
assert list.is_empty() == false
}
fn test_len() ? {
mut list := DoublyLinkedList<int>{}
assert list.len() == 0
list.push_back(1)
assert list.len() == 1
list.pop_back() ?
assert list.len() == 0
}
fn test_first() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
assert list.first() ? == 1
list.push_back(2)
assert list.first() ? == 1
list = DoublyLinkedList<int>{}
list.first() or { return }
assert false
}
fn test_last() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
assert list.last() ? == 1
list.push_back(2)
assert list.last() ? == 2
list = DoublyLinkedList<int>{}
list.last() or { return }
assert false
}
fn test_push() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
assert list.last() ? == 1
list.push_back(2)
assert list.last() ? == 2
list.push_back(3)
assert list.last() ? == 3
}
fn test_pop() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
assert list.pop_back() ? == 3
list.push_back(4)
assert list.pop_back() ? == 4
assert list.pop_back() ? == 2
list = DoublyLinkedList<int>{}
list.pop_back() or { return }
assert false
}
fn test_pop_front() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
assert list.pop_front() ? == 1
list.push_back(4)
assert list.pop_front() ? == 2
assert list.pop_front() ? == 3
list = DoublyLinkedList<int>{}
list.pop_front() or { return }
assert false
}
fn test_insert() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
// [1, 2, 3]
list.insert(1, 111) ?
// [1, 111, 2, 3]
list.insert(3, 222) ?
// [1, 111, 2, 222, 3]
assert list.pop_back() ? == 3
assert list.pop_back() ? == 222
assert list.pop_front() ? == 1
assert list.pop_front() ? == 111
}
fn test_push_front() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
list.push_front(111)
assert list.first() ? == 111
}
fn test_delete() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(0)
list.push_back(1)
list.push_back(2)
list.delete(1)
assert list.first() ? == 0
assert list.last() ? == 2
assert list.len() == 2
list.delete(1)
assert list.first() ? == 0
assert list.last() ? == 0
assert list.len() == 1
list.delete(0)
assert list.len() == 0
}
fn test_iter() ? {
mut list := DoublyLinkedList<int>{}
for i := 0; i < 10; i++ {
list.push_back(i * 10)
}
mut count := 0
for i, v in list {
count += 1
assert int(i * 10) == v
}
assert count == 10
// test it gets reset
count = 0
for i, v in list {
count += 1
assert int(i * 10) == v
}
assert count == 10
}
fn test_index() ? {
mut list := DoublyLinkedList<int>{}
for i := 0; i < 10; i++ {
list.push_back(i * 10)
}
for i := 0; i < 10; i++ {
assert list.index(i * 10) ? == i
}
}
fn test_str() ? {
mut list := DoublyLinkedList<int>{}
list.push_back(1)
list.push_back(2)
list.push_back(3)
assert list.str() == '[1, 2, 3]'
}