Circular doubly linked list
Circular doubly linked list is a data structure that is a combination of both circular singly linked list and doubly linked list. It stores 2 pointers, one is pointing to the next node, the other to the previous node and the first pointer points to the last and correspondingly last to the first. This approach makes a continual circular loop which can be traversed in both directions.
Doubly linked list class¶
Implementation of the insertion and deletion algorithms are very similar to a linear doubly linked list, to check the description proceed to corresponding article.
Class implementation with OOP principles and annotations.
CircularDoublyinkedList.lua
---@class Node
---@field value any
---@field prev Node | nil
---@field next Node | nil
local Node = {}
Node.__index = Node
---@param value any
---@param prev? Node | nil
---@param next? Node | nil
---@return Node
function Node:new(value, prev, next)
return setmetatable({
value = value,
prev = prev,
next = next,
}, self)
end
---@class CircularDoublyLinkedList
---@field private _size number
---@field private _tail Node
local CircularDoublyLinkedList = {}
CircularDoublyLinkedList.__index = CircularDoublyLinkedList
---@return CircularDoublyLinkedList
function CircularDoublyLinkedList:new()
local t = {
_size = 0,
_tail = nil,
}
return setmetatable(t, self)
end
---@private
---@param value any
---@return Node
function CircularDoublyLinkedList:_addToEmpty(value)
local node = Node:new(value)
self._tail = node
node.next = self._tail
node.prev = self._tail
return self._tail
end
---@return Node | nil
function CircularDoublyLinkedList:tail()
return self._tail
end
---@return Node | nil
function CircularDoublyLinkedList:head()
return self._tail and self._tail.next
end
---@return number
function CircularDoublyLinkedList:size()
return self._size
end
---@return boolean
function CircularDoublyLinkedList:isEmpty()
return self._size == 0
end
---Inserts new node at the end of the list.
---@param value any
---@return Node
function CircularDoublyLinkedList:append(value)
local node = Node:new(value)
if self:isEmpty() then
node = self:_addToEmpty(value)
self._size = self._size + 1
else
node = self:insertAfter(value, self._tail)
self._tail = node
end
return node
end
---Inserts new node at the front of the list.
---@param value any
---@return Node
function CircularDoublyLinkedList:prepend(value)
local node = Node:new(value)
if self:isEmpty() then
node = self:_addToEmpty(value)
self._size = self._size + 1
else
node = self:insertAfter(value, self._tail)
end
return node
end
---Inserts a new node with value after given node. If after node is nil,
---then one node inserted and pointing to itself.
---@param value any
---@param after Node | nil
---@return Node
function CircularDoublyLinkedList:insertAfter(value, after)
local node = Node:new(value)
if after == nil then
node = self:_addToEmpty(value)
else
node.next = after.next
node.prev = after
after.next.prev = node
after.next = node
end
self._size = self._size + 1
return node
end
---Inserts a new node with value before given node. If before node is nil,
---then one node inserted and pointing to itself.
---@param value any
---@param before Node | nil
---@return Node
function CircularDoublyLinkedList:insertBefore(value, before)
local node = Node:new(value)
if before == nil then
node = self:_addToEmpty(value)
self._size = self._size + 1
else
node = self:insertAfter(value, before.prev)
end
return node
end
---Removes and returns a node after given node. If given node not found nil is
---returned.
---@return any
function CircularDoublyLinkedList:removeNode(node)
if node and node.next == node then
self._tail = nil
else
node.next.prev = node.prev
node.prev.next = node.next
if node == self._tail then
self._tail = node.prev
end
self._size = self._size - 1
end
return node
end
---Removes and returns a node after found node with gven value. If given
---node not found nil is
---returned.
---@return any
function CircularDoublyLinkedList:removeByValue(value)
local node = self:findByValue(value)
if node then
self:removeNode(node)
end
return node
end
---Checks if the list contins a give value.
---@param value any
---@return boolean
function CircularDoublyLinkedList:contains(value)
return self:findByValue(value) ~= nil
end
---Finds the first occurrence of the value. Returns nil if not found.
---@param value any
---@return Node | nil
function CircularDoublyLinkedList:findByValue(value)
local node = self._tail
if not node then
return nil
end
repeat
if node.value == value then
return node
end
node = node.next
until node == self._tail
return nil
end
---Traversal of the list in forwards direction.
---@param fn fun(node: Node)
function CircularDoublyLinkedList:traverseForwards(fn)
local node = self._tail and self._tail.next
if not node then
return
end
repeat
fn(node)
node = node.next
until node == self._tail.next
end
---Traversal of the list in backwards direction.
---@param fn fun(node: Node)
function CircularDoublyLinkedList:traverseBackwards(fn)
local node = self._tail
if not node then
return
end
repeat
fn(node)
node = node.prev
until node == self._tail
end
---@param sep? string
---@return string
function CircularDoublyLinkedList:toString(sep)
sep = sep or " <=> "
local t = {}
self:traverseForwards(function(node)
t[#t + 1] = tostring(node.value)
end)
return table.concat(t, sep)
end
return CircularDoublyLinkedList
Usage of CircularDoublyLinkedList class¶
local cdll = CircularDoublyLinkedList:new()
cdll:insertAfter("A")
local foundA = cdll:findByValue("A")
cdll:prepend("B")
cdll:append("C")
cdll:insertBefore("D", foundA)
cdll:insertAfter("E", foundA)
cdll:removeNode(foundA)
print(cdll:toString(), cdll:size()) --> "B <=> D <=> E <=> C" 4
References¶
Feedback
For feedback, please check the contacts section. Before writing, please specify where you came from and who you are. Sometimes spammers go insane. Thank you in advance for your understanding.