0% found this document useful (0 votes)
7 views6 pages

A Docxweek10

The document discusses various algorithms and techniques for sorting and finding majority elements in arrays, including merge sort and divide-and-conquer strategies. It details the time complexity of these algorithms, particularly focusing on O(n log n) for merge sort and O(n) for majority element detection. Additionally, it presents methods for counting inversions and optimizing operations using data structures like doubly linked lists.

Uploaded by

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

A Docxweek10

The document discusses various algorithms and techniques for sorting and finding majority elements in arrays, including merge sort and divide-and-conquer strategies. It details the time complexity of these algorithms, particularly focusing on O(n log n) for merge sort and O(n) for majority element detection. Additionally, it presents methods for counting inversions and optimizing operations using data structures like doubly linked lists.

Uploaded by

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

A = [5, 8, 2, 0, 23, 786, −2, 65]

[5, 8, 2, 0] [23, 786, −2, 65]


[5, 8] [2, 0]
[5] [8]
[5, 8]
[2] [0]
[0, 2]
[0, 2, 5, 8]
[23, 786] [−2, 65]
[23] [786]
[23, 786]
[-2] [65]
[-2, 65]
[-2, 23, 65, 786]
[-2, 0, 2, 5, 8, 23, 65, 786]
Q2.
Base case lines 2 and 3: O(1)
Divide step:
Line 5: O(n/2) copy the values into a new list
Line 6: O(n/2)
- The divide step could be done in O(1) if an arraylist is used, by using start
and end bounding indices as parameters to the recursive function.
Line 7: Recursive calls (conquer step) – two recursive calls with half the input
size: 2*T(n/2)

Line 7: Merge step (concatenation): For an arraylist: O(n) to allocate a new


array of the combined capacity and copy all values over. But it could be O(1)
for a doubly linked list – join the tail of first list with the head of 2 nd list.

Recurrence relation:
T(n) = 2*T(n/2) + O(n)

Unrolling:
1st unrolling: T(n) = 2*T(n/2) + O(n)
2nd unrolling:
T(n) = 2*(2*T(n/4) + O(n/2)) + O(n)
T(n) = 2*2*T(n/4) + 2*O(n/2) + O(n)
3rd unrolling:
T(n) = 2*2*(2*T(n/8) + O(n/4)) + 2*O(n/2) + O(n)
T(n) = 2*2*2*T(n/8) + 2*2*O(n/4) + 2*O(n/2) + O(n)
….
Ith unrolling:
T(n) = 2i * T(n/2i) + i * O(n)

Base case when T(n/2i) = T(1) = O(1)


n/2i = 1
i = log2(n)
this is the height of the recursion tree

Total running time once all recursion has finished:


T(n) = 2 log2(n) * T(n/2 log2(n)) + log2(n) * O(n)
T(n) = n * O(1) + O(nlogn)
T(n) = O(nlogn)

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

a) X is a majority element:
Count(x) in the whole > n/2

Assume x is not a majority in either half:


Count(x) in first half ≤ n/4
Count(x) in second half ≤ n/4
Total = sum of both halfs ≤ n/2 which is a contradiction, therefore x must
be a majority in either first or second half:

Count(x) in first half > n/4 OR Count(x) in second half > n/4

b)
Def check_majority(A, x):
count = 0
for e in A:
If x == e:
count += 1
return count > len(A)/2

c) Divide and conquer algorithm


Divide step: divide array in half
Recursively solve each half – we get a solution for each half, which is the
majority element in that half (if it exists).
Merge step: Check both non-null candidate solutions if they are a
majority in the whole array, with part b. If not, return None.

Def find_majority(A):
If len(A) == 1:
Return A[0]
Mid = len(A)/2
Half1 = find_majority(A[0:mid])
Half2 = find_majority(A[mid:len(A)])
If Half1 != None and check_majority(A, Half1):
Return Half1
If Half2 != None and check_majority(A, Half2):
Return Half2
Return None

[2,3,2,3]
d) Same as problem 2

Question 4.
Merge sort removes inversions when a value is selected from the right half.
The number of inversions removed is the number of elements remaining in the
left half

- Keep an additional counter, which is incremented in the merge step and


returned at the end.
- The recursive function now returns 2 values: the sorted array, and the
inversion count.
- The initial value of the inversion count passed to the merge function is
the sum of inversion counts from the recursive calls

def merge_sort(S):
# base case if |S| < 2 then
return S

mid ← ⌊|S|/2⌋
# divide

left ← S[:mid] # doesn’t include S[mid]


right ← S[mid:] # includes S[mid]
# recur
sorted_left, c1 ← merge_sort(left)
sorted_right, c2 ← merge_sort(right)
# conquer
return merge(sorted_left, sorted_right, c1+c2)

def merge(L, R, inv_count):


result ← array of length (|L| + |R|)
l, r ← 0, 0
while l + r < |result| do
index ← l + r
if r ≥ |R| or (l < |L| and L[l] < R[r]) then
result[index] ← L[l]
l←l+1
else
result[index] ← R[r]
r←r+1
inv_count += |L|- l
return result, inv_count

Question 5.
- Start in the centre

Compare the midpoint with its index:


A[mid] > mid: go left else go right
def find_gap(A):
start = 0
end = len(A)
if A[0] != 0:
return 0
while end-start > 1:
mid = int((start+end)/2)
# print(A[start:end], start, mid, end)
if A[mid] > mid:
end = mid
else:
start = mid
return A[start]+1

Time complexity: O(logn)

Question 6.

- Ensure it’s a doubly linked list so values can be removed in O(1)


- Loop through the list two elements at a time. If both are the same value,
discard one
- If both are different values, discard both
- This relies upon the fact that a majority element will be the same as its
neighbour at some point in the list
- Repeat this process, until only 2 candidate values remain – check both
these values in the entire list using Q3b)

Time complexity:
The number of elements in the list after each pass will decrease by at least
half, since at least one value is removed for every 2 values.
Therefore the number of iterations will be:
n + n/2 + n/4 + n/8 + n/16 + …. 2*n = O(n)
ie,
T(n) = T(n/2) + O(n)
Solves to O(n)
On each iteration, O(1) to compare adjacent elements, and O(1) to remove
from the middle of a doubly linked list (we know the node position).

Alternative approach:
- Keep a candidate majority element, initially set to the first value
- Keep a counter initially zero
- Iterate through the list, check if the element is equal to the candidate, if
it is, increase the counter, otherwise decrease the counter. If the
counter falls below 0, set the current element to be the new candidate
and reset counter to 0
- At the end, check if the candidate is a majority in the whole list. If it is,
return it.
[4,4,4,7,3]

You might also like