Data Structure Using in Python

Download as pdf or txt
Download as pdf or txt
You are on page 1of 32

1

Data Structure Using


Python

Define Data Structure:

A data structure is a storage that is used to store and organize data.


It is a way of arranging data on a computer so that it can be
accessed and updated efficiently.
A data structure is not only used for organizing the data. It is also
used for processing, retrieving, and storing data. There are different
basic and advanced types of data structures that are used in almost
every program or software system that has been developed. So we
must have good knowledge about data structures.

1. Linear Data Structure: Data structure in which data


elements are arranged sequentially or linearly, where each
element is attached to its previous and next adjacent
elements, is called a linear data structure.

Example: Array, Stack, Queue, Linked List, etc.


2

2. Static Data Structure: Static data structure has a fixed


memory size. It is easier to access the elements in a static
data structure.
Example: array.

3. Dynamic Data Structure: In dynamic data structure, the size


is not fixed. It can be randomly updated during the runtime
which may be considered efficient concerning the memory
(space) complexity of the code.

Example: Queue, Stack, etc.

4. Non-Linear Data Structure: Data structures where data


elements are not placed sequentially or linearly are called
non-linear data structures. In a non-linear data structure, we
can’t traverse all the elements in a single run only.
Examples: Trees and Graphs.

Abstract Data Types:

Abstract Data type (ADT) is a type (or class) for objects whose
behavior is defined by a set of values and a set of operations. The
definition of ADT only mentions what operations are to be performed
but not how these operations will be implemented. It does not specify
how data will be organized in memory and what algorithms will be
used for implementing the operations. It is called “abstract” because
it gives an implementation-independent view.
3

● Abstract Data Type (ADT): It’s like a box where you can store data
and perform operations on it, but you can’t see how it’s done
inside.
● Interface: This is the part where you, as the user, interact with the
ADT. You can use the public functions provided to work with the
data.
● Public Functions: These are the operations that you’re allowed to
use, like adding or removing data.
● Private Functions: These are hidden operations that help the ADT
work properly, but you don’t need to worry about them.
● Data Structures: Inside the ADT, data is organized in a certain way,
like in an array or a linked list, which are types of lists with
different properties.
● Memory: This is where all the data in the ADT is actually stored.

Three ADTs namely List ADT, Stack ADT, Queue ADT.

1. List ADT

A List ADT (Abstract Data Type) is a collection of elements with a


defined sequence, supporting operations like insertion, deletion, and
access by position. It abstracts the implementation details, focusing
on the operations that can be performed on the list
4

● The data is generally stored in key sequence in a list which


has a head structure consisting of count, pointers and
address of compare function needed to compare the data in
the list.
● The data node contains the pointer to a data structure and
a self-referential pointer which points to the next node in the
list.
● The List ADT Functions is given below:
● get() – Return an element from the list at any given position.
● insert() – Insert an element at any position of the list.
● remove() – Remove the first occurrence of any element from a
non-empty list.
● removeAt() – Remove the element at a specified location from
a non-empty list.
● replace() – Replace an element at any position by another
element.
● size() – Return the number of elements in the list.
● isEmpty() – Return true if the list is empty, otherwise return
false.
● isFull() – Return true if the list is full, otherwise return false.

Implementation:
5

Output:
6

Stack ADT:

A Stack ADT is a collection where elements are added and removed from
one end, called the top. Instead of storing data directly in each node, a
pointer to the data is stored. The stack has a head node that contains a
pointer to the top element and a count of the number of entries.

● In Stack ADT Implementation instead of data being stored in


each node, the pointer to data is stored.
● The program allocates memory for the data and the address
is passed to the stack ADT.
● The head node and the data nodes are encapsulated in the
ADT. The calling function can only see the pointer to the
stack.
● The stack head structure also contains a pointer to top and
count of the number of entries currently in the stack.
● push() – Insert an element at one end of the stack called top.
● pop() – Remove and return the element at the top of the
stack, if it is not empty.
7

