0% found this document useful (0 votes)
8 views

Sorting and Seaeching Array Tail Recursion

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views

Sorting and Seaeching Array Tail Recursion

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 25

Contents

What are Data Structures? ....................................................................................................... 2


 Data Structures is about how data can be stored in different structures. ....... 2
What is Algorithm? ....................................................................................................................... 2
 Algorithms is about how to solve different problems, often by searching
through and manipulating data structures. ............................................................................. 2
Example .............................................................................................................................................. 3
Selection Sort ................................................................................................................................. 4
Selection Sort Implementation ............................................................................................. 4
Example .............................................................................................................................................. 4
Insertion Sort Implementation ............................................................................................. 5
Example .............................................................................................................................................. 5
Merge Sort Implementation ....................................................................................................... 9
Example ............................................................................................................................................ 10
Step-by-Step Explanation ................................................................................................... 11
Code Walkthrough .............................................................................................................................. 12
Example............................................................................................................................................. 13
Code Explanation ...................................................................................................................... 13
Example Walkthrough ........................................................................................................................ 14
Edge Cases .......................................................................................................................................... 14
Manual Run Through ..................................................................................................................... 15
Binary Search Implementation ............................................................................................. 15
Example ............................................................................................................................................ 16
Code Explanation................................................................................................................................ 16
Example Walkthrough ........................................................................................................................ 17
Algorithm: ........................................................................................................................................... 20
Sum of Diagonals in a 2D Array.......................................................................................................... 23
Tail Recursion ..................................................................................................................................... 24
Characteristics of Tail Recursion ......................................................................................................... 24
Example: Factorial (Tail Recursion) ..................................................................................................... 24
Advantages of Tail Recursion .............................................................................................................. 25
Example in Python .............................................................................................................................. 25

1
What are Data Structures?

 Data Structures is about how data can be stored in different structures.


What is Algorithm?

 Algorithms is about how to solve different problems, often by searching


through and manipulating data structures.

Term Description

Algorithm A set of step-by-step instructions to solve a specific problem.

Data Structure A way of organizing data so it can be used efficiently. Common data
structures include arrays, linked lists, and binary trees.

Time Complexity A measure of the amount of time an algorithm takes to run, depending on
the amount of data the algorithm is working on.

Space Complexity A measure of the amount of memory an algorithm uses, depending on


the amount of data the algorithm is working on.

Big O Notation A mathematical notation that describes the limiting behavior of a function
when the argument tends towards a particular value or infinity. Used in
this tutorial to describe the time complexity of an algorithm.

Recursion A programming technique where a function calls itself.

2
Divide and Conquer A method of solving complex problems by breaking them into smaller,
more manageable sub-problems, solving the sub-problems, and
combining the solutions. Recursion is often used when using this method
in an algorithm.

Brute Force A simple and straight forward way an algorithm can work by simply trying
all possible solutions and then choosing the best one.

Bubble Sort

Bubble Sort is an algorithm that sorts an array from the lowest


value to the highest value.
How it works:
Go through the array, one value at a time.
For each value, compare the value with the next value.
If the value is higher than the next one, swap the values so
that the highest value comes last.
Go through the array as many times as there are values in the
array.
Bubble Sort Implementation:
To implement the Bubble Sort algorithm in a programming language,
we need:
An array with values to sort.
An inner loop that goes through the array and swaps values if
the first value is higher than the next value. This loop must
loop through one less value each time it runs.
An outer loop that controls how many times the inner loop
must run. For an array with n values, this outer loop must
run n-1 times.
Example

my_array = [64, 34, 25, 12, 22, 11, 90, 5]

n = len(my_array)

for i in range(n-1):

3
for j in range(n-i-1):

if my_array[j] > my_array[j+1]:

my_array[j], my_array[j+1] = my_array[j+1], my_array[j]

print("Sorted array:", my_array)

Selection Sort

The Selection Sort algorithm finds the lowest value in an array


and moves it to the front of the array.

How it works:

Go through the array to find the lowest value.


Move the lowest value to the front of the unsorted part of
the array.
Go through the array again as many times as there are values
in the array.

Selection Sort Implementation

To implement the Selection Sort algorithm in a programming


language, we need:

An array with values to sort.


