Python Merge Sort Algorithm - python

Hi I am attempting to make a Merge Sort algorithm for fun, and do not want to just copy code off the internet. Which is why I have not referred to another person's Stack Overflow thread. So unless the thread has the same issue, please do not direct me towards that. I am using 2 functions, merge and merge sort. Merge sort is recursive, I intend for it to split a list in half, and then sort each half. The merge algorithm should then take the two sorted lists and return a new list, which is just the two lists combined and sorted. Eventually the code should return a fully sorted list. Below is my code, and if you run it you will see that I am getting an empty list returned, which makes no sense to me. Any help would be greatly appreciated. Thanks :)
def merge(left, right):
resultList = []
leastRight = 0
leastLeft = 0
if len(left) >= len(right):
for i in range(len(left)-1):
counter = 0
for j in range(len(right)-1):
counter += 1
if right[counter % (len(right)-1)] <= right[j]:
leastRight = right[counter % (len(right)-1)]
print("leastRight if",leastRight)
else:
leastRight = right[j]
print("leastRight else", leastRight)
right.remove(leastRight)
if left[i] <= leastRight:
resultList.append(left[i])
else:
resultList.append(leastRight)
else:
for i in range(len(right)-1):
counter = 0
for j in range(len(left)-1):
counter += 1
if left[counter % (len(left)-1)] <= left[j]:
leastLeft = left[counter%(len(left)-1)]
print("leastLeft if", leastLeft)
else:
leastLeft = left[j]
print("leastLeft else", leastLeft)
left.remove(leastLeft)
if right[i] <= leastLeft:
resultList.append(right[i])
else:
resultList.append(leastLeft)
return (resultList)
def mergeSort(alist):
print("alist", alist)
if len(alist) > 2:
middleIndex = len(alist) // 2
sortedLeft = mergeSort(alist[:middleIndex])
print("left", sortedLeft)
sortedRight = mergeSort(alist[middleIndex:])
print("right", sortedRight)
result = merge(sortedLeft, sortedRight)
print("result", result)
else:
result = alist
return (result)
print("mergesort", mergeSort([6, 0, 2, 8, 9, 1]))

Sorry, but approach of your merge function is not usable at all. Principle of choosing the smallest element is too tangled here and causes errors (I just saw it cannot merge [6] and [0,2]). Have you read classical description of merging?
In mergesort (we cannot omit treatment of length 2 lists):
if len(alist) >= 2:
Quick-made working implementation of merge routine.
def merge(left, right):
resultlist = []
llen = len(left)
rlen = len(right)
il = 0
ir = 0
while il < llen and ir < rlen: #while both list are not exhausted
if left[il] <= right[ir]: #choose the smallest current item
resultlist.append(left[il])
il += 1
else:
resultlist.append(right[ir])
ir += 1
#now treat the rest tail
while il < llen:
resultlist.append(left[il])
il += 1
while ir < rlen:
resultlist.append(right[ir])
ir += 1
return resultlist
result
>>> mergesort [0, 1, 2, 6, 8, 9]
Note it would be wise to make resultlist of known length for speed.
def merge(left, right):
llen = len(left)
rlen = len(right)
resultlist = [0]*(llen+rlen) # we know data type and full length
il = 0
ir = 0
k = 0
while il < llen and ir < rlen:
if left[il] <= right[ir]:
resultlist[k] = left[il]
il += 1
else:
resultlist[k] = right[ir]
ir += 1
k += 1
while il < llen:
resultlist[k] = left[il]
il += 1
k += 1
while ir < rlen:
resultlist[k] = right[ir]
ir += 1
k += 1
return resultlist

Related

Hello guys, I'm not experienced with programming and I would like to run the Mergesort algorithm in Python for a default generated list

