Introduction to Data Structures and Algorithms With Python
Tags: datastructures, python, algorithms, machinelearning
Published: 2022-02-21 13:00:47

## What is Data Structures?

This are containers that organize and group data according to type. 
Data Structures allows you to organize your data in such a way that enables you to store collections of data, relate them and perform operations on them accordingly. 

What are Algorithms?
An algorithm is a set of instructions for solving a problem or accomplishing a task.

Types of Data Structures
**In-built** 
Python has implicit support for Data Structures which enable you to store and access data. These structures are called List, Dictionary, Tuple and Set.
**User-defined**
Python allows its users to create their own Data Structures enabling them to have full control over their functionality. The most prominent Data Structures are Stack, Queue, Tree, Linked List,Hashmap,Graph etc.

## Built-in Data Structures

1. List
Lists are used to store data of different data types in a sequential manner. There are addresses assigned to every element of the list, which is called an Index. The index value starts from 0 and goes on until the last element called the positive index. There is also negative indexing which starts from -1 enabling you to access elements from the last to first element.

**Creating a list**
To create a list, you use the square brackets and add elements into it accordingly.
If you dont pass any element in the list you get an empty list.

```
myList = [] # create empty list
print(myList)
myList = [1,3,"john"] #creating list with data
print(myList)
```
Output 
> []
> [1, 3, 'john']

Adding Elements.
Adding elements can be achieved by append(),insert(),extend() functions.

```
myList = [1,3,"john"]
print(myList)
myList.append(4) #add as a single element
print(myList)
myList.extend([10, 'more_example']) #add as different elements
print(myList)
myList.insert(3, 'insert_example') #add element i given position 
print(myList)
```
Ouput 
> [1, 3, 'john']
> [1, 3, 'john', 4]
> [1, 3, 'john', 4, 10, 'more_example']
> [1, 3, 'john', 'insert_example', 4, 10, 'more_example']

Deleting Elements

If you want to delete an items to the list you can user del keyword, pop() or remove() functions.

```
myList=[1, 3, 'insert_example', 4, 10, 'more_example']
del myList[2] #delete element at index 2
print(myList)
myList.remove('more_example') #remove element with value
print(myList)
a = myList.pop(1) #pop element from list
print('Popped Element: ', a, ' List remaining: ', myList)
myList.clear() #empty the list
print(myList)
```
Output
> [1, 3, 'john', 'insert_example', 4, 10, 'more_example']
> [1, 3, 4, 10, 'more_example']
> [1, 3, 4, 10]
> Popped Element:  3  List remaining:  [1, 4, 10]
> []

Accessing Elements
 To access the values you pass the index of the values.

```
myList = [1, 2, 3, 'john', 10, 15]
for element in myList: #access elements one by one
    print(element)
print(myList) #access all elements
print(myList[4]) #access index 4 element
print(myList[0:2]) #access elements from 0 to 1 and exclude 2
print(myList[::-1]) #access elements in reverse
```
 Ouput

> 1
> 2
> 3
> john
> 10
> 15
> [1, 2, 3, 'john', 10, 15]
> 10
> [1, 2]
> [15, 10, 'john', 3, 2, 1]

We other list functions which includes :-
The len() function returns to us the length of the list.
The index() function finds the index value of value passed where it has been encountered the first time.
The count() function finds the count of the value passed to it.
The sorted() and sort() functions do the same thing, that is to sort the values of the list. The sorted() has a return type whereas the sort() modifies the original list.

**Dictionary**
Dictionaries are used to store key-value pairs.

Creating a dictionary
Dictionaries can be created using the calledbrackets or using the dict() function.

```
myDict = {}  # empty dictionary
print(myDict)
myDict = {"key": "value", 1: "kenya", 2: "uganda"}  # with values
print(myDict)
```

output

> {}
> {'key': 'value', 1: 'kenya', 2: 'uganda'}

Changing and Adding key, value pairs
To change the values of the dictionary, you need to do that using the keys. So, you firstly access the key and then change the value accordingly. To add values, you simply just add another key-value pair as shown below.

```
myDict = {"key": "value", 1: "kenya", 2: "uganda"}  # with values
print(myDict)
myDict[1] = " Nigeria"  # access the key and give new  value
print(myDict)
myDict[3] = "Niger"  # adding a new key value pair
print(myDict)
```
output

> {'key': 'value', 1: 'kenya', 2: 'uganda'}
> {'key': 'value', 1: ' Nigeria', 2: 'uganda'}
> {'key': 'value', 1: ' Nigeria', 2: 'uganda', 3: 'Niger'}

Deleting key, value pairs

To delete the values, you use the pop() function which returns the value that has been deleted.
To retrieve the key-value pair, you use the popitem() function which returns a tuple of the key and value.
To clear the entire dictionary, you use the clear() function.

```
myDict = {'key': 'value', 1: ' Nigeria', 2: 'uganda', 3: 'Niger'}
x = myDict.pop(3)  # pop element
print(x)
print(myDict)
y = myDict.popitem()  # empty dictionary
print(y)
print(myDict)
myDict.clear()
print(myDict)
```
Output