An inner loop that goes through the array, finds the lowest
value, and moves it to the front of the array. This loop must
loop through one less value each time it runs.
An outer loop that controls how many times the inner loop
must run. For an array with n values, this outer loop must
run n−1 times.

Example

my_array = [64, 34, 25, 5, 22, 11, 90, 12]

n = len(my_array)

for i in range(n-1):

min_index = i

for j in range(i+1, n):

4
if my_array[j] < my_array[min_index]:

min_index = j

min_value = my_array.pop(min_index)

my_array.insert(i, min_value)

print("Sorted array:", my_array)

Insertion Sort

The Insertion Sort algorithm uses one part of the array to hold
the sorted values and the other part of the array to hold values
that are not sorted yet.

How it works:

Take the first value from the unsorted part of the array.
Move the value into the correct place in the sorted part of
the array.
Go through the unsorted part of the array again as many times
as there are values.

Insertion Sort Implementation

To implement the Insertion Sort algorithm in a programming


language, we need:

An array with values to sort.


An outer loop that picks a value to be sorted. For an array
with n values, this outer loop skips the first value, and
must run n−1 times.
An inner loop that goes through the sorted part of the array,
to find where to insert the value. If the value to be sorted
is at index i, the sorted part of the array starts at
index 0 and ends at index i−1.

Example

my_array = [64, 34, 25, 12, 22, 11, 90, 5]

n = len(my_array)

for i in range(1,n):

5
insert_index = i

current_value = my_array.pop(i)

for j in range(i-1, -1, -1):

if my_array[j] > current_value:

insert_index = j

my_array.insert(insert_index, current_value)

print("Sorted array:", my_array)

Iteration 1 (i=1):

• current_value = 34, insert_index = 1


• Compare 64 (index 0) > 34. Update insert_index = 0.
• Insert 34 at index 0.
• my_array = [34, 64, 25, 12, 22, 11, 90, 5]

Iteration 2 (i=2):

• current_value = 25, insert_index = 2


• Compare 64 (index 1) > 25. Update insert_index = 1.
• Compare 34 (index 0) > 25. Update insert_index = 0.
• Insert 25 at index 0.
• my_array = [25, 34, 64, 12, 22, 11, 90, 5]

Iteration 3 (i=3):

• current_value = 12, insert_index = 3


• Compare 64 > 12, 34 > 12, and 25 > 12. Update insert_index = 0.
• Insert 12 at index 0.
• my_array = [12, 25, 34, 64, 22, 11, 90, 5]

Iteration 4 (i=4):

• current_value = 22, insert_index = 4


• Compare 64 > 22, 34 > 22, and 25 > 22. Update insert_index = 1.
• Insert 22 at index 1.
• my_array = [12, 22, 25, 34, 64, 11, 90, 5]

Iteration 5 (i=5):

• current_value = 11, insert_index = 5


• Compare 64 > 11, 34 > 11, 25 > 11, 22 > 11, and 12 > 11. Update insert_index = 0.

6
• Insert 11 at index 0.
• my_array = [11, 12, 22, 25, 34, 64, 90, 5]

Iteration 6 (i=6):

