Recursive MergeSort of Two Arrays - python

I am challenging myself to write code by hand for the purpose of interviews and I created a sub-par implementation of a recursive MergeSort.
The concept is basically taking two lists "alist" and "blist", and combining/sorting them into list "clist". The lists are assumed sorted and the same size prior to combination.
How can I make the original code (which I know does not work properly) reflect the model code (working as intended) with the fewest amount of changes possible? Knowing the degree of my errors will help me greatly.
Original Code:
alist = [1,5,8,9]
blist = [2,4,7,10]
clist = []
temp = []
def MergeSort(alist,blist):
if len(alist) > 1:
midpoint = len(alist)//2
MergeSort(alist[midpoint:],alist[:midpoint])
if len(blist) > 1:
midpoint = len(blist)//2
MergeSort(blist[midpoint:],blist[:midpoint])
if alist[0] < blist[0]:
temp[0] = alist[0]
alist[0] = blist[0]
blist[0] = temp[0]
MergeSort(alist,blist)
else:
alist[len(alist)] = blist[len(blist)-1]
MergeSort(alist,blist)
if blist[0] == None:
return alist
clist = MergeSort(alist,blist)
print(clist)
Model Code:
alist = [1,5,8,9]
blist = [2,4,7,10]
def MergeSort(alist, blist):
clist = []
if alist == [] and blist != []:
return clist + blist
if alist != [] and blist != []:
if alist[0] <= blist[0]:
clist.append(alist[0])
clist = clist + MergeSort(alist[1:], blist)
if alist[0] > blist[0]:
clist.append(blist[0])
clist = clist + MergeSort(alist, blist[1:])
return clist
print(MergeSort(alist,blist))

There are two part to the algorithm
Merge Sort : Sort a list
Merge : Merge two already sorted lists into a single sorted list
Merge is missing in your code.
Algorithm:
MergeSort(A, B)
1.1 MergeSort(first_half_of_A, second_half_A)
// Now first_half_of_A and second_half_A are in sorted order independently
1.2 Merge(first_half_of_A, first_half_of_A)
// Now A is fully sorted
2.1 MergeSort(first_half_of_B, second_half_B)
2.2 Merge(first_half_of_B, second_half_B)
// Now B is fully sorted
3. Merge(A, B)
Merge(A,B) is simple, since A and B are already sorted scan thought them picking the smallest element each time.
def merge(alist,blist):
temp = list()
i = 0
j = 0
while i < len(alist) and j < len(blist) :
if alist[i] < blist[j] :
temp.append(alist[i])
i += 1
else:
temp.append(blist[j])
j += 1
while i < len(alist):
temp.append(alist[i])
i += 1
while j < len(blist):
temp.append(blist[j])
j += 1
return temp
# Test case
assert merge([1,5,8,9], [1,2,3,4]) == [1, 1, 2, 3, 4, 5, 8, 9]
Now finally the MergeSort
def MergeSort(alist,blist):
if len(alist) > 1:
midpoint = len(alist)//2
MergeSort(alist[midpoint:],alist[:midpoint])
alist[:] = merge(alist[midpoint:], alist[:midpoint])
if len(blist) > 1:
midpoint = len(blist)//2
MergeSort(blist[midpoint:],blist[:midpoint])
blist[:] = merge(blist[midpoint:], blist[:midpoint])
return merge(alist, blist)
# Test Case
assert MergeSort([1,5,8,9], [2,4,7,10]) == [1, 2, 4, 5, 7, 8, 9, 10]
# Testing
import numpy as np
for i in range(1000):
a = np.random.rand(100)
b = np.random.rand(100)
c = np.append(a,b)
assert np.sum(MergeSort(a,b)-np.sort(c)) == 0

Related

Find time needed for vertices to stop disappearing