● peek() – Return the element at the top of the stack without


removing it, if the stack is not empty.
● size() – Return the number of elements in the stack.
● isEmpty() – Return true if the stack is empty, otherwise return
false.
● isFull() – Return true if the stack is full, otherwise return false.

Stack Implementation:

Output:
8

Queue ADT

● The queue abstract data type (ADT) follows the basic design
of the stack abstract data type.
● Each node contains a void pointer to the data and the link
pointer to the next element in the queue. The program’s
responsibility is to allocate memory for storing the data.
● enqueue() – Insert an element at the end of the queue.
● dequeue() – Remove and return the first element of the
queue, if the queue is not empty.
● peek() – Return the element of the queue without removing it,
if the queue is not empty.
● size() – Return the number of elements in the queue.
● isEmpty() – Return true if the queue is empty, otherwise return
false.
● isFull() – Return true if the queue is full, otherwise return false.
● front() - This operation returns the element at the front
end without removing it.
● rear() - This operation returns the element at the rear
end without removing it.
9

Queue implementation

class
Queue:
"""
Define the queue data structure.
"""
def __init__(self):
self.front = None
self.rear = None
self.size = 0

Now, we have the Queue data structure, let’s make it more


interesting by giving it some behaviors, shall we? Similar to a
Stack, we can define several methods that get check if the
queue is empty, or get the size of the queue, or access to the
front and the rear of the queue.

"""

Return the size of the queue


"""
def size(self):
return self.size

"""
Check if the queue is empty
"""
def isEmpty(self):
return self.size == 0

"""
10

Return the front element of the


queue.
"""
def getFront(self):
if not self.front:
return None
return self.front.data

"""
Return the rear element of the
queue.
"""
def getRear(self):
if not self.rear:
return None
return self.rear.data

Two main operations that make a Queue unique: enqueue


and dequeue.

1. enqueue(data): add a new piece of data to the rear


of the queue.
11

According to the diagram, there are three steps involved in this


function:

1. Set the next pointer of rear to the added node.


2. Set the rear pointer to the newly added node.
3. Increase the size by 1.

There’s one edge case we need to cover: when the queue is


empty. When this happens, the previous algorithm will not work.
What we need to do is setting the front and the rear pointers to
the added node.
"""

Add a new element to the end of the queue.


"""
def enqueue(self, data):
node = Node(data)
if self.rear is None:
self.front = node
else:
self.rear.next = node
self.rear = node
self.size += 1

2. dequeue(): pop the front element from the queue and return it.
12

According to the diagram, there are three steps involved:

1. Save the front pointer as the removed pointer.


2. Set the front pointer to the next pointer.
3. Decrease the size by 1.

Same as the enqueue function, we need to handle the edge


case when there is only one element in the queue: we need to
set both front and rear to None. Also, we need to do a sanitary
check to make sure we will not dequeue an empty queue.
"""

Remove the top element of the queue and return it.


"""
def dequeue(self):
if self.isEmpty():
raise Exception("[ERROR]: dequeue an empty queue!")
removed = self.front.data
if self.front is self.rear:
self.front = None
self.rear = None
else:
self.front = self.front.next
self.size -= 1
return removed
13

BAGS & Iterator

1. BAGS
A bag object is an unordered collection of zero or more elements
of some type.
Bags (Multisets) A bag, or multiset, is a collection of elements
where the order is immaterial but, unlike a set, duplicate elements
do matter.

Why We Use BAGs in Python.

Bags are used to store a collection of items where order and


uniqueness are not important. They are useful for counting
occurrences, grouping items, and performing simple collection
operations without the constraints of sets or lists. Bags provide a
flexible and efficient way to handle collections with duplicate items.
They can be useful in scenarios like inventory management,
frequency analysis, and basic data aggregation.

Example
14

Applications of BAGS