• current_value = 90, no changes needed (it's already in the correct position).


• my_array = [11, 12, 22, 25, 34, 64, 90, 5]

Iteration 7 (i=7):

• current_value = 5, insert_index = 7
• Compare all elements and update insert_index = 0.
• Insert 5 at index 0.
• my_array = [5, 11, 12, 22, 25, 34, 64, 90]

Merge Sort

The Merge Sort algorithm is a divide-and-conquer algorithm that


sorts an array by first breaking it down into smaller arrays, and
then building the array back together the correct way so that it
is sorted.
Divide: The algorithm starts with breaking up the array into
smaller and smaller pieces until one such sub-array only consists
of one element.
Conquer: The algorithm merges the small pieces of the array back
together by putting the lowest values first, resulting in a sorted
array.

How it works:

Divide the unsorted array into two sub-arrays, half the size
of the original.
Continue to divide the sub-arrays as long as the current piece
of the array has more than one element.
Merge two sub-arrays together by always putting the lowest
value first.
Keep merging until there are no sub-arrays left

7
Step 1: We start with an unsorted array, and we know that it splits
in half until the sub-arrays only consist of one element. The Merge
Sort function calls itself two times, once for each half of the
array. That means that the first sub-array will split into the
smallest pieces first.

[ 12, 8, 9, 3, 11, 5, 4]
[ 12, 8, 9] [ 3, 11, 5, 4]
[ 12] [ 8, 9] [ 3, 11, 5, 4]
[ 12] [ 8] [ 9] [ 3, 11, 5, 4]

Step 2: The splitting of the first sub-array is finished, and


now it is time to merge. 8 and 9 are the first two elements to
be merged. 8 is the lowest value, so that comes before 9 in the
first merged sub-array.

[ 12] [ 8, 9] [ 3, 11, 5, 4]

Step 3: The next sub-arrays to be merged is [ 12] and [ 8, 9].


Values in both arrays are compared from the start. 8 is lower than
12, so 8 comes first, and 9 is also lower than 12.

[ 8, 9, 12] [ 3, 11, 5, 4]

Step 4: Now the second big sub-array is split recursively.

[ 8, 9, 12] [ 3, 11, 5, 4]
[ 8, 9, 12] [ 3, 11] [ 5, 4]
[ 8, 9, 12] [ 3] [ 11] [ 5, 4]

Step 5: 3 and 11 are merged back together in the same order as


they are shown because 3 is lower than 11.

[ 8, 9, 12] [ 3, 11] [ 5, 4]

Step 6: Sub-array with values 5 and 4 is split, then merged so


that 4 comes before 5.

[ 8, 9, 12] [ 3, 11] [ 5] [ 4]
[ 8, 9, 12] [ 3, 11] [ 4, 5]

Step 7: The two sub-arrays on the right are merged. Comparisons


are done to create elements in the new merged array:

1. 3 is lower than 4
2. 4 is lower than 11

8
3. 5 is lower than 11
4. 11 is the last remaining value

[ 8, 9, 12] [ 3, 4, 5, 11]

Step 8: The two last remaining sub-arrays are merged. Let's look
at how the comparisons are done in more detail to create the new
merged and finished sorted array:

3 is lower than 8:

Before [ 8, 9, 12] [ 3, 4, 5, 11]


After: [ 3, 8, 9, 12] [ 4, 5, 11]

Step 9: 4 is lower than 8:

Before [ 3, 8, 9, 12] [ 4, 5, 11]


After: [ 3, 4, 8, 9, 12] [ 5, 11]

Step 10: 5 is lower than 8:

Before [ 3, 4, 8, 9, 12] [ 5, 11]


After: [ 3, 4, 5, 8, 9, 12] [ 11]

Step 11: 8 and 9 are lower than 11:

Before [ 3, 4, 5, 8, 9, 12] [ 11]


After: [ 3, 4, 5, 8, 9, 12] [ 11]

Step 12: 11 is lower than 12:

Before [ 3, 4, 5, 8, 9, 12] [ 11]


After: [ 3, 4, 5, 8, 9, 11, 12]

The sorting is finished!

Merge Sort Implementation

To implement the Merge Sort algorithm we need:

An array with values that needs to be sorted.


A function that takes an array, splits it in two, and calls
itself with each half of that array so that the arrays are
split again and again recursively, until a sub-array only
consist of one value.

9
Another function that merges the sub-arrays back together in
a sorted way.

Example

def mergeSort(arr):

if len(arr) <= 1:

return arr

mid = len(arr) // 2

leftHalf = arr[:mid]

rightHalf = arr[mid:]

sortedLeft = mergeSort(leftHalf)

sortedRight = mergeSort(rightHalf)

return merge(sortedLeft, sortedRight)

def merge(left, right):

result = []

i = j = 0

while i < len(left) and j < len(right):

if left[i] < right[j]:

result.append(left[i])

i += 1

else:

result.append(right[j])

j += 1

result.extend(left[i:])

10
result.extend(right[j:])

return result

unsortedArr = [3, 7, 6, -10, 15, 23.5, 55, -13]

sortedArr = mergeSort(unsortedArr)

print("Sorted array:", sortedArr)

Step-by-Step Explanation
unsortedArr = [3, 7, 6, -10, 15, 23.5, 55, -13]
1. Splitting the Array

• Initially, mergeSort splits the array into two halves:

leftHalf = [3, 7, 6, -10]


rightHalf = [15, 23.5, 55, -13]

• It then recursively splits these halves:


o Left: [3, 7] and [6, -10]
o Right: [15, 23.5] and [55, -13]
• This process continues until each sub-array contains only one element:
o [3], [7], [6], [-10], [15], [23.5], [55], [-13]

2. Merging the Sub-Arrays

Merging starts from the smallest sub-arrays:

• Merge [3] and [7] into [3, 7].


• Merge [6] and [-10] into [-10, 6].
• Merge [15] and [23.5] into [15, 23.5].
• Merge [55] and [-13] into [-13, 55].

3. Continue Merging

• Merge [3, 7] and [-10, 6] into [-10, 3, 6, 7].


• Merge [15, 23.5] and [-13, 55] into [-13, 15, 23.5, 55].

4. Final Merge

• Merge [-10, 3, 6, 7] and [-13, 15, 23.5, 55] into:

11
[-13, -10, 3, 6, 7, 15, 23.5, 55]

Code Walkthrough

Input:

unsortedArr = [3, 7, 6, -10, 15, 23.5, 55, -13]

Execution:

1. The array is split recursively into sub-arrays of length 1.


2. Pairs of sub-arrays are merged using the merge function.

Output:

Sorted array: [-13, -10, 3, 6, 7, 15, 23.5, 55]

Linear Search

The Linear Search algorithm searches through an array and returns


the index of the value it searches for.

How it works:

Go through the array value by value from the start.


Compare each value to check if it is equal to the value we
are looking for.
If the value is found, return the index of that value.
If the end of the array is reached and the value is not found,
return -1 to indicate that the value was not found.

Linear Search Implementation


To implement the Linear Search algorithm we need:
An array with values to search through.
A target value to search for.
A loop that goes through the array from start to end.
An if-statement that compares the current value with the
target value, and returns the current index if the target
value is found.

12
After the loop, return -1, because at this point we know the
target value has not been found.

Example
def linearSearch(arr, targetVal):

for i in range(len(arr)):

if arr[i] == targetVal:

return i

return -1

arr = [3, 7, 2, 9, 5]

targetVal = 9

result = linearSearch(arr, targetVal)

if result != -1:

print("Value",targetVal,"found at index",result)

else:

print("Value",targetVal,"not found")

Code Explanation
1. Function Definition
def linearSearch(arr, targetVal):

• Accepts two parameters:


o arr: The array to search in.
o targetVal: The value to search for.

2. Traversal with a For Loop


for i in range(len(arr)):
if arr[i] == targetVal:
return i

• range(len(arr)) ensures all indices of the array are covered.


• For each index i, it checks if arr[i] equals targetVal.

3. Return -1 if Not Found


return -1

13
• If the loop completes without finding the targetVal, the function returns -1, indicating that
the value is not present.

Example Walkthrough
Input
arr = [3, 7, 2, 9, 5]
targetVal = 9
Execution Steps

1. Start the loop:


o Check arr[0] (value = 3): not equal to 9.
o Check arr[1] (value = 7): not equal to 9.
o Check arr[2] (value = 2): not equal to 9.
o Check arr[3] (value = 9): equal to 9.
2. The function returns 3 (the index of the target value).

Output
Value 9 found at index 3

Edge Cases

1. Value Not Found:

arr = [3, 7, 2, 9, 5]
targetVal = 10
Output
Value 10 not found

Binary Search

Binary Search works by repeatedly dividing a sorted array into two


halves and comparing the target value with the middle element. The
Binary Search algorithm searches through an array and returns the
index of the value it searches for.

How it works:

Check the value in the center of the array.


If the target value is lower, search the left half of the
array. If the target value is higher, search the right half.
Continue step 1 and 2 for the new reduced part of the array
until the target value is found or until the search area is
empty.

14
If the value is found, return the target value index. If the
target value is not found, return -1.

Manual Run Through

Let's try to do the searching manually, just to get an even


better understanding of how Binary Search works before actually
implementing it in a programming language. We will search for
value 11.
Step 1: We start with an array.
[ 2, 3, 7, 7, 11, 15, 25]

Step 2: The value in the middle of the array at index 3, is it


equal to 11?
[ 2, 3, 7, 7, 11, 15, 25]

Step 3: 7 is less than 11, so we must search for 11 to the right


of index 3. The values to the right of index 3 are [ 11, 15,
25]. The next value to check is the middle value 15, at index
5.
[ 2, 3, 7, 7, 11, 15, 25]

Step 4: 15 is higher than 11, so we must search to the left of


index 5. We have already checked index 0-3, so index 4 is only
value left to check.
[ 2, 3, 7, 7, 11, 15, 25]
We have found it!
Value 11 is found at index 4.
Returning index position 4.

Binary Search Implementation

To implement the Binary Search algorithm we need:

An array with values to search through.


A target value to search for.
A loop that runs as long as left index is less than, or equal
to, the right index.
An if-statement that compares the middle value with the target
value, and returns the index if the target value is found.
An if-statement that checks if the target value is less than,
or larger than, the middle value, and updates the "left" or
"right" variables to narrow down the search area.
After the loop, return -1, because at this point we know the
target value has not been found.

15
Example

def binarySearch(arr, targetVal):

left = 0

right = len(arr) - 1

while left <= right:

mid = (left + right) // 2

if arr[mid] == targetVal:

return mid

if arr[mid] < targetVal:

left = mid + 1

else:

right = mid - 1

return -1

myArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]

