Interpreted

Lua

LuaLateX and NeoVim use Lua. So do thousands of embedded systems. It is a language worth learning for sure. Here is a beautiful N-Queens solution:

N = 25 -- board size

-- check whether position (n, c) is free from attacks
function isplaceok (a, n, c)
    for i = 1, n - 1 do
        if (a[i] == c) or                    -- cumulative column
                (a[i] - i == c - n) or       -- ↘ diagonal (cumulative)
                (a[i] + i == c + n) then     -- ↙ diagonal (cumulative)
            return false
        end
    end
    return true
end

-- print a board
function printsolution (a)
    for i = 1, N do         -- for each row
        for j = 1, N do     -- and for each column
            -- write "X" or "-" plus a space
            io.write(a[i] == j and "X" or "-", " ")
        end
        io.write("\n")
    end
    io.write("\n")
end

-- add to board 'a' all queens from 'n' to 'N'
function addqueen (a, n)
    if n > N then           -- all queens have been placed?
        printsolution(a)
        return true
    else    -- try to place n-th quen
        for c = 1, N do
            if isplaceok(a, n, c) then
                a[n] = c    -- place n-th queen at column 'c'
                if addqueen(a, n + 1) then
                    return true
                end
            end
        end
    end
end

addqueen({},1)

--[[
this code is really quite confusing.

firstly it uses backtracking, secondly the table data structure is non-trivial.
the notation a[n] == c, checks if there is a queen at co-ordinate (n,c).

it works by not using subarrays, and just indexing the column of each queen!

the recursion is fine, the base case exists at the start of the addqueen function, and we
loop through all the rows, adding queens where you can, and then backtracking through the
tree when we've ended up in a non-solvable situation.

the confusion for me arose in the isplaceok function, where I thought we would loop over
the entire NxN board; how naive! instead if we are considering the nth queen, we have only
placed 1..n-1 queens, and only need to check those squares!

finally, the crux of the magic is in

        if (a[i] == c) or                    -- cumulative column
                (a[i] - 1 == c - n) or       -- ↘ diagonal (cumulative)
                (a[i] + 1 == c + n) then     -- ↙ diagonal (cumulative)

where it took me some time to understand that the checks were cumulative and that indeed
the indexing wizardry checked the cumulative column / diagonal / anti-diagonal.

note that there are 92 solutions to the 8x8 problem.

furthermore, the alternatives are the brute-force approach, where 8^8 boards are spawned and we need to check all column / diagonal and anti-diagonal violations.

there is also the permutation approach where you can just eliminate the row and col the queen was placed on and then continue your permutations for the n-1 sized square board. this avoids some cases by construction.

the true brute-force approach is O(N^N), whilst permutation is O(N!) and lastly backtracking is O(N!) worst case (but this is much better in practice).
--]]

Python

Data Types

categorytypes
textstr
numericint, float, complex
sequencelist, tuple, range
mappingdict
setset, frozenset
booleanbool
binarybytes, bytearray, memoryview
noneNoneType

Time Complexities 1

lists

operationtime complexity
sequence.append(item)\(\mathcal{O}(1)\)
sequence.pop(item)\(\mathcal{O}(1)\)
sequence.insert(0,item)\(\mathcal{O}(n)\)
sequence.pop(item,0) {{< mnote “slow because popping at start requires shifting all indices down” >}}\(\mathcal{O}(n)\)
sequence[index]\(\mathcal{O}(1)\)
sequence[index]=value\(\mathcal{O}(1)\)
item in sequence\(\mathcal{O}(n)\)
len(sequence)\(\mathcal{O}(1)\)
sequence.extend(iterable)\(\mathcal{O}(k)\)
sequence + other_sequence\(\mathcal{O}(n+k)\)
sequence[index:index+k]\(\mathcal{O}(k)\)
sequence.index(item)\(\mathcal{O}(n)\)
sequence.count(item)\(\mathcal{O}(n)\)
for item in sequence:\(\mathcal{O}(n)\)

double-ended queue (deque)