Inventory Management: Bags are ideal for managing inventories in


stores or warehouses where multiple items of the same kind need to
be tracked. Unlike sets, bags allow duplicate entries, making it easy
to count and manage quantities of each item.

Word Frequency Count: Bags are frequently used in text processing


to count the occurrence of each word in a document. This
application helps in tasks like keyword extraction, sentiment analysis,
and topic modeling by providing a straightforward way to tally word
frequencies.

Multisets in Mathematics: In mathematical applications, bags (or


multisets) are used to represent collections of elements where
repetition is allowed. They are useful in combinatorial problems
where the number of occurrences of elements is significant.

Voting Systems: In electronic voting systems, bags can be used to


collect votes where multiple votes for the same candidate are
possible. This allows for easy tallying and counting of votes to
determine the winner.

Event Logging: Bags are also useful in logging systems where


multiple identical events need to be recorded and counted. This
helps in analyzing the frequency and patterns of events, such as
error occurrences or user actions in software applications.

2. Iterators
15

An iterator is an object that contains a countable number of values.

An iterator is an object that can be iterated upon, meaning that you can
traverse through all the values.

Technically, in Python, an iterator is an object which implements the iterator


protocol, which consist of the methods __iter__() and __next__().

Iterator VS Iterable

Feature Iterator Iterable

Definition An object that implements An object that implements


__iter__() and __next__() the __iter__() method,
methods. returning an iterator.

Methods Must implement __next__() Must implement __iter__()


method. method.

Usage Used to iterate over Provides an iterator using


elements one at a time. __iter__() to start iteration.

State Maintains state between Does not maintain state;


calls to __next__(). produces an iterator.

Example Lists, tuples, and Lists, tuples, dictionaries,


dictionaries in Python are and sets are iterable. They
16

iterable, and their iterators provide iterators that


are used to traverse the traverse their elements.
elements.

Creation Created by calling __iter__() Can be directly used in a


on an iterable. for loop or with functions
like iter().

Memory Can be more memory Generally requires the


Efficiency efficient when used with whole dataset to be in
large datasets, as it memory if the iterable is a
generates values on-the-fly. container type (e.g., list).

Implementation :

Output:

2.Code:

Create an iterator that returns numbers, starting with 1, and each sequence
will increase by one (returning 1,2,3,4,5 etc.):

Output:
17

Applications of Iterators:

Data Streaming:

Iterators are used in data streaming applications to process large datasets


one chunk at a time. This allows handling data that doesn’t fit entirely in
memory by yielding chunks sequentially. Examples include processing log files
or streaming data from APIs.

Lazy Evaluation:

In lazy evaluation, iterators compute values on-the-fly, improving performance


by avoiding unnecessary computations. They are used in scenarios like
generating large sequences or reading data from files where processing is
deferred until required.

Search Algorithms:

Iterators are employed in search algorithms to traverse large graphs or trees


efficiently. For example, in depth-first or breadth-first search, iterators handle
nodes one at a time, allowing the algorithm to explore the structure
systematically.

Pipeline Processing:

In data processing pipelines, iterators pass data through a sequence of


processing stages. Each stage can process or filter data incrementally, such
as in ETL (Extract, Transform, Load) processes or data transformations in
machine learning workflows.

Matrix Abstract Data Type


A Matrix Abstract Data Type (ADT) is a two-dimensional collection of
elements arranged in rows and columns. It supports operations such
as accessing elements, updating values, and performing
18

matrix-specific operations like addition, multiplication, and


transposition. Matrices are used in various applications, including
mathematical computations, computer graphics, and data
representation. They provide a structured way to manage and
manipulate grid-like data efficiently.

Sparse Matrix
If most of the elements of the matrix have 0 value, then it is
called a sparse matrix.

Why to use Sparse Matrix instead of simple matrix ?

● Storage: There are lesser non-zero elements than zeros


