Data Structures


A Data Structure as the name suggests is the way Data or Information is structured or organized in computer systems. Data Structures are the programmatic way of storing and organizing data in a computer.

You can imagine it like a real world book storage. You can organize your books in alphabetical order of their title or categorize them according their genre. You can put all the books together or seprate them into compartments for easier access.


Below I will describe some of the most commonly used Data Structures

Arrays

An Array is a data structure which is used to store data in adjacent memory location.  The data stored in the array is called the element of an array and the location of the array is accessed using index. The size of the array is fixed.

So going with a book storage analogy we can think of it as books stored in alphabetical order of their titles. In order to get to a book starting with "D", you will have to sequentially go past books with titles starting with "A", "B" and "C"


Big O - Time complexity of searching an item in an array.
Lets take an example we have a array of string below:

let stringArray = ['Apple', 'Orange', 'Banana', 'Strawberry'];

Now if I need to find the index of 'Apple' in this stringArray my code will look something like this:

for(let i = 0; i < stringArray.length; i++){
    if(stringArray[i] === 'Apple'){
        return i;
    }
}

Here since 'Apple' is at the first index the code executed in O(1) time. However for Big O notation we consider the worst time. So in case we need to find 'Strawberry' then our code will look something like this:

for(let i = 0; i < stringArray.length; i++){
    if(stringArray[i] === 'Strawberry'){
        return i;
    }
}

Since 'Strawberry' is at the last index the code executed in O(n) time, assuming that n = stringArray.length. So the time complexity of searching an item in an Array is O(n).

Linked List

A linked list is a data structure where data is arranged as nodes, each node contains two parts the actual data and the address to the next node. The data stored in a linked list is accessed by traversing through the list from the first node till we reach the node with required information. The size of the linked list can shrink and grow on demand.

Comparing it with our real world book storage example you can think of link list as muliple book bins. Each bin has books of the same starting alphabet, plus it has a note which tells you where the next alphabet bin is located. So "A" bin will have all the books with title starting with A and it will have note which will tell you where "B" bin is located in the house.


Big O - Time complexity of searching an item in a linked list.
Lets take an example we have a linked list of string below:

'Apple', 'Orange', 'Banana', 'Strawberry'

class LinkedListNode(val) {
    
            this.data = data;
            this.next = null;
        }
 }
const head = new LinkedListNode('Apple') ;
head.next = new LinkedListNode('Orange');
head.next.next = new LinkedListNode('Banana');
head.next.next.next = new LinkedListNode('Strawberry');

Now if I need to find 'Apple' in this linked list

let current = head;
while(current !== null){
    if(current.data === 'Apple')
        return current;
    current = current.next();
}

Here since 'Apple' is at the first node the code executed in O(1) time. However for Big O notation we consider the worst time. So in case we need to find 'Strawberry' then our code will look something like this:

let current = head;
while(current !== null){
    if(current.data === 'Strawberry')
        return current;
    current = current.next();
}
Since 'Strawberry' is at the last node the code executed in O(n) time, assuming that n = number of nodes. So the time complexity of searching an item in a Linked List is O(n).

Map

A Map is a data structure which stores data as key - value pairs. The value can be directly accessed using the key. 

map country-code to country-name

Big O - Time complexity of searching an item in a Map.
Lets take an example we have a Map below:

let countryMap = new Map();
countryMap['us'] = 'United States';
countryMap['es'] = 'Spain';
countryMap['zw'] = 'Zimbabwe';

So if I need to access 'United States' from the Map all I need to do is:

return countryMap['us'];

Similarly for 'Zimbabwe'.

return countryMap['zw'];

Here we see that no matter what the size of the Map is the time complexity of Map does not change so it is O(1) (constant complexity).

Graph

A Graph is a Data Structure which contains a set on nodes (vertices) and set of edges connecting the nodes. Data stored in a graph can be accessed by traversing through the edges of the nodes till you reach the required information. 

Graphs are most commonly represented in a program as Adjacency Matrix or Adjacency List.

Adjacency Matrix - is represented as a two dimensional array of size VxV where V is the total number of vertices. So for the above graph the can be represented as follows:
Assuming that the above array is called adjacencyMatrix then adjacencyMatrix[0][1] = 1 means there is a edge between vertex 0 and vertex 1. Similarly adjacencyMatrix[0][2] = 0 means there is no edge between vertex 0 and 2. 

Big O - Time complexity of searching an item in a Graph represented by Adjacency Matrix.
 
So the above graph can be represented as below and we have to search any item in this two dimensional array.
const adjacencyMatrix  = [[0,1,0,0,1],
                                            [1,0,1,1,1],
                                            [0,1,0,1,0],
                                            [0,1,1,0,1],
                                            [1,1,0,1,0]];
let rows                         = 5;
let cols                          = 5;

for(let i =0; i<5;i++){
    for(let j = 0; j<5;j++){
         console.log(a[i][j]);  
    }

We see that to search an item we need 2 nested for loops each size 5 which is the total number of vertices in the graph. Assuming that n is the number of vertices the time complexity of searching an item in adjacency matrix representation of a graph is O(n2).

Adjacency List -  is an array of lists where an entry a[i] represents the list of vertices which the given vertex is connected by an edge. Lets see how the graph above can be represented as a Adjacency List.

Big O - Time complexity of searching an item in a Graph represented by Adjacency List. So the above graph can be represented as below:

const adjacencyList = new Map();
adjacencyList.set(0, new Set([1,4]));
adjacencyList.set(1, new Set([0,4,2,3]));
adjacencyList.set(2, new Set([1,3]));
adjacencyList.set(3, new Set([1,4,2]));
adjacencyList.set(4, new Set([3,0,1]));

The time complexity of accessing items in a map is O(1) so to search an item in the Adjacency List with Map implementation is O(1).

Tree

A Tree is a special type of Graph Data Structure where all the nodes originate either directly or indirectly from exactly one node, called the root node.
General Tree Structure


We can represent the above Tree using the Adjacency List like below:
 
const adjacencyList = new Map();
adjacencyList.set("A", new Set(["B","C","D"]));
adjacencyList.set("B", new Set(["E","F"]));
adjacencyList.set("C", new Set(["G"]));
adjacencyList.set("D", new Set(["H","I"]));
adjacencyList.set("G", new Set(["J","K"]));

The time complexity of accessing items in a map is O(1) so to search an item in the Adjacency List with Map implementation is O(1).

Useful links: