You are on page 1of 14

Data Structures and Algorithms Analysis structure.

If an operation takes ƒ(n) time in execution,


then the actual operation may take time as the
Data Structure is a systematic way to organize data in order random number which would be maximum as ƒ(n).
to use it efficiently.
Basic Terminology
Following terms are the foundation terms of a data structure.
• Data − Data are values or set of values.
• Interface − Each data structure has an interface.
Interface represents the set of operations that a data • Data Item − Data item refers to single unit of values.
structure supports. An interface only provides the list
of supported operations, type of parameters they can • Group Items − Data items that are divided into sub
accept and return type of these operations. items are called as Group Items.

• Implementation − Implementation provides the • Elementary Items − Data items that cannot be
internal representation of a data structure. divided are called as Elementary Items.
Implementation also provides the definition of the
• Attribute and Entity − An entity is that which
algorithms used in the operations of the data
contains certain attributes or properties, which may
structure.
be assigned values.
Characteristics of a Data Structure
• Entity Set − Entities of similar attributes form an
• Correctness − Data structure implementation entity set.
should implement its interface correctly.
• Field − Field is a single elementary unit of
• Time Complexity − Running time or the execution information representing an attribute of an entity.
time of operations of data structure must be as small
• Record − Record is a collection of field values of a
as possible.
given entity.
• Space Complexity − Memory usage of a data
• File − File is a collection of records of the entities in
structure operation should be as little as possible.
a given entity set.
Need For Data Structure
Algorithm
As applications are getting complex and data rich,
Algorithm is a step-by-step procedure, which defines a set of
there are three common problems that applications
instructions to be executed in a certain order to get the desired
face now-a-days.
output. Algorithms are generally created independent of
• Data Search − Consider an inventory of 1 underlying languages, i.e. an algorithm can be implemented
million(106) items of a store. If the application is to in more than one programming language.
search an item, it has to search an item in 1
From the data structure point of view, following are some
million(106) items every time slowing down the
important categories of algorithms −
search. As data grows, search will become slower.
• Search − Algorithm to search an item in a data
• Processor speed − Processor speed although
structure.
being very high, falls limited if the data grows to
billion records. • Sort − Algorithm to sort items in a certain order.
• Multiple requests − As thousands of users can • Insert − Algorithm to insert item in a data structure.
search data simultaneously on a web server, even
the fast server fails while searching the data. • Update − Algorithm to update an existing item in a
data structure.
To solve the above-mentioned problems, data structures
come to rescue. Data can be organized in a data structure in • Delete − Algorithm to delete an existing item from a
such a way that all items may not be required to be searched, data structure.
and the required data can be searched almost instantly
Characteristics of an Algorithm
Execution Time Cases
Not all procedures can be called an algorithm. An algorithm
There are three cases which are usually used to compare should have the following characteristics −
various data structure's execution time in a relative manner.
• Unambiguous − Algorithm should be clear and
• Worst Case − This is the scenario where a particular unambiguous. Each of its steps (or phases), and
data structure operation takes maximum time it can their inputs/outputs should be clear and must lead to
take. If an operation's worst case time is ƒ(n) then only one meaning.
this operation will not take more than ƒ(n) time where
• Input − An algorithm should have 0 or more well-
ƒ(n) represents function of n.
defined inputs.
• Average Case − This is the scenario depicting the
• Output − An algorithm should have 1 or more well-
average execution time of an operation of a data
defined outputs, and should match the desired
structure. If an operation takes ƒ(n) time in execution,
output.
then m operations will take mƒ(n) time.
• Finiteness − Algorithms must terminate after a finite
• Best Case − This is the scenario depicting the least
number of steps.
possible execution time of an operation of a data
• Feasibility − Should be feasible with the available • insertion and removal from a queue
resources. • returning a value from a function

• Independent − An algorithm should have step-by-


step directions, which should be independent of any
programming code.

Algorithm Analysis

Algorithm analysis deals with the execution or running time of


various operations involved. The running time of an operation
can be defined as the number of computer instructions
executed per operation.

Efficiency of an algorithm can be analyzed at two different


stages, before implementation and after implementation. They
are the following −

• A Priori Analysis − This is a theoretical analysis of


an algorithm. Efficiency of an algorithm is measured
by assuming that all other factors, for example,
processor speed, are constant and have no effect on
the implementation. O(n) – Linear Time

• A Posterior Analysis − This is an empirical analysis O(n) means that the run time increases at the same pace as
of an algorithm. The selected algorithm is the input.
implemented using programming language. This is
then executed on target computer machine. In this
analysis, actual statistics like running time and space
required, are collected.