and thus lesser memory can be used to store only those
elements.
● Computing time: Computing time can be saved by
logically designing a data structure traversing only
non-zero elements..

Example:

00304

00570

00000

02600

Representing a sparse matrix by a 2D array leads to wastage of


lots of memory as zeroes in the matrix are of no use in most of
the cases. So, instead of storing zeroes with non-zero elements,
we only store non-zero elements. This means storing non-zero
elements with triples- (Row, Column, value).
19

Sparse Matrix Representations can be done in many ways


following are two common representations:

1. Array representation
2. Linked list representation

Method 1: Using Arrays:

2D array is used to represent a sparse matrix in which there are


three rows named as

● Row: Index of row, where non-zero element is located


● Column: Index of column, where non-zero element is
located
● Value: Value of the non zero element located at index –
(row,column)

Implementation:
20

Output:

Method 2: Using Linked Lists


In the linked list, each node has four fields. These four fields are
defined as:

● Row: Index of row, where non-zero element is located


● Column: Index of column, where non-zero element is
located
● Value: Value of the non zero element located at index –
(row,column)
● Next node: Address of the next node
21

Implementation:
22

Spiral Matrix:

A spiral matrix is a matrix in which the elements are arranged in


a spiral order, starting from the top-left corner and proceeding
clockwise.

Example:

Input:

r = 4, c = 4

matrix[][] = {{1, 2, 3, 4},

{5, 6, 7, 8},

{9, 10, 11, 12},

{13, 14, 15,16}}

Output:

1 2 3 4 8 12 16 15 14 13 9 5 6 7 11 10
23

Implementation:

Output:
24

Time Complexity: O(m*n), where m and n are the number of rows


and columns of the given matrix respectively.
Auxiliary Space: O(m*n), for the seen matrix and the result vector.

Symmetric Matrix:

A symmetric matrix is one that is equal to its transpose.


Here's how to identify one and how it can be applied. A
symmetric matrix is a matrix that is symmetric along the
diagonal, which means Aᵀ = A , or in other words, the
matrix is equal to its transpose. It's an operator with the
self-adjoint property.

The symmetric matrices are simply the Hermitian


matrices but with the conjugate transpose being the
same as themselves. Therefore, it has all the properties
that a symmetric matrix has.
25

Properties of a Symmetric Matrix


Three properties of symmetric matrices are introduced in this section.
They are considered to be the most important because they concern
the behavior of eigenvalues and eigenvectors of those matrices. Those
are the fundamental characteristics, which distinguishes symmetric
matrices from non-symmetric ones.

Symmetric Matrix Properties


1. Has real eigenvalues
2. Eigenvectors corresponding to the eigenvalues are orthogonal
3. Always diagonalizable

Symmetric vs. Skew-Symmetric Matrix

A symmetric matrix is a matrix equal to its transpose. In contrast, a


skew-symmetric (or antisymmetric or antimetric) matrix is one that is
opposite to its transpose, or when its transpose equals its negative.

In a skew-symmetric matrix, the condition Aᵀ = -A is met, plus all main


diagonal entries are zero and the matrix’s trace equals zero. The sum of
two skew-symmetric matrices, as well as a scalar multiple of a
skew-symmetric matrix, will always be skew-symmetric.

Applications of Symmetric Matrices

Statistics
In statistics and multivariate statistics, symmetric matrices can be used
to organize data, which simplifies how statistical formulas are
expressed, or presents relevant data values about an object. Symmetric
matrices may be applied for statistical problems covering data
correlation, covariance, probability or regression analysis.
26

Machine Learning and Data Science


Symmetric matrices, such as Hessian matrices and covariance matrices,
are often applied in machine learning and data science algorithms to
optimize model processing and output. A matrix can help algorithms
effectively store and analyze numerical data values, which are used to
solve linear equations.

Engineering and Robotics