myTarget = 15

result = binarySearch(myArray, myTarget)

if result != -1:

print("Value",myTarget,"found at index", result)

else:

print("Target not found in array.")

Code Explanation
1. Function Definition
def binarySearch(arr, targetVal):
left = 0
right = len(arr) - 1

• arr is the sorted input array.


• targetVal is the value to search for.

16
• left and right pointers mark the bounds of the current search range.

2. While Loop
while left <= right:

• The loop continues as long as the search range is valid (left ≤ right).

3. Calculate Midpoint
mid = (left + right) // 2

• mid is the index of the middle element of the current range.

4. Check the Midpoint


if arr[mid] == targetVal:
return mid

• If the middle element matches the target, return its index.

5. Adjust the Range


if arr[mid] < targetVal:
left = mid + 1
else:
right = mid - 1

• If the target is greater than arr[mid], search the right half.


• Otherwise, search the left half.

6. Return -1 if Not Found


return -1

• If the loop exits without finding the target, return -1.

Example Walkthrough
Input
myArray = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
myTarget = 15
Execution Steps

1. Initial State:
o left = 0, right = 9 (indices of the first and last elements).
o Calculate mid = (0 + 9) // 2 = 4. Check arr[4] = 9.
2. First Comparison:
o arr[4] < 15, so adjust the range: left = mid + 1 = 5.
3. Second Iteration:
o Calculate mid = (5 + 9) // 2 = 7. Check arr[7] = 15.
4. Match Found:
o arr[7] == 15. Return 7.

