PairedLinkedLists
This package provides doubly-linked list and sorted skip-list implementations in Julia, with support for reciprocal inter-list node links.
List types:
DoublyLinkedList— a standard doubly-linked list.PairedLinkedList— a doubly-linked list whose nodes each carry a reciprocal link to a node in a secondPairedLinkedList. Adding a link from node A to node B automatically sets the reverse link from B to A.TargetedLinkedList— a doubly-linked list whose nodes carry a one-way link to a node in any other list type.SkipList— a sorted skip list with O(log n) insertion and deletion.PairedSkipList— a sorted skip list whose nodes carry reciprocal inter-list links.
Installation
using Pkg
Pkg.add("PairedLinkedLists")Inter-list linking
The distinctive feature of this package is the ability to link nodes across two lists. A paired link is reciprocal: addtarget!(node_a, node_b) sets node_a.target = node_b and node_b.target = node_a in a single call, and removetarget! clears both ends. A targeted link is one-way: only the source node's target field is set, leaving the destination unchanged.
Linking works at both the list level (pairing two lists together so their nodes can be linked) and the node level (pairing individual nodes within already-paired lists).
Usage
Basic list operations
julia> l = DoublyLinkedList{Int}(1, 2, 3, 4, 5);
julia> push!(l, 6)
DoublyLinkedList{Int64}(1, 2, 3, 4, 5, 6)
julia> pop!(l)
6
julia> popat!(l, 2)
2
julia> insert!(l, 2, 10)
DoublyLinkedList{Int64}(1, 10, 3, 4, 5)
julia> collect(l)
5-element Vector{Int64}:
1
10
3
4
5Node access and iteration
getnode returns the node at a given index. Nodes hold data and prev/next references. ListNodeIterator iterates over nodes rather than data values:
julia> l = DoublyLinkedList{Int}(10, 20, 30);
julia> node = getnode(l, 2)
ListNode{Int64, DoublyLinkedList{Int64}}(20)
julia> node.prev.data
10
julia> node.next.data
30
julia> [n.data for n in ListNodeIterator(l)]
3-element Vector{Int64}:
10
20
30
julia> [n.data for n in ListNodeIterator(l; rev=true)]
3-element Vector{Int64}:
30
20
10Inter-list linking with PairedLinkedList
julia> l1 = PairedLinkedList{Int}(1, 2, 3);
julia> l2 = PairedLinkedList{Int}(10, 20, 30);
julia> addtarget!(l1, l2);
julia> l1.target === l2
true
julia> addtarget!(getnode(l1, 1), getnode(l2, 2));
julia> getnode(l1, 1).target.data
20
julia> getnode(l2, 2).target.data
1
julia> removetarget!(getnode(l1, 1));
julia> hastarget(getnode(l1, 1))
false
julia> hastarget(getnode(l2, 2))
falseSorted skip lists
SkipList keeps elements in sorted order and supports O(log n) insertion and deletion. A custom sort key can be supplied via the sortedby keyword:
julia> sl = SkipList{Int}(5, 3, 1, 4, 2);
julia> collect(sl)
5-element Vector{Int64}:
1
2
3
4
5
julia> push!(sl, 0); collect(sl)
6-element Vector{Int64}:
0
1
2
3
4
5
julia> negate(x) = -x;
julia> sl2 = SkipList{Int}(5, 3, 1, 4, 2; sortedby=negate);
julia> collect(sl2)
5-element Vector{Int64}:
5
4
3
2
1API Reference
PairedLinkedLists.PairedLinkedListsPairedLinkedLists.AbstractDoublyLinkedListPairedLinkedLists.AbstractLinkedListPairedLinkedLists.AbstractListPairedLinkedLists.AbstractListNodePairedLinkedLists.AbstractNodePairedLinkedLists.AbstractPairedLinkedListPairedLinkedLists.AbstractPairedListNodePairedLinkedLists.AbstractPairedSkipListPairedLinkedLists.AbstractPairedSkipNodePairedLinkedLists.AbstractSkipLinkedListPairedLinkedLists.AbstractSkipListPairedLinkedLists.AbstractSkipNodePairedLinkedLists.AbstractTargetedLinkedListPairedLinkedLists.AbstractTargetedListNodePairedLinkedLists.DoublyLinkedListPairedLinkedLists.ListDataIteratorPairedLinkedLists.ListDataIteratorPairedLinkedLists.ListNodePairedLinkedLists.ListNodeIteratorPairedLinkedLists.ListNodeIteratorPairedLinkedLists.OneWayPairedLinkedLists.PairedLinkedListPairedLinkedLists.PairedListNodePairedLinkedLists.PairedSkipListPairedLinkedLists.PairedSkipNodePairedLinkedLists.ReciprocalPairedLinkedLists.SkipListPairedLinkedLists.SkipNodePairedLinkedLists.TargetKindPairedLinkedLists.TargetedLinkedListPairedLinkedLists.TargetedListNodePairedLinkedLists.UntargetedBase.mapBase.popat!PairedLinkedLists.addtarget!PairedLinkedLists.atheadPairedLinkedLists.attailPairedLinkedLists.copyfromcachePairedLinkedLists.copyfromcachePairedLinkedLists.deletenode!PairedLinkedLists.deletenode!PairedLinkedLists.getnodePairedLinkedLists.hastargetPairedLinkedLists.headPairedLinkedLists.insertafter!PairedLinkedLists.insertbefore!PairedLinkedLists.isdisconnectedPairedLinkedLists.listtypePairedLinkedLists.newnodePairedLinkedLists.nodetypePairedLinkedLists.removetarget!PairedLinkedLists.skiplistsidenticalPairedLinkedLists.tailPairedLinkedLists.target
PairedLinkedLists.PairedLinkedLists — Module
PairedLinkedLists.jl provides implementations for doubly-linked lists and skip lists, as well as "paired lists" that also contain inter-list links between nodes in two separate lists.
See also DoublyLinkedList, PairedLinkedList, SkipList, PairedSkipList
PairedLinkedLists.AbstractDoublyLinkedList — Type
Abstract type for plain doubly-linked lists. T is the element type.
PairedLinkedLists.AbstractLinkedList — Type
Abstract type for linked-list containers. T is the element type.
PairedLinkedLists.AbstractList — Type
Root abstract type for all list containers. T is the element type.
PairedLinkedLists.AbstractListNode — Type
Abstract type for nodes of a doubly-linked list. T is the element type; L is the parent list type.
PairedLinkedLists.AbstractNode — Type
Root abstract type for all list nodes. T is the element type; L is the parent list type.
PairedLinkedLists.AbstractPairedLinkedList — Type
Abstract type for doubly-linked lists whose nodes carry a reciprocal inter-list target link. T is the element type.
PairedLinkedLists.AbstractPairedListNode — Type
Abstract type for nodes with a reciprocal inter-list target link. T is the element type; L is the parent list type.
PairedLinkedLists.AbstractPairedSkipList — Type
Abstract type for skip lists whose nodes carry a reciprocal inter-list target link. T is the element type; F is the type of the sortedby key function.
PairedLinkedLists.AbstractPairedSkipNode — Type
Abstract type for skip-list nodes with a reciprocal inter-list target link. T is the element type; L is the parent list type.
PairedLinkedLists.AbstractSkipLinkedList — Type
Abstract type for skip-list-backed containers. T is the element type; F is the type of the sortedby key function.
PairedLinkedLists.AbstractSkipList — Type
Abstract type for skip lists. T is the element type; F is the type of the sortedby key function.
PairedLinkedLists.AbstractSkipNode — Type
Abstract type for nodes of a skip list. T is the element type; L is the parent list type.
PairedLinkedLists.AbstractTargetedLinkedList — Type
Abstract type for doubly-linked lists whose nodes carry a one-way inter-list target link. T is the element type; N is the target node type; L is the target list type.
PairedLinkedLists.AbstractTargetedListNode — Type
Abstract type for nodes with a one-way inter-list target link. T is the element type; N is the target node type; L is the parent list type.
PairedLinkedLists.DoublyLinkedList — Type
l = DoublyLinkedList{::Type}()
l = DoublyLinkedList(elts...)Create a DoublyLinkedList made up of ListNodes with nodes containing data of a specified type.
The list contains its length len, a "dummy" node head at the beginning of the list, and a "dummy" node tail at the end of the list.
The first "real" node of a list l can be accessed with l.head.next or head(l). Similarly, the last "real" node can be accessed with l.tail.prev or tail(l).
See also ListNode, SkipList, PairedLinkedList, TargetedLinkedList
PairedLinkedLists.ListDataIterator — Type
ListDataIterator(start; rev=false)
ListDataIterator(start, stop; rev=false)Return an iterator over the data values of a linked list, beginning at node start.
If stop is provided, iteration halts when that node is reached (the stop node itself is not yielded). start and stop must belong to the same list.
If rev is true, the iterator advances toward the head of the list; otherwise it advances toward the tail.
PairedLinkedLists.ListDataIterator — Method
ListDataIterator(list; rev=false)Return an iterator over the data values of a linked list.
If rev is true, the iterator starts at the tail and advances toward the head; otherwise it starts at the head and advances toward the tail.
PairedLinkedLists.ListNode — Type
node = ListNode(list::DoublyLinkedList [, data])Create a ListNode belonging to the specified list. The node contains a reference list to the parent list and contains the provided data, but it has no specific insertion point into list (see insertafter!).
node.prev and node.next represent the previous and next nodes, respectively, of a list.
See also DoublyLinkedList, PairedListNode, TargetedListNode.
PairedLinkedLists.ListNodeIterator — Type
ListNodeIterator(start; rev=false)
ListNodeIterator(start, stop; rev=false)Return an iterator over the nodes of a linked list, beginning at node start.
If stop is provided, iteration halts when that node is reached (the stop node itself is not yielded). start and stop must belong to the same list.
If rev is true, the iterator advances toward the head of the list; otherwise it advances toward the tail.
PairedLinkedLists.ListNodeIterator — Method
ListNodeIterator(list; rev=false)Return an iterator over the nodes of a linked list.
If rev is true, the iterator starts at the tail and advances toward the head; otherwise it starts at the head and advances toward the tail.
Examples
julia> l = DoublyLinkedList{Int}(1, 2, 3, 4, 5);
julia> [node.data for node in ListNodeIterator(l)]
5-element Vector{Int64}:
1
2
3
4
5
julia> [node.data for node in ListNodeIterator(l; rev=true)]
5-element Vector{Int64}:
5
4
3
2
1
julia> [node.data for node in ListNodeIterator(getnode(l, 2), getnode(l, 4))]
2-element Vector{Int64}:
2
3PairedLinkedLists.OneWay — Type
OneWay <: TargetKindOne-way target links: addtarget! and removetarget! update only the originating object, leaving its target unchanged.
PairedLinkedLists.PairedLinkedList — Type
l = PairedLinkedList{::Type}()
l = PairedLinkedList{::Type}(elts...)Create a PairedLinkedList made up of PairedListNodes containing data of a specified type. Each node can have an inter-list link to a node belonging to the list's target.
The list contains its length len, a "dummy" node head at the beginning of the list, and a "dummy" node tail at the end of the list.
The first "real" node of a list l can be accessed with l.head.next or head(l). Similarly, the last "real" node can be accessed with l.tail.prev or tail(l).
See also PairedListNode, PairedSkipList, DoublyLinkedList, TargetedLinkedList
Examples
julia> l1 = PairedLinkedList{Int}(1, 2, 3);
julia> l2 = PairedLinkedList{Int}(10, 20, 30);
julia> addtarget!(l1, l2);
julia> l1.target === l2
true
julia> l2.target === l1
truePairedLinkedLists.PairedListNode — Type
node = PairedListNode(list::PairedLinkedList, data)Create a PairedListNode belonging to the specified list. The node contains a reference list to the parent list and contains the provided data, but it has no specific insertion point into list (see insertafter!).
node.prev and node.next represent the previous and next nodes, respectively, of a list.
A node's target should always either be a reference to itself (denoting an unpaired node) or a node belonging to the target of its parent list.
The target link is assumed to be reciprocated for a PairedListNode. For example, node === node.target.target should be true. For one-way inter-list links, see TargetedListNode.
See also PairedLinkedList, ListNode, TargetedListNode.
PairedLinkedLists.PairedSkipList — Type
l = PairedSkipList{::Type}(; sortedby=identity, skipfactor=2)
l = PairedSkipList{::Type}(elts...; sortedby=identity, skipfactor=2)Create a PairedSkipList with nodes containing data of a specified type. Each node can have an inter-list link to a node belonging to the list's target.
The list contains its length len, a "dummy" node head at the beginning of the list, and a "dummy" node tail at the end of the list. The list also contains a reference to its "target" list.
The first "real" node of a list l can be accessed with l.head.next or head(l). Similarly, the last "real" node can be accessed with l.tail.prev or tail(l).
The skipfactor controls the average branching ratio between levels (default: 2). The sortedby function determines the sort key applied to each element (default: identity).
See also SkipList, PairedSkipNode
PairedLinkedLists.PairedSkipNode — Type
node = PairedSkipNode(list::PairedSkipList [, data])Create a PairedSkipNode belonging to the specified list. The node contains a reference list to the parent skip list and contains the provided data, but it has no specific insertion point into list (see insertafter!).
node.prev and node.next represent the previous and next nodes, respectively, of a list. node.up and node.down represent the nodes in adjacent levels within the skip list data structure.
A node's target should always either be a reference to itself (denoting unpaired node) or a node belonging to the target of its parent list.
The target link is assumed to be reciprocated for a PairedSkipNode. For example, node === node.target.target should be true.
See also PairedSkipList, SkipNode
PairedLinkedLists.Reciprocal — Type
Reciprocal <: TargetKindTwo-way target links: addtarget! and removetarget! update both an object and its target, maintaining x === x.target.target.
PairedLinkedLists.SkipList — Type
l = SkipList{::Type}(; sortedby=identity, skipfactor=2)
l = SkipList{::Type}(elts...; sortedby=identity, skipfactor=2)Create a SkipList made up of SkipNodes containing data of a specified type.
The list contains a "dummy" node head at the beginning of the list and a "dummy" node tail at the end of the list.
The node at the head of the topmost level of the datastructure can be accessed by l.top.
The first "real" node of a list l can be accessed with l.head.next or head(l). Similarly, the last "real" node can be accessed with l.tail.prev or tail(l).
The skipfactor controls the average branching ratio between levels (default: 2). The sortedby function determines the sort key applied to each element (default: identity).
See also PairedSkipList, SkipNode
Examples
julia> l = SkipList{Int}(3, 1, 4, 1, 5, 9);
julia> collect(l)
6-element Vector{Int64}:
1
1
3
4
5
9PairedLinkedLists.SkipNode — Type
node = SkipNode(list::SkipList [, data])Create a SkipNode belonging to the specified list. The node contains a reference list to the parent skip list and contains the provided data, but it has no specific insertion point into list (see insertafter!).
node.prev and node.next represent the previous and next nodes, respectively, of a list. node.up and node.down represent the nodes in adjacent levels within the skip list data structure.
See also SkipList, PairedSkipNode
PairedLinkedLists.TargetKind — Type
TargetKindTrait describing how a node or list participates in cross-list target links — the defining feature of this package. The kinds are Reciprocal (two-way links), OneWay (one-way links), and Untargeted (no links). TargetKind(x) reports the kind for a node or list x, and hastarget, addtarget!, and removetarget! dispatch on it.
A type opts into targeting by defining TargetKind(::Type{MyType}) to return Reciprocal() or OneWay() and providing a target field initialized to the object itself; pointing target at the object encodes the unlinked state.
See also target, hastarget, addtarget!, removetarget!.
PairedLinkedLists.TargetedLinkedList — Type
l = TargetedLinkedList{T,R}()
l = TargetedLinkedList{T,R}(elts...)
l = TargetedLinkedList(list)Create a TargetedLinkedList made up of TargetedListNodes containing data of a specified type. Each node can have an inter-list link to a node belonging to the list's target.
The list contains its length len, a "dummy" node head at the beginning of the list, and a "dummy" node tail at the end of the list. The list also contains a reference to its "target" list.
The first "real" node of a list l can be accessed with l.head.next or head(l). Similarly, the last "real" node can be accessed with l.tail.prev or tail(l).
See also TargetedListNode, DoublyLinkedList, PairedLinkedList
Examples
julia> base = DoublyLinkedList{Int}(1, 2, 3);
julia> tl = TargetedLinkedList(base)
TargetedLinkedList{Int64, DoublyLinkedList{Int64}, ListNode{Int64, DoublyLinkedList{Int64}}}()
julia> push!(tl, 10); push!(tl, 20); push!(tl, 30);
julia> collect(tl)
3-element Vector{Int64}:
10
20
30
julia> addtarget!(getnode(tl, 1), getnode(base, 2));
julia> getnode(tl, 1).target.data
2PairedLinkedLists.TargetedListNode — Type
node = TargetedListNode(list::AbstractTargetedLinkedList, data, [target::AbstractListNode])Create a TargetedListNode belonging to the specified list. The node contains a reference list to the parent list and contains the provided data, but it has no specific insertion point into list (see insertafter!).
node.prev and node.next represent the previous and next nodes, respectively, of a list.
A node's target should always either be a reference to itself (denoting an unpaired node) or a node belonging to the target of its parent list.
The target link is not assumed to be reciprocated for a TargetedListNode, as the targeted node may not even have a target field. For guaranteed two-way inter-list links, see PairedListNode.
See also TargetedLinkedList, PairedListNode, ListNode.
PairedLinkedLists.Untargeted — Type
Untargeted <: TargetKindNo target links: hastarget is always false and the targeting mutators leave the object unchanged.
Base.map — Method
map(f, l::DoublyLinkedList)Apply f to each element of l, returning a new DoublyLinkedList.
map is defined only for DoublyLinkedList. It is intentionally not provided for the other list types: an arbitrary f need not preserve a SkipList's sortedby ordering, and mapping a PairedLinkedList or TargetedLinkedList would leave the new list's nodes without the cross-list target links that define those types. Build such a list explicitly instead.
Base.popat! — Method
popat!(l::AbstractList, idx::Int) -> data
popat!(l::AbstractList, idx::Int, default) -> data_or_defaultRemove and return the element at index idx of l. Throws BoundsError if idx is out of range.
The two-argument form is the same as Base.popat!. The three-argument form returns default instead of throwing when idx is out of range; no element is removed in that case.
PairedLinkedLists.addtarget! — Method
addtarget!(node, target_node)
addtarget!(list, target_list)Link the provided node or list to target, assigning it as the object's target, and return the object.
For a Reciprocal object (such as a PairedListNode or PairedLinkedList) the reverse link is established as well, and any prior target of either object is removed first. For a OneWay object (such as a TargetedListNode or TargetedLinkedList) only the object's own link is set and target is left unchanged.
Examples
julia> l1 = PairedLinkedList{Int}(1, 2, 3);
julia> l2 = PairedLinkedList{Int}(10, 20, 30);
julia> addtarget!(l1, l2);
julia> hastarget(l1)
true
julia> l1.target === l2
true
julia> addtarget!(getnode(l1, 1), getnode(l2, 2));
julia> getnode(l1, 1).target.data
20
julia> getnode(l2, 2).target.data
1See also TargetKind, hastarget, removetarget!.
PairedLinkedLists.athead — Method
athead(node) -> BoolReturn true if the node is the "dummy" sentinel at the beginning of the list, and false otherwise.
PairedLinkedLists.attail — Method
attail(node) -> BoolReturn true if the node is the "dummy" sentinel at the end of the list, and false otherwise.
PairedLinkedLists.copyfromcache — Method
copyfromcache(c::SkipListCache)Returns a skip list created from a SkipListCache. This adds and removes entries as speficied by the cache.
PairedLinkedLists.copyfromcache — Method
copyfromcache(l::AbstractSkipLinkedList)Returns a copy of a skip list created from its cache. This adds and removes entries in the same order as had been previously done in original list.
PairedLinkedLists.deletenode! — Method
deletenode!(node::SkipNode)Remove node from the list to which it belongs, update the list's length, and return the node.
The node can be at any level of the skip list, and all nodes directly above or below will also be removed.
PairedLinkedLists.deletenode! — Method
deletenode!(node)Remove node from the list to which it belongs, update the list's length, and return the node.
For a PairedListNode or PairedSkipNode, the node's cross-list target link is also cleared before removal.
PairedLinkedLists.getnode — Method
node = getnode(l::AbstractList, index)Return the node at the specified index of the list.
PairedLinkedLists.hastarget — Method
hastarget(node) -> Bool
hastarget(list) -> BoolReturn true if the provided node or list currently links to a target, and false otherwise.
See also TargetKind, addtarget!, removetarget!.
PairedLinkedLists.head — Method
node = head(list)Return the first real node in list. This is list.head.next, not list.head itself, which is a dummy sentinel.
Throws ArgumentError if the list is empty.
PairedLinkedLists.insertafter! — Method
insertafter!(node, prev)Insert node into a list after the preceding node prev, update the list's length, and return the node.
node and prev must belong to the same list.
PairedLinkedLists.insertbefore! — Method
insertbefore!(node, next)Insert node into a list before the subsequent node next, update the list's length, and return the node.
node and next must belong to the same list.
PairedLinkedLists.isdisconnected — Method
isdisconnected(node) -> BoolReturn true if the node is not connected to any other node, and false otherwise.
PairedLinkedLists.listtype — Method
t = listtype(::AbstractNode)
t = listtype(::Type{<:AbstractNode})Return the type of list that can contain the provided type of node.
PairedLinkedLists.newnode — Method
node = newnode(list, data)Create a list node containing data of the type appropriate for list (e.g. a ListNode for a DoublyLinkedList, a PairedListNode for a PairedLinkedList, etc.).
The returned node is disconnected from list; use insertafter! or insertbefore! to place it.
PairedLinkedLists.nodetype — Method
t = nodetype(::AbstractList)
t = nodetype(::Type{<:AbstractList})Return the type of the nodes contained in the list.
PairedLinkedLists.removetarget! — Method
removetarget!(node) -> node
removetarget!(list) -> list
removetarget!(list, idx::Int) -> nodeRemove the target link of the node or list (or of the idx-th node of list) and return the object whose link was removed. An object without a target is returned unchanged.
For a Reciprocal object the reverse link is removed as well. Removing a list's target also removes the target link of each of its nodes.
removetarget!(list) removes both the list-level link and all node-level target links within that list.
See also TargetKind, hastarget, addtarget!.
PairedLinkedLists.skiplistsidentical — Method
skiplistsidentical(l1::AbstractSkipLinkedList, l2::AbstractSkipLinkedList)Returns true if the two skip lists are identical, false otherwise.
The lists are considered identical if the values of their nodes are the same at each "level" of the list. Because nodes are typically added at a random level, two skip lists constructed from the same data will generally not be identical unless generated using copyfromcache.
See also copyfromcache
PairedLinkedLists.tail — Method
node = tail(list)Return the last real node in list. This is list.tail.prev, not list.tail itself, which is a dummy sentinel.
Throws ArgumentError if the list is empty.
PairedLinkedLists.target — Method
target(node) -> node_or_nothing
target(list) -> list_or_nothingReturn the target of node or list, or nothing if no target is currently linked. For Reciprocal objects (PairedListNode, PairedSkipNode, PairedLinkedList, PairedSkipList) the link is bidirectional; for OneWay objects (TargetedListNode, TargetedLinkedList) it is one-directional. Untargeted objects always return nothing.
This is the public accessor for the cross-list link; callers should use it rather than reading the .target field directly, as the unlinked state is encoded by a self-reference which target hides.
See also TargetKind, hastarget, addtarget!, removetarget!.