Symmetrical matrices can provide structure for data points and help
solve large sets of linear equations needed to calculate motion,
dynamics or other forces in machines. This can be helpful for areas in
control system design and optimization design of engineered systems,
as well as for solving engineering problems related to control theory.

Python Set Operations

If you remember your high school basic math, you’ll probably recall
mathematical set operations like union, intersection, difference, and
symmetric difference. Now, the interesting part is we can do the same
thing with Python sets.

Set Union

The union of two sets is the set that contains all of the elements from
both sets without duplicates. To find the union of two Python sets, use
the union() method or the | syntax.

Here is an example of how to perform a union set operation in


python.
27

​ Python

set1 = {11, 22, 33}

set2 = {33, 54, 75}

# Perform union operation using the `|` operator or the `union()` method

union_set = set1 | set2

# Or: union_set = set1.union(set2)

print(union_set)

Output:

{33, 22, 54, 75, 11}

Explanation:

In this example, we have two sets set1 and set2. We can perform a
union operation using either the | operator or the union() method,
which combines all the elements from both sets and removes any
duplicates. The resulting union_set contains all elements from set1
and set2.
28

Set Intersection

The intersection of two sets is the set that contains all of the
common elements of both sets. To find the intersection of two Python
sets, we can either use the intersection() method or & operator.

Here is an example to demonstrate this.

​ Python

set1 = {11, 22, 33}

set2 = {33, 54, 75}

# Perform intersection operation using the `&` operator or the `intersection()`


method

intersection_set = set1 & set2

# Or: intersection_set = set1.intersection(set2)

print(intersection_set)

Output:

{33}
29

Explanation:

In this example, we have two sets set1 and set2. We can perform an
intersection operation using either the & operator or the
intersection() method, which returns a new set that contains only the
elements that are common to both set1 and set2. The resulting
intersection_set contains only element 33, which is present in both
set1 and set2.

Set Difference

The difference between the two sets is the set of all the elements
in the first set that are not present in the second set. To
accomplish this, we can either use the difference() function or
the – operator in python.

Here is an example to demonstrate this.

​ Python

set1 = {1, 2, 3, 4, 5}

set2 = {3, 4, 5, 6, 7}

# Using the difference() method

set3 = set1.difference(set2)

print(set3)
30

# Using the - operator

set4 = set1 - set2

print(set4)

Output:

{1, 2}

{1, 2}

Explanation: In this example, we have two sets set1 and set2 with
some common elements. We want to get the elements that are
present in set1 but not in set2. We can achieve this by using the
difference() method or the – operator.

In the first approach, we use the difference() method of set1 and


pass set2 as an argument. This returns a new set set3 that
contains the elements that are present in set1 but not in set2.

In the second approach, we use the – operator to perform the


set difference operation. This is equivalent to using the
difference() method. The result is stored in the set4.

Set Symmetric Difference

The symmetric difference between the two sets is the set


containing all the elements that are either in the first or second
set but not in both. In Python, you can use either the symmetric
difference() function or the ^ operator to achieve this.
31

Here is an example that demonstrates this.

​ Python

set1 = {1, 2, 3, 4, 5}

set2 = {3, 4, 5, 6, 7}

# Using the symmetric_difference() method

set3 = set1.symmetric_difference(set2)

print(set3)

# Using the ^ operator

set4 = set1 ^ set2

print(set4)

Output:

{1, 2, 6, 7}

{1, 2, 6, 7}

Explanation: In this example, we have two sets set1 and set2 with
some common elements. We want to get the elements that are
32

present in either of the sets, but not in both. We can achieve this
by using the symmetric_difference() method or the ^ operator.

In the first approach, we use the symmetric_difference() method


of set1 and pass set2 as an argument. This returns a new set
set3 that contains the elements that are present in either of the
sets, but not in both.

In the second approach, we use the ^ operator to perform the


set symmetric difference operation. This is equivalent to using
the symmetric_difference() method. The result is stored in the
set4

You might also like