17
Output
Value 15 found at index 7

Recursion
Recusion is the process where a function calls itself until the
base condition or termination condition occurs.

Array 1D & 2D
Q. Explain the difference between a list and an array in Python. Which is more
flexible, and why?

Difference between a list and an array in Python:

List: Can store elements of mixed data types (e.g., [1, "two",
3.0]). It is flexible but slower for numerical operations.
Array: Requires all elements to be of the same data type
(using the array module or numpy), which is faster for
numerical computations.
More flexible: Lists, because of their ability to hold mixed
data types.

Q. How do you access the last element of a 1D array in Python?


Provide an example.

Accessing the last element of a 1D array:

arr = [10, 20, 30, 40]

print(arr[-1]) # Output: 40

Q. Describe how slicing works in 1D arrays with an example.


Slicing in 1D arrays
arr = [1, 2, 3, 4, 5]
print(arr[1:4]) # Output: [2, 3, 4] (elements from index 1 to 3)

Q. Explain the time complexity of searching for an element in an unsorted


array vs. a sorted array.

Time complexity of searching:

• Unsorted array: O(n) (Linear Search)


• Sorted array: O(log n) (Binary Search)

18
Q. How can you reverse a 1D array in Python? Mention two methods.
Method 1: Using slicing:
arr = [1, 2, 3, 4]
print(arr[::-1]) # Output: [4, 3, 2, 1]
Method 2: Using reverse():
arr.reverse()

Problem-Solving Questions:
Q. Write a Python program to find the second largest number in a given array.

Find the second largest number:

