Skip list
The skip list is a nifty tool to implement a time line, a Z-index for sprites or other tasks where a data structure with fast insert and delete operations is needed, rather than one with fast random access.
Warning, hardcore paragraph: an (indexable) skip list is an ordered, probabilistic data structure with O(log(n)) insertions, deletions and search, and O(n*log(n)) space usage. Their main advantage over binary trees is that they're very easy to implement ~= 150 LOC in Lua.
The only restriction is that, since it's an ordered data structure, its elements have to be comparable with < and <=.
Here's a Lua implementation based on this python version.
-- Redacted due to bugs! See https://pastebin.com/uJUchZCP
The API by example:
local skiplist = require"skiplist"
math.randomseed(os.time())
-- Creation:
insdel = skiplist.new(8) -- you must pass an estimation of the length of the list to get optimal performance.
-- insertions
s = "YeeHoyeE"
for i=1,#s do
insdel:insert(s:sub(i,i))
print(insdel)
end
print()
-- indexing
print(insdel[4])
print(insdel[8])
print(insdel[12]) -- out of bounds --> nil
print()
-- iterate over the list.
for k,v in insdel:ipairs() do
print(k,v)
end
print()
-- remove elements
print(insdel:delete"Z") --> not found
for i=1,#s do
insdel:delete(s:sub(i,i))
print(insdel)
end
-- alternate insertion syntax, pop() and first()
insdel:insert("e")
insdel[0]=("g")
print( insdel:first() ) -- returns the first element.
print( insdel:pop() ) -- returns the first element and removes it from the list
print( insdel:first() )
print( insdel.size )
print( insdel:pop() )
print( insdel:pop() ) -- attempt to pop an empty list --> nil + error message.
print()
-------------------------------
-- ASCII representation of the structure:
-- Uncomment the _End assignment at the penultimate line
-- of the previous code section for this to work.
-- This is required because the following code goes
-- under the hood to show the inner structure of the list.
-- This isn't needed for normal use of the library.
l = skiplist.new(9)
s = "SweetLOVE"
for i=1,#s do local e = s:sub(i,i) l:insert(e) end
for level = l.maxLevel, 1, -1 do
local line='Level '..level..": "
node = l.head
while node.value ~= _End do
line=line..node.value..node.width[level].." "
for i=1,node.width[level]-1 do line=line.." " end
node = node.next[level]
end
print(line.."NIL")
end
Output:
( Y )
( Y, e )
( Y, e, e )
( H, Y, e, e )
( H, Y, e, e, o )
( H, Y, e, e, o, y )
( H, Y, e, e, e, o, y )
( E, H, Y, e, e, e, o, y )
e
y
nil
1 E
2 H
3 Y
4 e
5 e
6 e
7 o
8 y
nil value not found: Z
( E, H, e, e, e, o, y )
( E, H, e, e, o, y )
( E, H, e, o, y )
( E, e, o, y )
( E, e, y )
( E, e )
( E )
( )
e
e
g
1
g
nil Trying to pop an empty list
Level 3: HEAD4 S6 NIL
Level 2: HEAD1 E3 S1 V2 e2 w1 NIL
Level 1: HEAD1 E1 L1 O1 S1 V1 e1 e1 t1 w1 NIL