0% found this document useful (0 votes)
10 views37 pages

Chapter 2 Lessons

The document covers fundamental concepts of data structures, including definitions, advantages, applications, and characteristics. It discusses algorithms versus programs, performance analysis, time and space complexities, and asymptotic notation. Additionally, it explores arrays, linked lists, and their operations in Python, along with review questions for each chapter.

Uploaded by

cxamari8
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
10 views37 pages

Chapter 2 Lessons

The document covers fundamental concepts of data structures, including definitions, advantages, applications, and characteristics. It discusses algorithms versus programs, performance analysis, time and space complexities, and asymptotic notation. Additionally, it explores arrays, linked lists, and their operations in Python, along with review questions for each chapter.

Uploaded by

cxamari8
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 37

Chapter 1: Review questions

• 1. what is data structure? and give examples


• 2. write advantages of data structures
• 3. what the applications of data structures?
• 4. Discuss Characteristics of data structures
• 5. Explain data structure Terminologies
• 6. Describe Key Differences Between Data Types and Data Structures
• 7. Give examples of python data types
Algorithm VS Program

Algorithm Program

1. design phase 1. implement phase

1. language independent 2.language dependent

1. machine independent 3. machine dependent

1. analyze 4.testing
Performance analysis of an
• The algorithm
performance of an algorithm is generally measured by the size of its input
data n, and the time and the memory space(store the variables, constants, and
instructions) used by the algorithm.
• The time required is measured by the key operations to be performed by the
algorithm (such as comparison operations),
1.Time complexity
• The time complexity of the algorithm is the amount of time that an algorithm will
take to execute on a computer system to produce the output.
• The aim of analyzing the time complexity of the algorithm is to determine, for a
given problem and more than one algorithm, which one of the algorithms is the
most efficient with respect to the time required to execute.
• The running time required by an algorithm depends on the input size; as the
input size n, increases, the runtime also increases. the number of items in the
• The runtime of an algorithm for a specific input depends on the key operations to
be executed in the algorithm and size of input . Example : comparison operation >
assignment operation .
• Ideally, these key operations should not depend upon the hardware, the operating
system, or the programming language being used to implement the algorithm.
• A constant amount of time is required to execute each line of code; however, each
line may take a different amount of time to execute.
if n==0 || n == 3 #constant time
print("data")
else:
• for i in range( #loop run for n times
• print("structure")
• The time required by the algorithm depends on the time required for each
statement, and how many times a statement is executed.
• The running time of the algorithm is the sum of time required by all the
• The total running time T(n) of the algorithm for a given value of n
Cases of running time :
1. The worst-case running time of the algorithm is the upper-bound complexity; it
is the maximum runtime required for an algorithm to execute for any given
input. The worst-case time complexity is very useful in that it guarantees that
for any input data, the runtime required will not take more time as compared to
the worst-case running time.
2. The average-case running time is the average running time required for an
algorithm to execute. In this analysis, we compute the average over the running
time for all possible input values.
3. Best-case running time is the minimum time needed for an algorithm to run; it is
the lower bound on the running time required for an algorithm.
2.Space complexity
• estimates the memory requirement to execute it on a computer to produce the output as a function
of input data. In order to write a programming solution for any problem, some memory is required
for storing variables, program instructions, and executing the program on the computer.
• def squares(n):
• square_numbers = []
• for number in n:
• square_numbers.append(number * number)
• return square_numbers
• nums = [2, 3, 5, 8 ]
• print(squares(nums))
• Given two algorithms to solve a given problem, with all other requirements being equal, then the
algorithm that requires less memory can be considered more efficient.
• Space complexity analysis is important to understand the efficiency of an algorithm, especially for
applications where the memory space requirement is high.
• Generally, algorithms that are asymptotically efficient are considered to be better algorithms for
large-size inputs.
3.Asymptotic notation/how to calculate Complexity

• To analyze the time complexity of an algorithm, the rate of growth (order of growth)
is very important when the input size is large.
• we analyze the efficiency of algorithms for large input sizes considering the higher
order of growth and ignoring the multiplicative constants and lower-order terms.
• The following asymptotic notations are commonly used to calculate the running
time complexity of an algorithm:
• Asymptotic notations are classified into three types:
1. Big-Oh (O) notation/ worst-case : It denotes the worst-case running time
complexity with a tight bound.
2. Omega ( Ω ) notation/ best-case : it measures the best case time complexity or
the best amount of time an algorithm can possibly take to complete
3. Theta ( Θ ) notation/ average-case : It denotes the lower bound of an algorithm’s
running time. It measures the best amount of time to execute the algorithm.
Example
• Assume you have a set of numbers S= (10, 50, 20, 15, 30)
• There are numerous algorithms for sorting the given numbers.
Big-Oh (O) notation
• is a way to describe the performance or complexity of an algorithm. And it helps
developers to understand and compare the efficiency of different algorithms for
solving the same problem.
• helps us predict how an algorithm will perform as the input data grows, and how
to optimize the code of the program like large dataset .
• Common Big-O Notations
1. O(1): Constant time complexity, where the algorithm's runtime remains
constant regardless of the input size.
2. O(n): Linear time complexity, where the algorithm's runtime grows linearly
with the input size. like linear search algorithm.
3. O(n^2): Quadratic time complexity, where the algorithm's runtime grows
quadratically with the input size. Like bubble sort algorithm. the performance of
the algorithm is directly proportional to the square of the input size.
1.O(1)- constant Time

Def get_first_element(numbers):
return numbers[0] # O(1)
Numbers =[12, 3, 4, 15]
print(get_first_element(numbers):

n = [12, 3, 4, 15]
ans = sum(n)
# display sum
print('Sum of the array is ', ans)
2.O(n)- linear time
def tot_sum(n):
sum = 0 # O(1)
for i in n:
sum = sum + I # O(1)*n= O(n)
return sum # O(1)
n = [12, 3, 4, 15]
ans = tot_sum(n)
print('Sum of the array is ', ans)
O(1)+O(n) +O(1)=O(n)
3.O(n^2): Quadratic time
complexity,
• Nested loops
• a = [2, 3, 5, 8 ]
• for i in a: # o(n)
• for x in a: # o(n)*n
• print(x)
Chapter 2 : Questions
1. What does mean “algorithm”? give an examples
2. Discuss algorithm characteristics
3. Identify differences b/w algorithm and program
4. What are the key steps in the process of designing an efficient algorithm?
5. Discuss algorithm design tools
6. What are some common types of algorithms, and how do they differ in terms of design
approach?
7. Why do we need to perform algorithm analysis?
8. What are the time and space complexities of the algorithm?
9. Why is asymptotic notation important in algorithm analysis?
10. What are the best, worst, and average cases for the algorithm?
11. What is Big O notation and why is it important in algorithm analysis?
12. How do O(1), O(n),and O(n^2)differ in terms of performance?
13. When comparing two algorithms with different time complexities, how do you decide
14. design an algorithm for a program that divides two numbers using
algorithm design tools
15. Find the time complexity of the following Python snippets:
A. print("Hello World")
B. n = 8
for i in range(1, n + 1):
print("Hello World !!!")
Chapter 3: Arrays
An array is a container which can hold a fix number of items.
Linear data structure
contiguous memory location.
Access elements randomly
Homogeneous elements or similar elements
Each item stored in an array is called an element and they can be of any type
including integers, floats, strings, etc.
Each location of an element in an array has a numerical index starting from 0.
What's the Difference between Python Lists and
Python Arrays?
Lists and arrays behave similarly.
Just like arrays, lists are an ordered sequence of elements.
They are also mutable and not fixed in size.
Lists store items that are of various data types. But arrays store single data type.
Types of Array
• There are two main types of arrays:
1. One-dimensional arrays: These arrays store a single row of elements.
2. Multidimensional arrays: These arrays store multiple rows of elements.
Array Operations
• Common operations performed on arrays include:
Traversal : Visiting each element of an array in a specific order (e.g., sequential,
reverse).
Insertion : Adding a new element to an array at a specific index.
Deletion : Removing an element from an array at a specific index.
Searching : Finding the index of an element in an array.
• Applications of Array
• Arrays are used in a wide variety of applications, including:
Storing data for processing
Implementing data structures such as stacks and queues
Representing data in tables and matrices
Creating dynamic data structures such as linked lists and trees
Array Representation
1.Creating 1D Array in Python
The general syntax:
variable_name = array(typecode,[elements])
import array as arr
# creating an array with integer type
a = arr.array('i', [1, 2, 3])
print (type(a), a)
# creating an array with char type
a = arr.array('u', 'BAT')
print (type(a), a)
creating an array with float type
a = arr.array('d', [1.1, 2.2, 3.3])
Basic Operations on Python Arrays
1.Accessing Array Element
• Example
• The below code shows how to access elements of an array.
from array import *
• array1 = array('i', [10,20,30,40,50])
• print (array1[0])
2.Insertion Operation
array1.insert(1,60)
for x in array1:
print(x)
3.Deletion Operation
array1.remove(40)
for x in array1:
print(x)
4.Search Operation
print (array1.index(40))
5.Update Operation
array1[2] = 80
for x in array1:
print(x)
1.2D Array in Python
Creating 2D Array in Python
Syntax of Python 2D Array
We can create a 2D array in python as below:
array_name=[n_rows][n_columns]
Example : arr=[[1,2,3],[4,5,6],[7,8,9]]
1.Accessing Values in Python 2D Array
Accessing single row syntax
the syntax:
array_name[row_ind][col_ind]example : print("Element at [1][0]=",arr[1][0])
Accessing an Internal Array/row
print("Element at [2]=",arr[2])
2.Traversing Values in Python 2D Array
for row in arr:
for col in row:
print(col ,end=" ")
print()
3.Inserting Values in Python 2D Array/new row
arr1.insert(ind,arr_insert)
arr.insert(2, [11, 12, 13])
print("Modified Array")
for row in arr:
for col in row
print(col , end=" ")
print()
• Inserting an Element in Inner Array./new element
• arr[1].insert(2,12) # arr_name[r_index].insert(c_index,new element)
4.Updating Values in Python 2D Array
Syntax of Updating a Single Element
arr_name[r_index][c _index]=new_element ex:arr[1][2]=16,
Updating an Inner Array/ new row
new_arr=[10,11,12]
arr[1]=new_arr
5.Deleting Values in Python 2D Array
• Syntax of Deleting a Single Element
• del(arr_name[r][c]) Example :del(arr[1][2])
• Deleting an Inner Array/ row
• del(arr[1])
Chapter 3: Questions
1. What is an array in Python? And general syntax .
2. How do you create a array in Python?
3. What is the difference between a list and an array in Python?
4. How do you manipulate arrays in Python?
5. What are some common operations for arrays in Python?
6. How do you access elements in a 1D array in Python?
7. How do you modify elements in a 1D array in Python?
8. What is a 2D array in Python?
9. How do you create a 2D array in Python?
10. How do you access elements in a 2D array in Python?
Chapter 4 : linked lists
Linked List is a linear data structure.
linked list elements are called Node.
Dynamic data structure
Linked list elements are stored randomly
Accessing the elements in linked list will be slower as compare to list
Each element of linked list contains data and pointer /address
The elements are linked using pointers.
When Should You Use Linked
Lists?
• Linked lists are most useful when:
You need to frequently insert and delete many elements
The data size is unpredictable or likely to change frequently
Direct access to elements is not a requirement
The dataset contains large elements or structures
Disadvantages
If head node is lost , linked list is lost
No random access is possible
How to Create a Linked List in
• classPython
Node:
• def __init__(self, data):
• self.data = data # Assigns the given data to the node
• self.next = None # Initialize the next attribute to null
• Create object node
• Node1=node(10)
Inserting a new node at the
beginning of a linked list
• def insertAtBeginning(self, new_data):
• new_node = Node(new_data) # Create a new node
• new_node.next = self.head # Next for new node becomes the current head
• self.head = new_node # Head now points to the new node
• def printList(self):
• temp = self.head # Start from the head of the list
• while temp:
• print(temp.data,end=' ') # Print the data in the current node
• temp = temp.next # Move to the next node
• print() # Ensures the output is followed by a new line
• if __name__ == '__main__':
• # Create a new LinkedList instance(object)
• llist = LinkedList()
• # Insert each letter at the beginning using the method we created
• llist.insertAtBeginning('fox')
• llist.insertAtBeginning('brown')
• llist.insertAtBeginning('quick’)
• llist.insertAtBeginning('the’)
• # Print the list
• llist.printList()
Inserting a new node at the end of a
linked
• def listnew_data):
insertAtEnd(self,
• new_node = Node(new_data) # Create a new node
• if self.head is None:
• self.head = new_node # If the list is empty, make the new node the head return
• last = self.head
• while last.next: # Otherwise, traverse the list to find the last node
• last = last.next # last is initialized with the head of the list
• last.next = new_node # Make the new node the next node of the last node
• if __name__ == '__main__':
• llist = LinkedList()
• llist.insertAtEnd('jumps')
• # Print the list
Deleting a node from the beginning of a linked list
• def deleteFromBeginning(self):
• if self.head is None:
• return "The list is empty" # If the list is empty, return this string
• self.head = self.head.next # Otherwise, remove the head by making the next node
the new head
Deleting a node from the end of
a linked list
• def deleteFromEnd(self):
• if self.head is None:
• return "The list is empty"
• if self.head.next is None:
• self.head = None # If there's only one node, remove the head by making it None
• return
• temp = self.head
• while temp.next.next: # Otherwise, go to the second-last node
• temp = temp.next
• temp.next = None # Remove the last node by setting the next pointer of the
second-last node to None
• Print the list before deletion
• print("List before deletion:")
• llist.printList()
• # Deleting nodes from the beginning and end
• llist.deleteFromBeginning()
• llist.deleteFromEnd()
• Print the list after deletion
• print("List after deletion:")
• llist.printList()
Searching the linked list for a
specific
• def search(self, value):value:
• current = self.head # Start with the head of the list
• position = 0 # Counter to keep track of the position
• while current: # Traverse the list
• if current.data == value: # Compare the list's data to the search value
• return f"Value '{value}' found at position {position}" # Print the value if a match is
found
• current = current.next
• position += 1
• return f"Value '{value}' not found in the list“ # Search for 'quick' and 'lazy' in the list
• print(llist.search('quick')) # Expected to find
• print(llist.search('lazy')) # Expected not to find

You might also like