Introduction to Data Structures and Algorithms With Python
Tags: python, 100daysofcode, algorithms, datascience
Published: 2022-02-21 07:12:45

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lv7qu1jxc7weygwqqe1f.jpg)
**Data Structures** are a collection of data values, the relationships among them, and the functions or operations that can be applied to the data.<br>
 Data Structures allow 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.<br>
**Data algorithms** are sets of well-defined instructions to solve a particular problem. It takes a set of input and produces a desired output. 

## **Types of data structures**
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i2q33q0e052aw9mcs84y.png)

 

###**1. Lists**<br>
This is an **ordered** and **changeable** collection of item that also **allows duplicate** items. It can contain different data types.
Lists are created through **square brackets[]** or **list()** constructor.
For example:
```python
my_list=["melon","apple","mango"]
```
**List Methods**
Python has a set of built-in methods that you can use on lists.
```python
#access a list item
print(my_list[2])

#append()  Adds an element at the end of the list
my_list.append("orange")

#clear()  Removes all the elements from the list
my_list.clear()

#copy()	 Returns a copy of the list
my_list.copy()

#extend()Adds list items to the end of the current list 
           
my_list.extend(another_list)

#insert()  Adds an element at the specified position\index
my_list.insert(3,"guava")

#pop()	Removes the element at the specified position\index
my_list.pop(1)

#remove()  Removes the item with the specified value
my_list.remove("apple")

#reverse()  Reverses the order of the list
my_list.reverse()

#sort()	 Sorts the list alphanumerically, ascending, by default
my_list.sort()
```

###**2. Tuples**
These are **unchangeable**,**ordered** data structures that allows duplicates.
They are created through **()** or **tuple()** constructor.
```python
#create a tuple
number=(45,34,678)

#access a tuple item
number[0]

#delete a tuple
del number
```
Once you have created a tuple you can not change it, that is you can not add, edit or remove any of the items in it. Not unless you change it to a mutable data structure like lists then back to a tuple.

```python
y=list(number)
#add the list method you want performed e.g sort()
y.sort()
number=tuple(y)
```

 
###**3. Dictionary**
Dictionaries are used to store data values in **key:value pairs**.
They are changeable and do not allow duplicate keys and are written with curly brackets **{}**
```python
dog = {'name':'Cara',
'color':'Brown',
'gender':'Female'}
```
**Dictionary methods**
```python
#keys()  prints out all the keys
for i in dog.keys():
   print(i)

#values()  prints out all the values
for i in dog.values():
   print(i)

#items() prints out the key:value pair
for i in dog.items():
   print(i)
```
Output
```python
name
color
gender

Cara
Brown
Female

('name', 'Cara')
('color', 'Brown')
('gender', 'Female')
```
It’s tedious to check whether a key exists in a dictionary before accessing that key’s value. Fortunately, dictionaries have a get() method that takes two arguments: the key of the value to retrieve and a fallback value to return if that key does not exist.
```python
print(dog.get('sound',0))
```
Output
```python
0
```

###**4. Sets**
Unlike dictionaries,sets are **unchangeable** and also **do not support duplicates.**
```python
myset = {"apple", "banana", "cherry"}
``` 

###**5. Stacks**
A **stack** is a linear data structure that follows the principle of Last In First Out (LIFO). This means the last element inserted inside the stack is removed first.
In programming terms, putting an item on top of the stack is called **push** and removing an item is called **pop**.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5k7zfnu0mhx1zihfltcc.png)
```python
# Creating a stack
def create_stack():
    stack = []
    return stack


# Creating an empty stack
def check_empty(stack):
    return len(stack) == 0


# Adding items into the stack
def push(stack, item):
    stack.append(item)
    print("pushed item: " + item)


# Removing an element from the stack
def pop(stack):
    if (check_empty(stack)):
        return "stack is empty"

    return stack.pop()


stack = create_stack()
push(stack, str(1))
push(stack, str(2))
push(stack, str(3))
push(stack, str(4))
print("popped item: " + pop(stack))
print("stack after popping an element: " + str(stack))

```
Output
```python
pushed item: 1
pushed item: 2
pushed item: 3
pushed item: 4
popped item: 4
stack after popping an element: ['1', '2', '3']
```

###**6. Queues**
A **queue** is a linear type of data structure used to store the data in a sequentially and it  follows the First In First Out (FIFO) rule - the item that goes in first is the item that comes out first.
**Enqueue** is adding an element to the end of the queue while
**Dequeue** is removing an element from the front of the queue.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7vvox5yx3zazur7i4g78.png)
```python
# Queue implementation in Python

class Queue:

    def __init__(self):
        self.queue = []

    # Add an element
    def enqueue(self, item):
        self.queue.append(item)

    # Remove an element
    def dequeue(self):
        if len(self.queue) < 1:
            return None
        return self.queue.pop(0)

    # Display  the queue
    def display(self):
        print(self.queue)

    def size(self):
        return len(self.queue)


q = Queue()
q.enqueue(1)
q.enqueue(2)
q.enqueue(3)
q.enqueue(4)
q.enqueue(5)

q.display()

q.dequeue()

print("After removing an element")
q.display()
```
Output
```python
[1, 2, 3, 4, 5]
After removing an element
[2, 3, 4, 5]
```

###**7. Linked lists**
A linked list is a linear data structure that includes a series of connected nodes. Each node stores the **data** and the **address of the next node.**
The first node is called the **head**, and it’s used as the starting point for any iteration through the list. The last node must have its _next_ reference pointing to **None** to determine the end of the list. Here’s how it looks:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dln3dsnoj0qbto7gssje.png)
```PYTHON
# Linked list implementation in Python


class Node:
    # Creating a node
    def __init__(self, item):
        self.item = item
        self.next = None


class LinkedList:

    def __init__(self):
        self.head = None


if __name__ == '__main__':

    linked_list = LinkedList()

    # Assign item values
    linked_list.head = Node(1)
    second = Node(2)
    third = Node(3)

    # Connect nodes
    linked_list.head.next = second
    second.next = third

    # Print the linked list item
    while linked_list.head != None:
        print(linked_list.head.item, end=" ")
        linked_list.head = linked_list.head.next
```
Output
```python
1 2 3 
```
So what makes them different from normal lists? Linked lists differ from lists in the way that they store elements in memory. While lists use a contiguous memory block to store references to their data, linked lists store references as part of their own elements.<br>
_The above concepts can be overwhelming ,so just take your time.
Happy coding!!_