operationdescriptiontime complexity
queue.append(item)Add item to right end\(\mathcal{O}(1)\)
queue.appendleft(item)Add item to left end\(\mathcal{O}(1)\)
queue.pop()Remove and return item from right end\(\mathcal{O}(1)\)
queue.popleft()Remove and return item from left end\(\mathcal{O}(1)\)
queue.extend(iterable)Add all elements from iterable to right end\(\mathcal{O}(k)\)
queue.extendleft(iterable)Add all elements from iterable to left (reversed)\(\mathcal{O}(k)\)
queue.remove(value)Remove first occurrence of value\(\mathcal{O}(n)\)
queue.rotate(n)Rotate n steps right (negative for left)\(\mathcal{O}(k)\)
queue.clear()Remove all elements\(\mathcal{O}(n)\)
queue.count(value)Count occurrences of value\(\mathcal{O}(n)\)
queue.index(value)Find index of first occurrence of value\(\mathcal{O}(n)\)
queue.reverse()Reverse elements in place\(\mathcal{O}(n)\)
item in queueCheck if item exists in queue\(\mathcal{O}(n)\)
queue[0] or queue[-1]Access first or last element\(\mathcal{O}(1)\)
queue[i]Access element at index i\(\mathcal{O}(n)\)
for item in queueIterate through all elements\(\mathcal{O}(n)\)

dictionary

operationtime complexity
mapping[key]=value\(\mathcal{O}(1)\)
mapping[key]\(\mathcal{O}(1)\)
mapping.get(key)\(\mathcal{O}(1)\)
mapping.pop(key)\(\mathcal{O}(1)\)
key in mapping\(\mathcal{O}(1)\)
for k, v in mapping.items()\(\mathcal{O}(n)\)
next(iter(mapping))\(\mathcal{O}(1)\)
next(reversed(mapping))\(\mathcal{O}(1)\)
value in mapping.values()\(\mathcal{O}(n)\)
mapping.update(iterable)\(\mathcal{O}(k)\)

set

operationtime complexity
my_set.add(item)\(\mathcal{O}(1)\)
my_set.remove(item)\(\mathcal{O}(1)\)
item in my_set\(\mathcal{O}(1)\)
for item in my_set\(\mathcal{O}(n)\)
set1 & set2\(\mathcal{O}(n)\)
set1 | set2\(\mathcal{O}(n)\)
set1 ^ set2\(\mathcal{O}(n)\)
set1 - set2\(\mathcal{O}(n)\)

counter

operationtime complexity
counter[item]\(\mathcal{O}(1)\)
counter.pop(item)\(\mathcal{O}(1)\)
for k, v in counter.items():\(\mathcal{O}(n)\)
for k, v in counter.most_common():\(\mathcal{O}(n\log(n))\)
for k, v in counter.most_common(k):\(\mathcal{O}(n \log(k))\)
counter.update(iterable)\(\mathcal{O}(k)\)
counter.subtract(iterable)\(\mathcal{O}(k)\)
counter.total()\(\mathcal{O}(n)\)

heap / priority queue

operationtime complexity
heapq.heapify(sequence)\(\mathcal{O}(n)\)
heapq.heappop(sequence)\(\mathcal{O}(\log(n))\)
heapq.heappush(sequence, item)\(\mathcal{O}(\log(n))\)
sequence[0]\(\mathcal{O}(1)\)

sorted list

operationtime complexity
sorted_sequence = sorted(sequence)\(\mathcal{O}(n\log(n))\)
sorted_sequence.index(item)\(\mathcal{O}(n)\)
bisect.bisect(sorted_sequence, item)\(\mathcal{O}(\log(n))\)
bisect.insort(sorted_sequence, item)\(\mathcal{O}(n)\)

general traversals:

operationtime complexity
min(iterable)\(\mathcal{O}(n)\)
max(iterable)\(\mathcal{O}(n)\)
sorted(iterable)\(\mathcal{O}(n\log(n))\)
heapq.nsmallest(k, iterable)\(\mathcal{O}(n\log(k))\)
statistics.multimode(iterable)\(\mathcal{O}(n)\)