It takes the random list I generated but the output is something a lot different than I expected.
I think that something goes wrong with merging.
e.g. : Input --> [267,168,236,190,2,500,4,45,86]
Output --> [2,2,2,2,2,4,4,4,45,45,86]
Thank you in advance.
import numpy as np
def mergeSort(myList):
if len(myList) > 1:
mid = len(myList) // 2
left = myList[:mid]
right = myList[mid:]
# Recursive call on each half
mergeSort(left)
mergeSort(right)
# Two iterators for traversing the two halves
i = 0
j = 0
# Iterator for the main list
k = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
# The value from the left half has been used
myList[k] = left[i]
# Move the iterator forward
i += 1
else:
myList[k] = right[j]
j += 1
# Move to the next slot
k += 1
# For all the remaining values
while i < len(left):
myList[k] = left[i]
i += 1
k += 1
while j < len(right):
myList[k]=right[j]
j += 1
k += 1
list1 = np.random.randint(low=1, high=800, size=100)
myList = list1
print("Given array is", end="\n")
print(myList)
mergeSort(myList)
print("Sorted array is: ", end="\n")
print(myList)
The problem with your code is only with inplace manipulation of your original list. merge sort needs extra space O(n).
you could simply rewrite your code like this to work: (notice that it is your code, only I modified two lines, look for # changed ...)
import numpy as np
def mergeSort(myList):
if len(myList) > 1:
mid = len(myList) // 2
left = myList[:mid].copy() # changed this line
right = myList[mid:].copy() # changed this line
# Recursive call on each half
mergeSort(left)
mergeSort(right)
# Two iterators for traversing the two halves
i = 0
j = 0
# Iterator for the main list
k = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
# The value from the left half has been used
myList[k] = left[i]
# Move the iterator forward
i += 1
else:
myList[k] = right[j]
j += 1
# Move to the next slot
k += 1
# For all the remaining values
while i < len(left):
myList[k] = left[i]
i += 1
k += 1
while j < len(right):
myList[k]=right[j]
j += 1
k += 1
myList = np.random.randint(low=1, high=800, size=100)
print("Given array is", end="\n")
print(myList)
mergeSort(myList)
print("Sorted array is: ", end="\n")
print(myList)
However this is not very optimized version of implementation for the mergsort specially in python.
Here is better implementation, pure python (from: https://github.com/amirhm/algo-data-scratch/blob/main/Sorting/mergesort.ipynb)
def mergesort(l):
def merge(l, r):
lp, rp = 0 , 0
d = []
while lp < len(l) and rp < len(r):
if l[lp] < r[rp]:
d.append(l[lp])
lp += 1
else:
d.append(r[rp])
rp += 1
if rp < len(r): d.extend(l[lp:])
if lp < len(l): d.extend(r[rp:])
return d
if len(l) <= 1:
return l
n = len(l)
return merge(mergesort(l[:n//2]), mergesort(l[n//2:]))
or much more abstract only in 9 lines:
def mergesort(l):
def merge(l, r):
res = []
while l and r : ((res.append(l.pop())) if (l[-1] > r[-1]) else res.append(r.pop()))
#while r or l: res.append(r.pop()) if r else (res.append(l.pop()))
if r: res[::-1].extend(r)
if l: res[::-1].extend(l)
return res
if len(l) <= 1: return l
return merge(mergesort(l[:len(l) // 2]), mergesort(l[len(l) // 2:]))

Max Sub Array (non-adjacent)

I'm working on a HackerRank Max Array Sum problem. I see some simple and smart solutions but I wanna know why my code failing.
Here is my code. It takes next 4 elements from array and choose index0 or index1 and shift 2 or 3 elements.
if I can find max subset elements instead of sum of elements I can see my mistake.
HackerRank Problem Link
def Calc(arr):
result = 0
i = 0
while i < len(arr):
tempar = [i if i > 0 else 0 for i in arr[i:i+4]]
if len(tempar) == 4:
tmax = max(tempar[0] + tempar[2], tempar[0] + tempar[3], tempar[1] + tempar[3])
if tempar[0] + tempar[2] == tmax or tempar[0] + tempar[3] == tmax:
result += tempar[0]
i += 2
elif tempar[1] + tempar[3] == tmax:
result += tempar[1]
i+=3
if len(tempar) == 3:
if tempar[0] + tempar[2] > tempar[1]:
result += tempar[0] + tempar[2]
else:
result += tempar[1]
i+=3
if len(tempar) == 2:
result += max(tempar)
i+=2
if len(tempar) == 1:
result += tempar[0]
i+=1
return result
input()
ar = list(map(int, input().split()))
print(Calc(ar))
I didn't read your algorithm and the problem carefully, so I can't say whether your algorithm is right or not.
But it seems that your code allows choosing an empty set when all elements are negative. (tempar = [i if i > 0 else 0 for i in arr[i:i+4]])
For example, your program will output 0 for the input
5
-1 -1 -1 -1 -1
Your algorithm doesn't work.
for simple input
5
-1 -1 3 -2 5
I suggest you use the following approach:
def Calc(arr):
result = 0
prev_result=0
is_last_included=False
i = 0
while i < len(arr):
if arr[i] > 0:
if is_last_included==False:
prev_result=result
result=result+arr[i]
is_last_included=True
elif (prev_result+arr[i])>result:
temp=result
result=prev_result+arr[i]
prev_result=temp
is_last_included=True
else:
prev_result=result
is_last_included=False
i=i+1
return result

using merge sort principle

Q- I have an array A with the sizes of apples and have to create another array S which would contain the indices of the apples in sorted order given that we cannot directly access or touch A only a function is_large(A,i,j) function can access it. It returns -1 is A[i] > A[j] and 1 if A[i] < A[j].
I wrote the program but it is giving incorrect results even for small arrays what is the problem? The main problem is that the first element is not changing positions because of that the whole array is unsorted.
def is_large_apples(apple_size, i, j):
""" Takes two indices and tells
which one of them is larger or smaller """
if apple_size[i] > apple_size[j]:
return 1
elif apple_size[i] < apple_size[j]:
return -1
def mergesort_apples(s, l, r):
""" This function takes indexed list and
makes recursive calls to sort the array """
if l < r:
mid = (l+r)//2
mergesort_apples(s, l, mid)
mergesort_apples(s, mid+1, r)
merge_apples(s, l, mid, r)
def merge_apples(s, l, mid, r):
""" This function takes the list and
the indices to merge them into the final array"""
nl = mid - l + 1
nr = r - mid
left, right = [], []
left = s[l:mid+1:1]
right = s[mid+1:r+1:1]
i, j, k = 0, 0, l;
while i < nl and j < nr:
print(s)
if is_large_apples(apple_size, i, j) == -1:
s[k] = left[i]
i += 1
else:
s[k] = right[j]
j += 1
k += 1
while i < nl:
s[k] = left[i]
k += 1
i += 1
while j < nr:
s[k] = right[j]
k += 1
j += 1
apple_size = [5, 7, 1,44,2,33] # Given list of sizes.
s = [x for x in range(0,len(apple_size))] # Original list of indices.
mergesort_apples(s, 0, len(s)-1)
print(s)
if is_large_apples(apple_size, left[i], right[j]) == -1:
Because you want to check not i and j position, but left[i] position and right[j] position.

How can I count the swaps in a Merge Sort Algorithm in Python [duplicate]

This question already has answers here:
Counting inversions in an array
(38 answers)
Closed 5 years ago.
I've done the MergeSort algorithm, but I don't know how to count the swaps.
My code is:
def mergesortInv(list):
if len(list) < 2:
return list
else:
middle = len(list) // 2
left = mergesortInv(list[:middle]) #definim les dues meitats
right = mergesortInv(list[middle:])
swaps=???
return mergeInv(left, right,swaps)
def mergeInv(left, right,swaps):
if len(left) < 1:
return right
if len(right) < 1:
return left
if left[0] <= right[0]:
return [left[0]] + mergeInv(left[1:],right,swaps)
else:
return [right[0]] + mergeInv(left,right[1:],swaps)
The output of this algorithm would be the sorted list(the algorithm works in this part) and the number of swaps: mergesortInv(list) == ([1, 2, 3, 4, 5, 7, 8], 6) 6 is the number of swaps.
Here is a slightly modified version of your code that appears to work:
def mergesortInv(list, mergeInv):
if len(list) < 2:
return list, 0
else:
middle = len(list) // 2
left, lc = mergesortInv(list[:middle], mergeInv) #definim les dues meitats
right, rc = mergesortInv(list[middle:], mergeInv)
merge, mc = mergeInv(left, right)
return merge, lc + rc + mc
def mergeInvRec(left, right):
if len(left) < 1:
return right, 0
if len(right) < 1:
return left, 0
if left[0] <= right[0]:
res, cnt = mergeInvRec(left[1:], right)
return [left[0]] + res, cnt
else:
res, cnt = mergeInvRec(left, right[1:])
return [right[0]] + res, len(left) + cnt
def mergeInvFlat(left, right):
res, cnt = [], 0
il, ir = 0, 0
nl, nr = len(left), len(right)
while il < nl and ir < nr:
if left[il] <= right[ir]:
res.append(left[il])
il += 1
else:
res.append(right[ir])
ir += 1
cnt += nl - il
res.extend(left[il:])
res.extend(right[ir:])
return res, cnt
It's mostly a matter of book keeping. Count the number of swaps at each step and add them. In the very last branch the first element of right bubbles all the way past every element of left which is why we tally len(left) swaps there.
Edit: As #PM2Ring points out the recursion in mergeInv is a bit reckless and will exceed Python's maximum recursion depth for moderately sized lists.
I've added a non-recursive version. You can switch between the recursive and nonrecursive versions by passing their name as the second arg to the main function.
I didn't test this, but this is just to give you an idea about what I suggested in the comment to your question.
def mergesortInv(list):
if len(list) < 2:
return list
else:
middle = len(list) // 2
left = mergesortInv(list[:middle]) #definim les dues meitats
right = mergesortInv(list[middle:])
# swaps=???
return mergeInv(left, right)
def mergeInv(left, right):
""" return a tuple of total swaps and the merged list """
if len(left) < 1:
return (0, right)
if len(right) < 1:
return (0, left)
if left[0] <= right[0]:
swaps, lst = mergeInv(left[1:],right)
return (swaps, [left[0]] + [lst])
else:
swaps, lst = mergeInv(left,right[1:])
return (swaps + 1, [right[0]] + [lst])
Usage,
swaps, lst = mergesortInv(mylist)

Why doesn't this python implementation of mergesort work?

When I input [8,7,6,5,4,3,2,1] the output is => [4, 3, 2, 1, 8, 7, 6, 5].
It seems like the only thing different from a working solution (comparing here) is that instead of a sorted list, I have a k variable that I am incrementing, and update arr[k] in place of sorted.
Why doesn't this work? And how does updating arr[k] work? It seems like you would be losing data by updating the original input array.
def mergesort(arr):
if len(arr) == 1:
return
else:
mid = len(arr)/2
left = arr[0:mid]
right = arr[mid:len(arr)]
sorted = []
i = 0
j = 0
mergesort(left)
mergesort(right)
while i < len(left) and j < len(right):
if left[i] < right[j]:
sorted.append(left[i])
i += 1
else:
sorted.append(right[j])
j += 1
while i < len(left):
sorted.append(left[i])
i += 1
while j < len(right):
sorted.append(right[j])
j += 1
return sorted
You should just assign to left and right variable as you function return the sorted list after sorting also in the base case you should return a list and use // for integer division check this code
def mergesort(arr):
if len(arr) == 1:
return arr
else:
mid = len(arr)//2
left = arr[0:mid]
right = arr[mid:len(arr)]
sorted = []
i = 0
j = 0
left = mergesort(left) #left is now sorted
right = mergesort(right)
while i < len(left) and j < len(right):
if left[i] < right[j]:
sorted.append(left[i])
i += 1
else:
sorted.append(right[j])
j += 1
while i < len(left):
sorted.append(left[i])
i += 1
while j < len(right):
sorted.append(right[j])
j += 1
return sorted
print (mergesort([8,7,6,5,4,3,2,1,3]))

Categories