We shall learn about a priori algorithm analysis.

Big-O

O(n²) → Quadratic Time

O(n²) means that the calculation runs in quadratic time, which


is the squared size of the input data.

In programming, many of the more basic sorting algorithms


have a worst-case run time of O(n²):
Big-O notation is the language we use for talking about how
long an algorithm takes to run (time complexity) or how much • Bubble Sort
memory is used by an algorithm (space complexity). Big-O • Insertion Sort
notation can express the best, worst, and average-case • Selection Sort
running time of an algorithm. For our purposes here, we are
going to focus primarily on Big-O as it relates to time
complexity.

O(1) – Constant Time

O(1) means that it takes a constant time to run an algorithm,


regardless of the size of the input.

In programming, a lot of operations are constant. Here are


some examples:

• math operations
• accessing an array via the index
• accessing a hash via the key
• pushing and popping on a stack
Algorithm Complexity

Suppose X is an algorithm and n is the size of input data, the


time and space used by the algorithm X are the two main
factors, which decide the efficiency of X.

• Time Factor − Time is measured by counting the


number of key operations such as comparisons in
the sorting algorithm.

• Space Factor − Space is measured by counting the


maximum memory space required by the algorithm.

The complexity of an algorithm f(n) gives the running time


and/or the storage space required by the algorithm in terms
O(log n) → Logarithmic Time of n as the size of input data.
O(log n) means that the running time grows in proportion to
the logarithm of the input size, meaning that the run time
barely increases as you exponentially increase the input. Time Complexity

Example algorithm in programming: Is time complexity the actual time required to execute the
code?
• Binary Search
NO!

Instead of measuring actual time required in executing each


statement in the code, Time Complexity considers how
many times each statement executes.

Time complexity of an algorithm represents the amount of time


required by the algorithm to run to completion. Time
requirements can be defined as a numerical function T(n),
where T(n) can be measured as the number of steps, provided
each step consumes constant time.

For example, addition of two n-bit integers takes n steps.


Consequently, the total computational time is T(n) = c ∗ n,
where c is the time taken for the addition of two bits. Here, we
observe that T(n) grows linearly as the input size increases.

Space Complexity

Space complexity of an algorithm represents the


amount of memory space required by the algorithm
in its life cycle. The space required by an algorithm is
equal to the sum of the following two components −

• A fixed part that is a space required to store certain


data and variables, that are independent of the size
of the problem. For example, simple variables and
constants used, program size, etc.
• A variable part is a space required by variables,
whose size depends on the size of the problem. For
example, dynamic memory allocation, recursion
stack space, etc.

Space Complexity = Auxiliary Space + Space used for input


values

3. Nested Loops with Different Iteration Bounds: If


Auxiliary Space Vs Space Complexity you have nested loops with different iteration
bounds, the time complexity will be determined by
AUXILIARY SPACE - space required by an algorithm/problem the product of the bounds. For example, if you have
during the execution of that algorithm/problem and it is not an outer loop iterating n times and an inner loop
equal to the space complexity because space complexity iterating m times, the total number of iterations
includes space for input values along with it also. becomes n * m, resulting in a time complexity of O(n
* m).
Why Is There A Need To Calculate Space Complexity?

Nowadays all systems come up with a large memory so this


space is not considered for them but to make our algorithm
more efficient so that it can run in less amount of space we
have to analyze the space complexity.

Developers of real-world applications are constrained by the


memory space of the systems they chose to run on. This is
where space complexity comes into play, as we never want to
run a function or process that consumes more space than the
system has available at any given time. In A Nutshell (Space Complexity)
How To Evaluate Space Complexity Algorithm? 1. Constant Space (O(1)): Algorithms that use a
We need to know the amount of memory used by different constant amount of memory regardless of the input
types of datatype variables, program instructions, constant size have constant space complexity. This includes
values and few other things like function call, recursion variables, constants, and a fixed number of memory
stack(in recursion case) in order to calculate the space allocations.
complexity. The amount of memory used by different types of
datatype variables varies by OS, but the way of calculating the
space complexity continues to remain the same.

2. Linear Space (O(n)): Algorithms that use memory


proportional to the input size have linear space
complexity. This can involve data structures like
arrays or lists that store each input element.

3. Quadratic Space (O(n2)): Algorithms that use


memory proportional to the square of the input size
have quadratic space complexity. This could arise
when using a 2D array to store pairs of elements.
In A Nutshell (Time Complexity)