def second_largest(arr):
unique_arr = list(set(arr)) # Remove duplicates
unique_arr.sort() # Sort in ascending order
return unique_arr[-2] if len(unique_arr) >= 2 else None

print(second_largest([10, 20, 4, 45, 99, 45])) # Output: 45

Q. Implement a function to rotate a 1D array k steps to the right. Example:


Input: [1, 2, 3, 4, 5], k = 2
Output: [4, 5, 1, 2, 3]

Rotate array k steps to the right:

def rotate_array(arr, k):


k %= len(arr) # To handle cases where k > len(arr)
return arr[-k:] + arr[:-k]

print(rotate_array([1, 2, 3, 4, 5], 2)) # Output: [4, 5, 1, 2, 3]

Q. Write a program to find all pairs of numbers in an array that sum up to a


given target. Example:
Input: [2, 4, 6, 3, 7, 8], Target = 10
Output: [(4, 6), (3, 7)]

Find all pairs with a given sum:

def find_pairs(arr, target):


pairs = []
seen = set()
for num in arr:
diff = target - num
if diff in seen:
pairs.append((diff, num))
seen.add(num)
return pairs

print(find_pairs([2, 4, 6, 3, 7, 8], 10)) # Output: [(4, 6), (3, 7)]

19
Q. Create a program to count the frequency of each element in a 1D array.

Count frequency of elements:

from collections import Counter

def count_frequency(arr):
return Counter(arr)

print(count_frequency([1, 2, 2, 3, 4, 4, 4]))
# Output: Counter({4: 3, 2: 2, 1: 1, 3: 1})

Q. Given an array of integers, write a function to rearrange the array so that


all negative numbers appear before positive numbers. Example:
Input: [1, -2, 3, -4, 5]
Output: [-2, -4, 1, 3, 5]

Rearrange negatives and positives:

def rearrange(arr):
return [x for x in arr if x < 0] + [x for x in arr if x >= 0]

print(rearrange([1, -2, 3, -4, 5])) # Output: [-2, -4, 1, 3, 5]

2D Array Questions
Theory Questions:
Q. Explain how a 2D array is represented in memory.

2D array representation:
A 2D array is represented as a list of lists in Python, where each
inner list represents a row. Example:

arr = [[1, 2], [3, 4]]

Q. How can you access a specific element in a 2D array? Provide an example.

Accessing elements:

arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]


print(arr[1][2]) # Output: 6 (row 1, column 2)

Q. Compare and contrast row-major and column-major order in the context of 2D


arrays.
Row-major vs Column-major:
Row-major: Rows are stored sequentially in memory (Python uses this).
Column-major: Columns are stored sequentially (used in some languages like
Fortran).

Q. Describe the process of transposing a 2D array. Write the algorithm.


Algorithm:
def transpose(matrix):
# Step 1: Initialize an empty matrix with swapped dimensions

20
rows, cols = len(matrix), len(matrix[0])
transposed = [[0 for _ in range(rows)] for _ in range(cols)]

# Step 2: Fill the transposed matrix


for i in range(rows):
for j in range(cols):
transposed[j][i] = matrix[i][j]

return transposed

Q. What are some real-world applications of 2D arrays? Provide at least two


examples.

Applications of 2D arrays:

• Game boards (e.g., Tic-tac-toe, Chess).


• Image representation (pixels).

Problem-Solving Questions:

Write a Python program to compute the transpose of a 2D array.

Transpose of a 2D array:

def transpose(matrix):
return [list(row) for row in zip(*matrix)]

print(transpose([[1, 2, 3], [4, 5, 6]]))


# Output: [[1, 4], [2, 5], [3, 6]]

Implement a function to find the row with the maximum sum in a 2D


array. Example:

Input: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Output: Row 3 has the maximum sum = 24\

Row with maximum sum:

def max_sum_row(matrix):
max_sum = 0
max_row = -1
for i, row in enumerate(matrix):
row_sum = sum(row)
if row_sum > max_sum:

21
max_sum = row_sum
max_row = i + 1
return max_row, max_sum