> {'key': 'value', 1: ' Nigeria', 2: 'uganda', 3: 'Niger'}
> Niger
> {'key': 'value', 1: ' Nigeria', 2: 'uganda'}
> (2, 'uganda')
> {'key': 'value', 1: ' Nigeria'}
> {}

Accessing Elements
You can access elements using the keys only. You can use either the get() function or just pass the key values and you will be retrieving the values.

```
myDict = {'key': 'value', 1: ' Nigeria', 2: 'uganda', 3: 'Niger'}
print(myDict[1])  # access elements using keys
print(myDict.get(3))  # access using get()
```
output

> Nigeria
> Niger

More functions 
Other Functions
You have different functions which return  keys(), values(), items().

```
myDict = {'key': 'value', 1: ' Nigeria', 2: 'uganda', 3: 'Niger'}
print(myDict.keys())  # get keys
print(myDict.values())  # get values
print(myDict.items())  # get key-value pairs
```
output

> dict_keys(['key', 1, 2, 3])
dict_values(['value', ' Nigeria', 'uganda', 'Niger'])
dict_items([('key', 'value'), (1, ' Nigeria'), (2, 'uganda'), (3, 'Niger')])

**Tuple**
Tuples are the same as lists are with the exception that the data once entered into the tuple cannot be changed no matter what. The only exception is when the data inside the tuple is mutable, only then the tuple data can be changed

Creating a Tuple
You create a tuple using parenthesis or using the tuple() function.

```
myTuple = (1, 2, 3) #create tuple
print(myTuple) 
```
output

> (1, 2, 3)

Accessing Elements
Accessing elements is the same as it is for accessing values in lists.

```
myTuple = (1, 2, 3,5)
for x in myTuple:
    print(x)
print(myTuple)
print(myTuple[0])
print(myTuple[:])
```
output

> 1
> 2
> 3
> 5
> (1, 2, 3, 5)
> 1
> (1, 2, 3, 5)

Appending Elements
To append the values, you use the ‘+’ operator which will take another tuple to be appended to it.

```
myTuple = (1, 2, 3,5)
myTuple=myTuple + (101,102,104,"now")
print(myTuple)
```
Output

> (1, 2, 3, 5, 101, 102, 104, 'now')

**Sets**
Sets are a collection of unordered elements that are unique. Meaning that even if the data is repeated more than one time, it would be entered into the set only once.

Creating a set
Sets are created using the Calledbrackets but instead of adding key-value pairs, you just pass values to it.

```
mySet={1,4,5,7,8,8,9,1,1}
print(mySet)
```
output
> {1, 4, 5, 7, 8, 9}

Adding elements
To add elements, you use the add() function and pass the value to it.

```
mySet={1,4,5,7,8,8,9,1,1}
mySet.add("string ")
print(mySet)
```
output
> {1, 4, 5, 7, 8, 9, 'string '}

Operations in sets
The different operations on set such as union, intersection and so on are shown below.

```
mySet = {1, 2, 3, 4}
mySet_2 = {3, 4, 5, 6}
print(mySet.union(mySet_2), '==', mySet | mySet_2) 
# union() function combines the data present in both sets.
print(mySet.intersection(mySet_2), '==', mySet & mySet_2) 
# intersection() function finds the data present in both sets only
print(mySet.difference(mySet_2), '==', mySet - mySet_2)  
# difference() function deletes the data present in both and outputs data present only in the set passed.
print(mySet.symmetric_difference(mySet_2), '==', mySet ^ mySet_2) # symmetric_difference() does the same as the difference() function but outputs the data which is remaining in both sets.
mySet.clear()
print(mySet)
```
output

> {1, 2, 3, 4, 5, 6} == {1, 2, 3, 4, 5, 6}
> {3, 4} == {3, 4}
> {1, 2} == {1, 2}
> {1, 2, 5, 6} == {1, 2, 5, 6}
> set()

## User-Defined Data Structures

**Queue**
A queue is a linear data structure which is based on the principle of First-In-First-Out (FIFO) where the data entered first will be accessed first. It is built using the array structure and has operations which can be performed from both ends of the Queue, that is, head-tail or front-back. Operations such as adding and deleting elements are called En-Queue and De-Queue and accessing the elements can be performed. Queues are used as Network Buffers for traffic congestion management, used in Operating Systems for Job Scheduling and many more.

**Stack**
Stacks are linear Data Structures which are based on the principle of Last-In-First-Out (LIFO) where data which is entered last will be the first to get accessed. It is built using the array structure and has operations namely, pushing (adding) elements, popping (deleting) elements and accessing elements only from one point in the stack called as the TOP. This TOP is the pointer to the current position of the stack. Stacks are prominently used in applications such as Recursive Programming, reversing words, undo mechanisms in word editors and more other applications.

**Linked List**
Linked lists are linear Data Structures which are not stored consequently but are linked with each other using pointers. The node of a linked list is composed of data and a pointer called next. These structures are most widely used in image viewing applications, music player applications.

**Graph**
Graphs are used to store data collection of points called vertices (nodes) and edges (edges). Graphs can be called as the most accurate representation of a real-world map. They are used to find the various cost-to-distance between the various data points called as the nodes and hence find the least path. Many applications such as Google Maps, Uber, and many more use Graphs to find the least distance and increase profits in the best ways.