PJUllrich

PJUllrich

Author of Building Table Views with Phoenix LiveView

Big-O Time Complexities of Elixir Data Structures

I am currently trying to compile a Big-O Time Complexity table for the Elixir data structures, but can’t find information for all complexities and am also not 100% sure that I’m getting them right. My goal is to have a comprehensive table like the Big-O Algorithm Complexity Table and maybe put it into a nice cheatsheet poster for future reference. I was able to find bits and pieces in the docs, Elixirforum posts and the Elixir mailing list, but I failed to find a comprehensive overview of all the complexities.

Would you mind helping me to complete and review the following table, please?

# Big-O Time Complexities for Elixir data structures

# Map [1]
Access:    O(log n)
Search:    O(log n)
Insertion: O(n) for < 32 elements, O(log n) for >= 32 elements [2]
Deletion:  O(n) for < 32 elements, O(log n) for >= 32 elements [2]
Caveats:
- 'With fewer than 32 elements, a Map is a list of tuples sorted by keys'
- 'With 32 and more elements, a Map is a Hash Array Mapped Trie (HAMT)'
- 'Maps are unordered, allow any key type, but disallow duplicate keys'

# List [3]
Access:    O(n)
Search:    O(n)
Insertion: O(1) for prepending, otherwise O(n)
Deletion:  O(1) if first element, otherwise O(n)
Caveats:
- 'Lists are not arrays as in other languages, but single-linked lists'

# Keyword [4]
Access:    O(n)
Search:    O(n)
Insertion: O(n)
Deletion:  O(n)

# Tuple [5]
Access:    O(1)
Search:    O(n)
Insertion: O(n)
Deletion:  O(n)
Caveats: 
- 'Tuples are better for reading, whereas Lists are better for traversals'
- 'Avoid using tuples as a collection'
  '(i.e. avoid Tuple.append/2, Tuple.insert_at/2, or Tuple.delete_at/2)'

# (erlang) array [6]
Access:    O(log n) [7]
Search:    O(log n)
Insertion: O(log n)
Deletion:  O(log n)
Caveats:
- 'An array is a trie of small tuples, with a smaller memory overhead to Lists'

# MapSet [8]
Same complexities as 'Map'

Discussion points

  • The time complexities are a mix of average, best, and worst complexities. Should we separate them into average, worst and best columns maybe?
  • Did I forget a data structure? I excluded the Arrays library, because it’s a library that uses different data structures under the hood.
  • Should we include ets as well? It has O(1) for access and insertion for set, bag, and duplicate_bag tables, but isn’t technically a data-structure :thinking:

References

  1. Map in Docs
  2. Map structure for < 32 and >= 32 elements
  3. List in Docs
  4. Tuple in Docs
  5. Keyword in Docs
  6. array in erlang Docs
  7. Complexities of erlang data structures
  8. MapSet in Docs

Other resources
Partial overview of complexities
Discussion of :array
Does Elixir have persistent data structures?
Way to get O(1) access/set
Sequences by @sasajuric

Most Liked

PJUllrich

PJUllrich

Author of Building Table Views with Phoenix LiveView

Thank you for the suggestion @hauleth. I moved the table to a GitHub Gist. The gist deviates only slightly from above table (I added a caveat to array as @sabiwara suggested). I’m happy that above table was quite comprehensive already. I hope it will serve as a good documentation for future reference :slight_smile:

GitHub gist: Big-O Time Complexities for Elixir Data Structures · GitHub

hauleth

hauleth

Maps:

Access is O(log n) amortised - always. If there is less than 32 keys, then these are stored as sorted tuples, which mean, than you can use binary search.

Insertion/deletion is O(n) or O(log n) (amortised) respectively for less than 32 and more than 32 keys.

List:

In the middle is still O(n)

Deletion O(1) if first element (just do tl(list) which is constant) O(n) otherwise

Keyword list:

The same as regular list (as these are the same things) unless you use Keyword module, which will do deduplication in most cases. Then indeed it will be O(n).

Array:

It is trie of tuples, so your Big-Os are wrong there.

sabiwara

sabiwara

Elixir Core Team

Oh sorry, I wasn’t really clear. Strictly speaking you can still use random access in a sparse index, but you would need to account for the “holes”, since the positions and size won’t be shifted. This will impact the rest of your algorithm if you cannot assume a dense array anymore.

In pseudo-code:

> dense_array = delete_at([1, 2, 3, 4], 2)
[1, 2, 4]
> sparse_array = reset_at([1, 2, 3, 4], 2)
[1, 2, :undefined, 4]
> dense_array[2]
4
> sparse_array[2]
:undefined
> sparse_array[3]
4

The sparse version is just really a replace_at, not an actual deletion, so it is a bit like comparing apples and oranges. But depending on your algorithm, it is probably the best option in practice when possible, and maybe convert it back to a dense array once you’re done deleting using sparse_to_list/1 (O(n)).

Where Next?

Popular in Questions Top

Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&amp;query=perfume&amp;page=2, I would like to get: ...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

Other popular topics Top

WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
Emily
I have VueJS GUIs with the project generated using Webpack. I have Elixir modules that will need to be used by the VueJS GUIs. I forese...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 43657 311
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New

We're in Beta

About us Mission Statement