Recap what algorithm does: Manipulating memory with operation.
Memory is a big array.
Data structures: How to organize data such that operations can be done efficiently.
Array
The basic data structure.
- Read an element on some position \(i\).
x=v[i]\(O(1)\). - Write an element on some position \(i\).
v[i]=x\(O(1)\). - Create a new array inside and initialize to \(0\).
v=new arr[n]\(O(n)\). - Get the length of the array.
v.length()\(O(1)\).
There are some important operation that are not just read/write/create.
For example, we may want to know is element \(x\) in array \(v\)? v.contains(x) should return true of false.
Trivial solution: Scan through the array, which takes \(O(n)\) time.
Vector
As known as resizable array. Don't need to specify the size when creating.
Read an element: \(O(1)\)
Write an element: \(O(1)\).
Create a vector: \(O(1)\).
Insert an element to the end: amortized \(O(1)\).
- When the length is less than \(2^i\), just normal write, taking \(O(1)\).
- When the length of vector is exceed \(2^i\), we need to allocated more \(2^i\) space, taking \(O(n)\) time (Here \(n\approx 2^i\)).
Check whether an element \(x\) is in vector \(v\): \(O(n)\). Not good though.
Dictionary
It is not a concrete data structure, rather an abstract data type. In practice, it needs more specification and implementation.
Create a new dictionary d=new dict()
Insert a key-value pair d.insert(key, val)
Find a value with a key d.find(key) returns the value or nothing.
Delete a key-value pair d.delete(key)
Get all elements d.as_vec()
Some Implementations
| find, insert, delete operations | |
|---|---|
| Hash table | \(O(1)\) in expectation |
| Red-black tree | \(O(\log n)\) |
| B-tree | \(O(\log n)\) |
| AVL tree | \(O(log n)\) |
Linked List
There are many variants. We use it less nowadays.
For each cell, we have a value, a pointer from the last cell and a pointer to the next cell.
\(O(1)\): Create a new one, insert front, insert back, remove front, remove back, remove given a concrete address.