1. For Loop (Single Loop): A single for loop that


iterates through an input of size n will generally result
in a time complexity of O(n). Each iteration takes a
constant amount of time.

4. Combined Space (O(n + m)): When multiple inputs


are involved, space complexity may depend on the
sum of their sizes. For example, if you have two
arrays of sizes n and m, respectively, and you create
2. Nested Loops: When you have nested loops, the a third array of size n + m, the space complexity
time complexity can increase. If you have two nested would be O(n + m).
for loops, one inside the other, each iterating n times,
the total number of iterations becomes n * n = n2,
resulting in a time complexity of O(n2).
Complexity

#include <iostream>

using namespace std;

int main() {

int arr[7] = {12, 45, 67, 23, 9, 50, 6};

int maxElement = arr[0]; // Assume the first element is the


maximum

for (int i = 1; i < 7; ++i) {


EXAMPLES if (arr[i] > maxElement) {

maxElement = arr[i];

cout << "Maximum element: " << maxElement << endl;

return 0;

}
Pointers: Fundamentals in Data Structures

Pointers

A pointer is a special variable that stores the memory address


of another variable.

• It "points" to the location where the data is stored


rather than holding the actual data.

Importance of Pointers Dynamic and static memory allocation in data structures

Pointers enable efficient manipulation of data structures like Static Memory allocation
linked lists, trees, and dynamic arrays.
• Allocation of memory at compile-time.
They allow for direct memory access and modification, • Static memory allocation reserves memory for
enhancing performance. variables before the program runs.
Memory Address Ex: declaring and array
Each byte in memory has a unique address. Pointers store Static memory allocation
and manipulate these memory addresses.
Int staticArray[5];

• The memory for static Array is allocated at compile-


Pointer Declaration time and remains fixed throughout the program's
execution.

Limitations of static memory allocation

• Fixed size: Cannot change size at runtime.


Example • Memory wastage: Memory is allocated even if not
fully utilized.

Dynamic memory allocation

• Allocation of memory at runtime.


• Dynamic memory allocation allows the program to
request memory as needed.

Example: Allocating memory for a dynamic array using ‘new’


in C++.

Dynamic memory allocation example:

Code example: int* dynamicArray = new int[5];


Let’s Try
Memory for dynamicArray is allocated at runtime, allowing
Write a program that calculates the sum of x and y using
flexibility in size.
pointers. Store the sum in z using pointers.
Advantages of dynamic memory allocation:
Initialize the following:
• Flexibility: Memory can be allocated or deallocated
int y = 88, x = 15, z;
as needed.
int *q = &y, *p = &x, *r = &z; • Efficient memory usage: Memory is allocated only
when required.
• Scenario: Resizing a dynamic array to
accommodate more elements
Pointer Dereferencing
Dynamic memory deallocation

• Dynamic memory must be explicitly deallocated


using delete in C++
• Importance of releasing memory to avoid memory Structures
leaks.
• A structure is a user-defined data type that groups
Code example: delete[] dynamicArray; variables of different data types under a single name.
• Structures allow creating complex data structures
Memory Leaks that hold different types of data.
• Unreleased dynamically allocated memory leads to
memory leaks
• Consequences: Gradual loss of available memory,
potential program crashes.

Example: Allocating memory in a loop without deallocating.

Choosing between allocation types

Considerations for choosing between static and dynamic


memory allocation:

• Fixed vs. variable size requirements


• Lifetime of the data.
• Memory efficiency.

Static memory allocation

Dynamic memory allocation


Whether you're implementing linked lists, stacks, queues, or
more intricate systems, structures provide the foundation for
encapsulating and managing data in a modular fashion.

3. Benefits of Using Structures:

• Readability: Structures enhance code readability by


encapsulating logically related data under a single
structure name.
• Modularity: They promote code modularity and
organization, making it easier to manage and
maintain large datasets.
• Abstraction: Structures allow you to abstract away
the low-level details of data organization, enabling a
clearer and higher-level view of your data

4. Encouraging Exploration: We encourage you to


Arrays of Structures
experiment with structures in your programming endeavors.
Code example: Student students[3]; As you explore their capabilities, you'll gain a deeper
understanding of how to create well-organized and efficient
Student students[3];: This line declares an array named data structures tailored to your application’s needs.
students that can hold three elements. Each element is of type
Student, which means it’s an instance of the Student structure. 5. Real-World Applications: Remember, structures aren’t
just about individual variables; they're about organizing data
1.By using this line, you're allocating memory for three in a way that mirrors real-world scenarios. From employee
instances of the Student structure within the students array. databases to geographic information systems, structures are
Each instance has its own id, name, and GPA member versatile tools that mirror the complexities of various domains.
variables.

INTRODUCTION TOABSTRACT DATA TYPES

Brief explana�on of ADTs as a high-level concept for


describing data structure and their opera�ons.

Review:

Private Access Specifier: Members declared as private


are accessible only within the class where they are
defined.

• They cannot be accessed directly by code


outside the class, including derived classes.
• Typically, data members (variables) are made
private to encapsulate the internal state of the
class and prevent direct modifica�on from
outside.
• Func�ons defined as private are typically used
for internal implementa�on details that
should not be directly called from outside the
class.

Public Access Specifier: Members declared as public


are accessible from anywhere, including outside the
class.
Summary and Conclusion: The Role of Structures in Data
Organization • Public members are the interface through
which external code interacts with the class.
1. Structures as Data Containers: Structures allow us to
create user-defined data types that bundle multiple variables • Data members and func�ons marked as public
of different data types under a single name. This promotes are accessible and can be used by code
better organization and readability of our code by grouping
outside the class.
related data together.

2. Building Blocks for Complexity: Structures serve as the


building blocks for constructing more complex data structures.
• Queue
• Linked List
• Set
• Dic�onary

Benefits of Using ADTs

• Improved modularity: ADTs allow isola�ng


implementa�on details from the user.
• Reusability: Once an ADT is designed and
What is an Abstract Data Type? implemented, it can be reused in various
applica�ons.
• An Abstract Data Type (ADT) is a logical
• Code maintenance: Changing the
descrip�on of how data is stored and
implementa�on of an ADT doesn't affect code
manipulated. It defines a set of data and
using the ADT.
opera�ons on that data, abstrac�ng away
implementa�on details. Example: Stack ADT
• ADTs encapsulate data and the opera�ons
that can be performed on that data. Defining a basic Stack ADT with push, pop, and peek
opera�ons.
ADT Components
Example: Queue ADT
Components of an ADT: Data (attributes) and
Operations (methods). Defining a basic Queue ADT with enqueue,
dequeue,and front opera�ons.
Data atributes, also known as member variables or
fields, are the proper�es or characteris�cs associated Example: Linked List ADT
with an instance of an ADT. These atributes store the Defining a basic Linked List ADT with insert, delete,
actual data that the ADT is designed to manage. They and traverse opera�ons.
define what kind of informa�on an instance of the ADT
Fundamentals of Linked Lists
can hold.
Linked Lists
Opera�ons, also referred to as methods, are the
ac�ons or behaviors that can be performed on an • A dynamic data structure that stores a collection of
elements.
instance of an ADT. They define how the data within the • Composed of nodes, each containing data and a
ADT can be manipulated, accessed, or modified. reference to the next node.
Opera�ons allow you to interact with the data and
Types of Linked Lists
carry out meaningful ac�ons.
• Singly Linked List: Nodes have a data field and a next
pointer.
• Doubly Linked List: Nodes have previous and next
pointers.
• Circular Linked List: Last node points back to the first
node.

Node Structure:

• Data Field: Holds the actual element.


• Next Pointer (Singly Linked): Points to the next node
in the sequence.
• Previous Pointer (Doubly Linked): Points to the
previous node

Advantages of Linked Lists

• Dynamic Size: Easily accommodate varying


Examples of ADTs numbers of elements.
• Efficient Insertions and Deletions: O(1) when
Common ADTs manipulating node pointers.
• Memory Efficiency: Memory allocated as needed, not
• Stack ina fixed block.
Disadvantages of Linked Lists Comprises nodes, each with data and a reference to the next
node.
• Increased Memory Overhead: Each node requires
additional memory for pointers. Anatomy of a Singly Linked List Node
• Slower Random Access: O(n) traversal to reach the
nth element. Each node contains:
• Complexity: Handling and maintaining pointers can • Data: Holds the value of the element.
be error-prone. • Next Pointer: Points to the next node in the
Basic Linked List Operations sequence.

• Traversal: Visit each node sequentially.


• Insertion: Add a new node at a specific position.
• Deletion: Remove a node from the list.
• Searching: Find a node with a specific value.
Key Characteristics
Insertion Operation
• Unidirectional: Nodes are connected in a linear
Insert at the beginning: manner.
• Starts with a Head: The first node is the head of the
• Create a new node.
list.
• Set new node's next pointer to the current first node.
• Ends with a Tail: The last node's next pointer is
• Update head pointer to the new node.
‘nullptr’.
Insert at the end:
Advantages of Singly Linked Lists
• Create a new node.
• Dynamic Size: Easily add or remove elements.
• Traverse to the last node.
• Efficient Insertions: Insertions at the beginning take
• Set the last node's next pointer to the new node. constant time.
Deletion Operation • Memory Efficiency: Memory allocated per node, not
in a block
Delete from the beginning:
Disadvantages of Singly Linked Lists
• Update head pointer to the next node.
• Free memory of the deleted node. • No Random Access: Traversal is required to access
elements.
Delete from the end: • Extra Memory: Requires additional memory for next
pointers
• Traverse to the second-to-last node. • Complexity: Managing pointers can be complex and
• Set its next pointer to NULL. error-prone.
• Free memory of the deleted last node
Basic Operations
Use Cases of Linked Lists
• Traversal: Traverse the list node by node.
• Applications: Implement stacks, queues, and hash • Insertion: Add a new node at the beginning or
tables. specific position
• Dynamic Memory Allocation: Used in memory • Deletion: Remove a node from the list.
management • Searching: Find a node with a specific value.
• Music and Video Playlists: Maintain a sequence of
media items.

Summary

• Linked Lists: A versatile data structure for dynamic


collections.
• Nodes: Basic building blocks with data and pointers.
• Operations: Traversal, insertion, deletion, and Use Cases of Singly Linked Lists
searching.
• Applications: Stacks, queues, hash tables, etc.
• Trade-offs: Efficient insertions and deletions, slower
• Dynamic Memory Allocation: Manage dynamic
random access.
memory.
Conclusion • Web Browsers: Maintain browsing history

• Linked lists provide a powerful tool for managing data Creating a Node in C++ (singly Linked List)
in dynamic scenarios.
• Understanding their basics is essential for building
more complex data structures and algorithm.

Introduction to singly linked list

A singly linked list is a linear data structure.


Advantages of Doubly Linked Lists

• Bidirectional Traversal: Allows easy navigation in


both directions.
• Efficient Insertions and Deletions: Adding or
removing nodes can be efficient, even at the
beginning or end.
• Flexibility: Can be useful in scenarios where
elements need to be accessed and modified in both
directions.

Disadvantages of Doubly Linked List

• Increased Memory Usage: Each node has two


pointers, consuming more memory compared to
singly linked lists
• Complexity: Managing and updating two pointers per
node can be more complex and error prone.

Summary Basic Operations

• Singly linked lists store elements using nodes. • Traversal: Traverse the list both forward and
• Nodes contain data and a next pointer. backward.
• Basic operations include traversal, insertion, • Insertion: Add a new node at various positions.
deletion, and searching. • Deletion: Remove a node from the list.
• Trade-offs: Efficient insertions, no random access. • Searching: Find a node with a specific value.

Use Cases of Doubly Linked Lists

Conclusion • Applications: Undo-redo functionality, navigation in


both directions.
• Understanding singly linked lists is essential for • Data Structures: Used as underlying structures for
building more complex data structures and more complex data structures.
algorithms.
• They provide a versatile tool for managing
sequences of data.

Doubly Linked Lists

Introduction to Doubly Linked Lists

• Doubly Linked List: A data structure that consists of


nodes, each having two pointers - one to the
previous node and one to the next node. Creating A node
• Unlike singly linked lists, doubly linked lists allow
traversal in both directions: forward and backward.

Anatomy of a Doubly Linked List Node

• A doubly linked list node contains:


• Data: The actual value or information the node
holds.
• Previous Pointer: Points to the previous node in the
sequence
• Next Pointer: Points to the next node in the
sequence.

Key Characteristics

• Bidirectional Traversal: Nodes can be traversed both


forwards and backwards.
• Starts with Head: The first node has a previous
pointer set to NULL.
• Ends with Tail: The last node has a next pointer set
to NULL.
Resultant List

Inserting at the Beginning

Summary

• Doubly linked lists allow bidirectional traversal.


• Each node has data, previous, and next pointers.
• Useful for scenarios requiring navigation in both
directions.
• Trade-offs: More memory usage, efficient
insertions/deletions.

Conclusion

• Doubly linked lists provide a versatile tool for


managing data with bidirectional navigation.
• Understanding their characteristics helps in
designing and implementing more complex data
structures.

You might also like