print(max_sum_row([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))


# Output: (3, 24)

Write a program to search for a specific value in a 2D array and


return its position. If the value is not found, return (-1, -1).

Search for a value:

def search_value(matrix, target):


for i, row in enumerate(matrix):
for j, value in enumerate(row):
if value == target:
return (i, j)
return (-1, -1)

print(search_value([[1, 2, 3], [4, 5, 6], [7, 8, 9]], 5))


# Output: (1, 1)

Implement matrix multiplication for two 2D arrays in Python.

Matrix multiplication:

def multiply_matrices(A, B):


result = [[sum(a * b for a, b in zip(A_row, B_col)) for B_col in zip(*B)]
for A_row in A]
return result

A = [[1, 2], [3, 4]]


B = [[5, 6], [7, 8]]
print(multiply_matrices(A, B))
# Output: [[19, 22], [43, 50]]

Given a 2D array, write a Python function to print its boundary


elements (elements in the outermost rows and columns).

Print boundary elements:

def boundary_elements(matrix):
rows, cols = len(matrix), len(matrix[0])
if rows == 1: # Single row
return matrix[0]
if cols == 1: # Single column

22
return [row[0] for row in matrix]
top = matrix[0]
bottom = matrix[-1]
left = [matrix[i][0] for i in range(1, rows - 1)]
right = [matrix[i][-1] for i in range(1, rows - 1)]
return top + right + bottom[::-1] + left[::-1]

print(boundary_elements([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))


# Output: [1, 2, 3, 6, 9, 8, 7, 4]

Sum of Diagonals in a 2D Array


Problem:

Calculate the sum of the primary diagonal and secondary diagonal.

Solution:
def sum_of_diagonals(matrix):
n = len(matrix) # Assuming the matrix is square
primary_sum = sum(matrix[i][i] for i in range(n))
secondary_sum = sum(matrix[i][n - i - 1] for i in range(n))
return primary_sum, secondary_sum

# Example
matrix = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]

primary, secondary = sum_of_diagonals(matrix)


print(f"Primary diagonal: {primary}, Secondary diagonal: {secondary}")
# Output: Primary diagonal: 15, Secondary diagonal: 15
Explanation:

1. Primary diagonal: Elements where the row index equals the column index (matrix[i][i]).
o For example: [1, 5, 9] from the given matrix.
2. Secondary diagonal: Elements where the row index and column index add up to n-1
(matrix[i][n - i - 1]).
o For example: [3, 5, 7] from the given matrix.

23
Tail Recursion

Tail recursion is a type of recursion where the recursive call is the last operation performed in
the function before it returns a value. In tail-recursive functions, there are no pending operations
left after the recursive call. This allows some programming languages (like Python's competitors
such as Scheme, or optimized languages like Java or C++) to optimize the recursive calls and
avoid consuming additional stack space for each recursive call.

Characteristics of Tail Recursion

1. Last Call Optimization (TCO):


Some languages optimize tail-recursive calls to reuse the current function's stack frame
instead of creating a new one. This avoids stack overflow issues for deep recursions.
2. No Pending Work:
After the recursive call, no operations (like addition, multiplication, etc.) are performed.
The result is returned directly.
3. State Passing:
Any intermediate state or result is usually passed as arguments to the function.

Example: Factorial (Tail Recursion)


Traditional Recursion
def factorial(n):
if n == 0:
return 1
return n * factorial(n - 1) # Multiplication is pending

• Here, the recursive call is followed by a multiplication operation, making it non-tail-recursive.

Tail-Recursive Factorial
def factorial_tail_recursive(n, accumulator=1):
if n == 0:
return accumulator # Final result directly returned
return factorial_tail_recursive(n - 1, n * accumulator) # Recursive call
is the last operation

• The accumulator stores the intermediate result, making it a tail-recursive function.

24
Advantages of Tail Recursion

1. Memory Efficiency: Reduces the function call stack, as some compilers/languages optimize tail
recursion to iterative form.
2. Prevents Stack Overflow: Tail-recursive functions can handle deep recursion without exhausting
stack space.

Example in Python

Although Python does not perform tail-call optimization, you can still write tail-recursive
functions:

Fibonacci Series (Tail Recursive)


def fibonacci_tail_recursive(n, a=0, b=1):
if n == 0:
return a
if n == 1:
return b
return fibonacci_tail_recursive(n - 1, b, a + b)

# Test
print(fibonacci_tail_recursive(10)) # Output: 55

25

You might also like