Introduction to Data Structures and Algorithms with Python.

Introduction to Data Structures and Algorithms with Python.

In this article, we are going to have an indepth look into the different data structures available in python. There are two type of data structures in python. These are Built-in data structures and user defined data structures

In computer science, a data structure is a data organization, management, and storage format that enables efficient access and modification of the data.

#Built-in data structures These are the data structures that are built in with the programming language. Let's go through these first:

Lists

A list is a data structure that is ordered where its elements are separated using a comma and they are enclosed using parentheses. Python lists are used to store data in array format. Different data types can be stored in the same list.

my_list = ['name', 210, 'age', 33.5]

You can call a list's contents using their indices my_list[0] will give an output of the string 'name' at index 0. You can use the slicing operation [:] to get an item or a range of items. Example:

name = [120, 'savor', 32.4, 'title']
# if we want to access items from index 1 to the end of the list 
print(name[1:]

this will give an out put of a list; ['savor',32.4, 'title'] if we want to access all items in the list non-inclusive of the last item, we add a negative sign to the slice.

print(name[:-1]

this will give an out put of a list; [120, 'savor', 32.4]

You can also use a loop to interate through the list.

# using the list `name` created above
for i in range(len(name)):
    print(name[i])

This loop prints each item in the list name. The loop iterates through the list and prints out each item until there is nothing else in the list.

Tuples

Tuples store data much like lists only that values in a tuple are unchangeable. A tuple is ordered and enclosed in round brackets. They are mostly used to store sensitive data that should not be reassigned later in the program such as user information.

my_tuple = ("Ken", 23)
print(my_tuple)

Items in the tuple can be accessed much like in a list using indices. They can store very many items. You can create a tuple without parentheses by simply declaring the tupple

my_tuple = "Finer", 1934, "Things"


# Declaring a tuple with a single item since the value is read as a string.


second_tuple = ("James")

print(type(second_tuple)) # type: <str>

# to declare a single variable tupple
# you should add a comma right after the first value

third_tuple = ("Jen",)

For tuples, it is also possible to unpack the items onto variables for use

animals = "cow", "cat", "pigeon"
print(animals)
a, b, c = animals
print(a)
print(b)
print(c)

Output

( "cow", "cat", "pigeon")
cow
cat
pigeon
# Changing tuple values
my_tuple = (4, 2, 3, [6, 5])


# TypeError: 'tuple' object does not support item assignment
# my_tuple[1] = 18

# However, item of mutable element can be changed
my_tuple[3][0] = 18    # Output: (4, 2, 3, [18, 5])
print(my_tuple)

Output

(4, 2, 3, [18, 5])

Sets

They are used to store multiple items in a single variable. Sets contain unique items and there cannot be any duplicates or else they will be eliminated. Set contents are unchangeable but you can remove or add items to the set. They are enclosed by curly braces.

my_set = {"apple", "banana", "cherry"}
print(my_set)

Dictionaries

Dictionaries store data in key:value pairs. They are enclosed in curly braces. They do not allow duplicates since a value can only be accessed using a single, distinct key. dict is a keyword that refers to a dictionary.

my_dict = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
print(my_dict)
print(my_dict.keys())
print(my_dict.values())
print(my_dict.items())
# try these out

User Defined Data Structures

As the name implies, user-defined data structures, enable users to declare how the Data Structure works as well as what operations it can perform. The user has absolute control over how the data is saved, manipulated, and so on.

Stack

They are linear data structures based on the principle of Last-In-First-Out(LIFO) where the last entered data will be the first to be accessed. These data structures only have one point in which elements can be accessed, this point is known as the TOP. It is generated using the array structure and includes operations such as, pushing(adding); This operation adds data at the TOP of the stack and popping(deleting) ; This operation deletes the last data on the TOP of the stack.

Stacks are commonly used in Undo mechanism in word editors, Recursive Programming and reversing words

1*tQ9Y11OdaMnhXwbfCF-edA.gif

Queue

A queue is a linear data structure that works on the First-In-First-Out (FIFO) principle, which means that the data that is entered first is accessed first. Operations on this data structure, which is built using the array structure, can be performed from both ends of the Queue. Stacks and queues differ in how data is removed. In a stack, the item that was most recently added is removed; in a queue, the item that was added first is removed.

Queues are prominently used as Network Buffers for traffic congestion management and for Job Scheduling in Operating systems.

qintro.png

Linked List

Linked lists are linear Data Structures with the collection of multiple nodes which aren't stored. Each element holds its own data as well as a pointer to the subsequent element's position. A node is an element of a linked list. The first node is called the head while the last one is called the tail. In a linked list, the last link points to null, marking the chain's end.

Linked Lists are mostly used in music player applications, image viewing applications and so on.

Implementing a linked list

To create a linked list, we create a node object and another class to use this node object.

class Node(object):

# Constructor to initialize class variables

    def __init__(self, data=None, next_node=None):
        self.data = data
        self.next_node = next_node

    #get data

    def get_data(self):
        return self.data

    # get next value

    def get_next(self):
        return self.next_node

    # set next data

    def set_next(self, new_next):
        self.next_node = new_next

It's implementation has the following functionality

  1. Insert- Inserts a new node in a linked list
  2. Size- Returns the size of the linked list
  3. Search - Returns a node containing the data else it will raise an error
  4. Delete- This method will destroy a node holding the data; otherwise, an error will be raised.

The __init__ method is used to initialise a class variable if the list has no nodes:

class LinkedList(object):

    def __init__(self, head=None):
        self.head = head

Insert

This insert method takes given data, creates a new node with it, and adds it to the list. You may theoretically add a node anywhere in the list, but the simplest way is to put it at the top and point the new node at the old one (sort of pushing the other nodes down the line).

def insert(self, data):
    new_node = Node(data)
    new_node.set_next(self.head)
    self.head = new_node

linked-list-programming-interview-questions-1.gif

Size

This method counts nodes until it can't find anymore and returns how many nodes it found.

# Returns total numbers of node in list

def size(self):
    current = self.head
    count = 0
    while current:
    count += 1
    current = current.get_next()
    return count

Search is similar to size in that it checks at each stop to determine if the current node holds the desired data rather than traversing the entire list of nodes. If this is the case, the node that holds the data is returned. If the method searches the entire list and still doesn't find the data, it returns a value error and informs the user that the data isn't in the list.

# Returns the node in list having nodeData, error occured if node not present

def search(self, nodeData):
    current = self.head
    isPresent = False
    while current and isPresent is False:
        if current.get_data() == nodeData:
            isPresent = True
        else:
            current = current.get_next()
            if current is None:
                raise ValueError("Data not present in list")
                return current

Delete

The delete function explores the list similarly to the search method, except in addition to remembering the current node, it also remembers the last node visited.

When the delete method reaches the node it intended to delete, it checks the pointer of the previous node (the 'prior' node) and resets it. Rather than pointing to the node that will be removed soon.

It will direct you to the next node in the chain. The unlucky node that is being erased is essentially gone from the list because no nodes point to it!

singly-linked-list-delete-at-random.gif

# Remove the node from linked list returns error if node not present

def delete(self, nodeData):
    current = self.head
    previous = None
    isPresent = False
    while current and isPresent is False:
        if current.get_data() == nodeData:
            isPresent = True
        else:
            previous = current
   current = current.get_next()
if current is None:
raise ValueError("Data not present in list")
if previous is None:
self.head = current.get_next()
else:
previous.set_next(current.get_next())

Tree

They are non-linear Data structures that have roots and nodes. The root is the node from which the data comes from, and the nodes are the other data points we have access to. The preceding node is called the parent while the node after is known as the child. The leaves are the outermost nodes.

Trees create a hierarchy that is used in HTML pages to distinguish which tag comes under which block. It can also be used for searching purposes

That concludes the discussion of Python's main significant Data Structures. Other significant data structures that we have not covered are graphs and hashmaps, which are technically similar to dictionaries.