datastructures
implements various datastructures frequently used in computer science. Among these are for example Fibonacci heaps, stacks, queues or hashmaps.
library(datastructures)
Fibonacci heaps are priority queue datastructures using the minimum heap property. Fibonacci heaps can be represented using collections of trees or linked lists consisting of nodes of elements. Every node is a pair of keys and values, where the key decides the ordering of the node. Fibonacci heaps have various applications, one of the most famous being in efficiently finding shortest paths using Dijkstra’s algorithm. Fibonacci heaps can add values in amortized O(1) time and remove the minimum value in O(log(n)) time makingthem a good choice in many real world scenarios.
You can create a heap like this:
fheap <- new("fibonacci_heap", "numeric", "character")
This gives us a heap with numeric
keys and character
values. You can pick from either integer
, numeric
or character
keys and values. So if we insert several key-value pairs, the pair with the minumum key would have the first priority. Let’s insert some values and have a look:
keys <- sample(seq(0, 1, by=.2))
values <- paste0("V", keys)
fheap <- insert(fheap, keys, values)
size(fheap)
## [1] 6
Since Fibonacci heaps use the minimum heap property the first element in the heap should be 0 -> V0
even though we inserted the pairs in a random fashion.
peek(fheap)
## $`0`
## [1] "V0"
size(fheap)
## [1] 6
That worked nicely. peek
gives us the first element from the heap without removing it. If we want to have it removed we call the pop
function. Of course this changes the priority of the heapL
pop(fheap)
## $`0`
## [1] "V0"
size(fheap)
## [1] 5
You can alternatively insert values like this:
fheap[-1] <- "V-1"
peek(fheap)
## $`-1`
## [1] "V-1"
Hashmaps also use hash functions to store key alue pairs, however without using any priority property. Given a key, the hash function computes a bucket where the value is stored making lookup, insertion and deletion possible in O(1) time. In the worst case these runtimes decrease to O(n) time, where \(n\) is the number of entries in a bucket.
A hashmap can get created like this, this time we use
hash <- new("hashmap", "character", "integer")
Insterion to hashs works just like before, by calling the insert
function or using a subset-operator:
keys <- paste0("V", 1:5)
values <- 1:5
hash <- insert(hash, keys[1:4], values[1:4])
hash[keys[5]] <- values[5]
size(hash)
## [1] 5
Values can then be accessed like this:
get(hash, keys[1])
## [1] 1
hash[keys[1]]
## [1] 1
Queues and stackes are two list datastructures using std::deque
in the backend, i.e. insertion at the end and getting the first element can be done in constant time O(1). Queues and stacks are for example used in depths-first-search and breadth-first-search making them a versatile datastructure. While queues use the first-in-first-out principle, stacks use the last-in-first-out principle.
The two datatypes use the exact same methods. You can instantiate a stack or a queue using:
qu <- new("queue", "numeric")
st <- new("stack", "numeric")
As before, you can pick either numeric
, integer
or character
keys. Now, let’s again insert some data
keys <- seq(0, 1, by=.2)
print(keys)
## [1] 0.0 0.2 0.4 0.6 0.8 1.0
insert(qu, keys)
## An object of class queue<numeric>
##
## First element -> 0
insert(st, keys)
## An object of class stack<numeric>
##
## First element -> 1
This is fairly similar. The difference between the two becomes obvious when we access the data, i.e either call peek
or pop
functions:
peek(qu)
## [1] 0
peek(st)
## [1] 1
In both cases we inserted a vector of values raning from 0
to 1
. Also, in both cases the first value that got inserted was 0
and the last value was 1
. If we now peek onto the datastructures the queue
returns the first element that got inserted, while the stack returns the last element that was inserted.
Both datastructures also offer the other functions from before, for example
size(qu)
## [1] 6
pop(qu)
## [1] 0
size(qu)
## [1] 5