I am given undirected graph that consists of N vertices numbered from 0 to N-1 connected with M edges.The graph is described by two arrays, A and B both of length M. A pair ([A[k],B[k]) describes edge between A[k] and B[k] for k from 0 to M-1.
Each second every vertex with at most 1 edge connected to disappears.Every edge which is connected to one of disappearing vertices also disappears.
After how many seconds will the vertices stop disappearing.
For N=7, ((0,1),(1,2),(2,0),(1,4),(4,5),(4,6)) answer should be 2.
def solution(N,A,B):
d2 = dict.fromkeys(range(N), 0)
count = 0
arr = []
for i in range(len(A)):
arr.append((A[i],B[i]))
while True:
for i in range(len(A)+1):
for c in arr:
if i in c:
d2[i] += 1
arr1 = arr
for i in range(len(A)+1):
if d2[i] <= 1:
arr = list(filter(lambda x: x[1] != i and x[0] != i, arr))
if len(arr) == len(arr1):
return count + 1
break
count += 1
Here is my code.For this test case (4, [0, 1, 2, 3], [1, 2, 3, 0]) it outputs Keyerror: 4. Can you help me to solve the problem.Thank you.
Try this (I inserted a couple of print() to let you see what's going on):
def solution(N, A, B):
linked = defaultdict(list)
for i in range(len(A)):
linked[A[i]].append(B[i])
linked[B[i]].append(A[i])
changed = True
seconds = 0
removing = []
while changed:
changed = False
seconds += 1
print(f'second {seconds}')
for i in list(linked.keys())[:]:
if len(linked[i]) > 1:
continue
if len(linked[i]) == 1:
removing.append((linked[i][0],i))
del linked[i]
changed = True
for i,j in removing:
print(f'remove {j} from {i}')
linked[i].remove(j)
removing = []
return seconds - 1

Finding Maximum non-negative Subarray in python

I've tried to find the sub-array(s) from a given which contain elements of maximum sum than any other sub array.
Below function has parameter as input a and the output needs to be returned. There can be more than one subarray as their maximum sum can be equal. The code did not seem to be working as expected.
def max_sum_subarray(a):
N, sub_sum, max_sum, subArrays = len(a), 0, 0, {}
p,q=0,0 #starting and ending indices of a max sub arr
for i in range(N):
q=i
sub_sum+=a[i]
if(a[i]<0):
q-=1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
sub_sum=0
p=i+1
if(sub_sum>=max_sum):
if(sub_sum>max_sum):
subArrays.clear()
subArrays[sub_sum]=[(p,q)]
else:
subArrays[sub_sum].append((p,q))
return(subArrays[p:q+1])
When I tried to run for input
a=[ 1, 2, 5, -7, 2, 5 ]
Expected output is [1, 2, 5] but it gave [2, 5] instead. Can anyone please post the solution in python?
It seems like you making this harder than necessary. You can just keep track of max array seen to far and the current one you're pushing into -- you don't really need to care about anything else. When you hit a negative (or the end of the array) decide if the current should be the new max:
def maxSub(a):
max_so_far = []
max_sum = 0
cur = []
for n in a:
if n >= 0:
cur.append(n)
else:
cur_sum = sum(cur)
if cur_sum > max_sum:
max_sum = cur_sum
max_so_far = cur
cur = []
return max([max_so_far, cur], key = sum)
a=[ 1, 2, 5, -7, 2, 5 ]
maxSub(a)
# [1, 2, 5]
Of course itertools.groupby makes this a one-liner:
from itertools import groupby
a=[ 1, 2, 5, -7, 2, 5 ]
max([list(g) for k,g in groupby(a, key=lambda x: x>0) if k == True], key=sum)
For the following conditions:
NOTE 1: If there is a tie, then compare with segment’s length and
return segment which has maximum length
NOTE 2: If there is still a tie, then return the segment with minimum
starting index
Here is my working code in python:
def check(max_arr,curr):
if sum(curr) > sum(max_arr):
max_arr = curr
elif sum(curr) == sum(max_arr):
if len(curr) > len(max_arr):
max_arr = curr
elif len(curr) == len(max_arr):
if max_arr and (curr[0] > max_arr[0]):
max_arr = curr
return max_arr
def maxset(A):
curr = []
max_arr = []
for i in A:
if i >= 0:
curr.append(i)
else:
max_arr = check(max_arr,curr)
curr = []
max_arr = check(max_arr,curr)
return max_arr

How do I get smallest postitive integer not present in the array

I am trying to find out smallest positive number not present in the list a
def smallitem(a):
a = sorted(set(a))
lst = []
for item in a:
item + = 1
if item not in a:
lst.append(item)
continue
mst = []
for item in lst:
if item < 1:
item += 1
if item not in a:
mst.append(item)
continue
n = 0
if mst:
n = min(mst)
return n or min(lst)
I think I have got the solution but it doesnt look correct to me the way I have done it.
for example:
smallitem([-1, -3]) # 1
smallitem([1,3,6,4,1,2, 87]) # 5
You can convert the list to a set and then keep incrementing a positive integer from 1 until it is not found in the set:
def smallitem(a):
set_a = set(a)
i = 1
while i in set_a:
i += 1
return i
Perhaps there's a lighter way do this.
The time complexity is always O(n).
def small_item(a):
s = set(a)
for i in range(1, max(s)):
if i not in s:
return i
return max(max(s) + 1, 1)
print small_item([1,3,6,4,1,2, 87])
print small_item([-1, -3])
Here's anther way to do this:
def smallitem(l):
l = list(set(sorted(l)))
x = l[0]
for i in l:
if i != x and x >= 1:return x
x += 1
return 1
Testing:
>>> smallitem([-3, -1])
1
>>> smallitem([1, 3, 6, 4, 1, 2, 87])
5
>>>

Python Quicksort implementation

I tried to implement the recursive quicksort in Python, but it doesn't work. I know that there is the problem that the array doesn't get sorted because the pivot is always higher than i, which results in the problem that i is always equals to m.
def partition(array):
pivot = array[-1]
m = 0
for i in range(len(array) - 1):
if array[i] < pivot:
array[i], array[m] = array[m], array[i]
m += 1
else:
continue
array[m], array[len(array)-1] = array[len(array)-1], array[m]
return m
def quicksort(array):
if len(array) > 1:
m = partition(array)
quicksort(array[:m])
quicksort(array[m+1:])
return array
def main():
testarray = [3,6,2,4,5,1,9,8,7,10,14]
print(quicksort(testarray))
if __name__ == '__main__':
main()
Two things. Firstly, you forgot to return array when it's of length 1, and secondly you aren't actually modifying array before returning. This will work.
def quicksort(array):
if len(array) > 1:
m = partition(array)
# return the concatenation of the two sorted arrays
return quicksort(array[:m]) + quicksort(array[m:])
else:
return array
For those looking for an iterative/non-recursive version of Quicksort, here's an implementation I came up with in Python:
from random import randint
def default_comparator_fn(a, b):
return -1 if a < b else (1 if a > b else 0)
def reverse_comparator_fn(a, b):
return default_comparator_fn(b, a)
def quick_sort(A, comparator_fn=default_comparator_fn):
n = len(A)
if n < 2:
# The list has only 1 element or does not have any.
return A
# There are at least 2 elements.
partitions = [[0, n - 1]] # [[start, end]]
while len(partitions):
partition = partitions.pop()
start = partition[0]
end = partition[1]
pivot_index = randint(start, end)
pivot = A[pivot_index]
A[pivot_index], A[start] = A[start], A[pivot_index]
breakpoint_index = start
k = start + 1
m = end
while k <= m:
res = comparator_fn(A[k], pivot)
if res < 0:
breakpoint_index = k
else:
while m > k:
res = comparator_fn(A[m], pivot)
if res < 0:
breakpoint_index = k
A[m], A[k] = A[k], A[m]
m -= 1
break
m -= 1
k += 1
A[start], A[breakpoint_index] = A[breakpoint_index], A[start]
if start < breakpoint_index - 1:
partitions.append([start, breakpoint_index - 1])
if breakpoint_index + 1 < end:
partitions.append([breakpoint_index + 1, end])
return A
# Example:
A = [4, 2, 5, 1, 3]
quick_sort(A) # Sort in ascending order ([1, 2, 3, 4, 5]).
quick_sort(A, reverse_comparator_fn) # Sort in descending order ([5, 4, 3, 2, 1]).
This implementation of Quicksort accepts an optional custom comparator function which defaults to a comparator which compares the elements of the list in ascending order.

Get the absolute difference between elements of a circular array

Let's say I have a array like l = [1, 3, 4, 5, 6, 8]
where the nth element represents the distance between the nth and n+1th object.
I want to find the distance between any two objects, and I used this code for this:
def dis(l_list, index1, index2, mylist):
m = mylist.index(index1)
n = mylist.index(index2)
i=0
j=0
if n > m:
while n >= m:
i = i + mylist[m]
m = m + 1
elif n < m:
while n <= m:
i = i + mylist[n]
n = n + 1
else:
return(0)
j = mylist[n] % l_mylist
print(abs(i - j))
l_mylist = input()
l_mylist = int(l_mylist)
mylist = []
mylist = list(map(int, input().split()))
i,j = input().split()
i, j=int(i), int(j)
dis(l_mylist, i, j, mylist)
but I am still getting the wrong output. Can anyone please point out where I am wrong?
If you want to sum around a potentially circular list. You can use a collections.deque() to rotate the list, e.g.:
from collections import deque
def dist(l, i1, i2):
d = deque(l)
d.rotate(-i1)
return sum(list(d)[:i2-i1]))
In []:
l = [1,2,3,4,5,6,7,8]
dist(l, 3-1, 6-1) # 3, 4, 5
Out[]:
12
In []:
dist(l, 6-1, 3-1) # 6, 7, 8, 1, 2
Out[]:
24
def distance(first_index, second_index, my_list):
temp_list = my_list + my_list
if (first_index > second_index):
first_index += len(my_list)
requested_sum = sum(my_list[second_index-1:first_index-1])
else:
requested_sum = sum(my_list[first_index-1:second_index-1])
return requested_sum
If I understood you correctly, then this should do the trick.
There are much more compact and efficient ways to do this, but this is the simplest and easiest to understand in my opinion.

Categories

Resources