So I'm trying to teach myself how to write a merge sort but for whatever reason I can't seem to make it work.
def merge(left, right):
result = []
i ,j = 0, 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 += left[i:]
result += right[j:]
return result
def mergesort(numlist):
if len(numlist) < 2:
return numlist
middle = int(len(numlist)/2)
left = mergesort(numlist[:middle])
right = mergesort(numlist[middle:])
return merge(left, right)
Every list I feed into the sort then attempt to print just comes up the exact same with no changes
Answering so this can be closed though it was fixed in comments with the help of #Jean-François Fabre.
Your current code works fine but it does not sort the list in-place. In the case of sorting a list called list_of_numbers, you need to assign the result back to list_of_numbers:
list_of_numbers = mergesort(list_of_numbers)
Related
I'm working on a project and tried to implement merge sort in python but I'm getting Index out of range Error as I'm new python sp don't know much about python syntax and inbuilt functions
def merge1(a,l,h):
if l<h:
mid=int((l+h)/2)
left=a[:mid]
right=a[mid:]
merge1(left,l,mid)
merge1(right,mid+1,h)
mergesort(a,l,mid,h)
def mergesort(a,l,mid,h):
i=l
j=mid+1
k=l
b=[]
while i<=mid and j<=h:
if a[i]<=a[j]:
b[k]=a[i]
i+=1
else:
b[k]=a[j]
j+=1
k+=1
if i>mid:
for x in mid(j,h):
b[k]=a[x]
k=k+1
else:
for x in range(i,mid):
b[k]=a[x]
i=i+1
for i in range(0,k):
a[k]=b[k]
a=[9,1,45,99,98,56]
merge1(a,0,len(a)-1)
print(a)
<ipython-input-71-e2786b6fbe02> in mergesort(a, l, mid, h)
15 b=[]
16 while i<=mid and j<=h:
---> 17 if a[i]<=a[j]:
18 b[k]=a[i]
19 i+=1
IndexError: list index out of range
Python syntax for sub array is array[begin, end], where the index range is from begin to end-1 inclusive (it doesn't include end). The terminating conditions for indexes should be < not <=, for example i < mid, instead of i <= mid.
The names of merge1 and mergesort are reversed. mergesort() is actually the merge function, and merge1 is actually the top down recursive mergesort function. The names should be swapped.
The merge sort function should only be creating stack of calls that include the array and index range. left and right should be created from the array "a" in the merge function, then merged back into "a" during the merge process.
The initial call to the merge sort function should have parameters (a, 0, len(a))
Example code
def mergesort(a,beg,end):
if (end-beg) > 1:
mid=(beg+end)//2
mergesort(a,beg,mid)
mergesort(a,mid,end)
merge(a,beg,mid,end)
def merge(a,beg,mid,end):
left = a[beg:mid]
right = a[mid:end]
i = 0
j = 0
k = beg
while True:
if left[i] <= right[j]:
a[k] = left[i]
i += 1
k += 1
if(i < len(left)):
continue
a[k:end] = right[j:len(right)]
break
else:
a[k] = right[j]
j += 1
k += 1
if(j < len(right)):
continue
a[k:end] = left[i:len(left)]
break
a=[9,1,45,99,98,56]
mergesort(a,0,len(a))
print(a)
As the problems stated above, I need to use a nonlibrary sorting function, specifically a mergesort function I implemented in a previous assignment. I have my assignment working really well using activity_arr.sort(key=operator.itemgetter(2)). However, the requirements are asking that I use this function:
def merge_sort(array):
''' Sorts an array using merge sort algorithm.'''
if len(array) > 1:
# '//' is for "floor division", used in case array is filled with floats
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
# Recursion to sort left and right half of array
merge_sort(left)
merge_sort(right)
i = 0
j = 0
k = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
# Fill the rest of the array with remaining numbers in each array
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1
I assume, I will have to modify it so that I can sort my list of tuples. An example of my list to sort is example_list = [(1, 3, 4), (7, 2, 5), (1, 2, 1)]. I want to sort in ascending order by the third element of the tuple. SO it should result in the following: sorted_list = [(1, 2, 1), (1, 3, 4), (7, 2, 5)].
I am still just learning python and still have much to learn in CS in general. I have no idea how to make this change despite lots of research I mostly find people saying to use .sort(). If I left out anything important please let me know! Any help or information for me to look up will be very much appreciated. Thank you!
You can pass an index based on which you want to sort array of tuples.
For example, if you want to sort array based on index 2(indexing starts from 0), you can use it as: merge_sort(example_list,2). Idea is simple, you have to compare element based on index given, like I have done it here:
if left[i][index] < right[j][index]:
def merge_sort(array,index): #edit here
''' Sorts an array using merge sort algorithm.'''
if len(array) > 1:
# '//' is for "floor division", used in case array is filled with floats
mid = len(array) // 2
left = array[:mid]
right = array[mid:]
# Recursion to sort left and right half of array
merge_sort(left,index) #edit here
merge_sort(right,index) #edit here
i = 0
j = 0
k = 0
while i < len(left) and j < len(right):
if left[i][index] < right[j][index]: #edit here
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
k += 1
# Fill the rest of the array with remaining numbers in each array
while i < len(left):
array[k] = left[i]
i += 1
k += 1
while j < len(right):
array[k] = right[j]
j += 1
k += 1
I understand mergesort works by divide and conquer, you keep halving until you reach a point where you can sort in constant time or the list is just one lement and then you merge the lists.
def mergesort(l):
if len(l)<=1:
return l
l1 = l[0:len(l)//2+1]
l2 = l[len(l)//2:]
l1 = mergesort(l1)
l2 = mergesort(l2)
return merge(l1,l2)
I have a working merge implementation and I checked it works fine but the merge sort implementation does not work it just returns half of the elements of the list.
I see on the internet mergesort is implemented using l & r and m = (l + r)/2. What is wrong with my implementation? I am recursively subdividing the list and merging too.
the problem is the +1 in your code, here:
l1 = l[0:len(l)//2]
l2 = l[len(l)//2:]
replace this with your code and you're be fine
The code you have listed doesn't appear to do any sorting. I can't know for certain because you haven't listed the merge() function's code, but the only thing that the above function will do is recursively divide the list into halves. Here is a working implementation of a merge sort:
def mergeSort(L):
# lists with only one value already sorted
if len(L) > 1:
# determine halves of list
mid = len(L) // 2
left = L[:mid]
right = L[mid:]
# recursive function calls
mergeSort(left)
mergeSort(right)
# keeps track of current index in left half
i = 0
# keeps track of current index in right half
j = 0
# keeps track of current index in new merged list
k = 0
while i < len(left) and j < len(right):
# lower values appended to merged list first
if left[i] < right[j]:
L[k] = left[i]
i += 1
else:
L[k] = right[j]
j += 1
k += 1
# catch remaining values in left and right
while i < len(left):
L[k] = left[i]
i += 1
k += 1
while j < len(right):
L[k] = right[j]
j += 1
k += 1
return L
Your function makes no comparisons of values in the original list. Also, when you are splitting the list into halves in:
l1 = l[0:len(l)//2 + 1]
the '+ 1' is unnecessary (and can actually cause incorrect solutions). You can simply use:
l1 = l[:len(l)//2]
If the length is even (i.e 12) it will divide the two halves from [0:6] and [6:12]. If it is odd it will still automatically divide correctly (i.e. length = 13 would be [0:6] and [6:13]. I hope this helps!
I am trying to understand algorithms by writing them myself. While trying to replicate merge sort I have run into some trouble: left & right return none-type and an error is raised for the len(left) in the first while loop. I have been fighting with the code and could not figure out what I was missing? Shouldn't it just loop until the size of left and right lists reduce to 1 which would allow them to get out of the if loop and continue with the next part of the function?
def merge_sort(A):
if len(A) < 2:
return A
else:
mid= len(A)//2
left= merge_sort(A[:mid])
right= merge_sort(A[mid:])
i = j = 0
sortedlist = []
while i < len(left) and j < len(right):
if left[i] < right[j]:
sortedlist.append(left[i])
i+=1
else:
sortedlist.append(right[j])
j+=1
while i < len(left):
sortedlist.append(left[i])
i+=1
while j < len(right):
sortedlist.append(right[j])
j+=1
print(str(sortedlist))
all you need to do is add a return statement (the last statement in the code below):
def merge_sort(A):
if len(A) < 2:
return A
else:
mid= len(A)//2
left = merge_sort(A[:mid])
right = merge_sort(A[mid:])
i = j = 0
sortedlist = []
while i < len(left) and j < len(right):
if left[i] < right[j]:
sortedlist.append(left[i])
i+=1
else:
sortedlist.append(right[j])
j+=1
while i < len(left):
sortedlist.append(left[i])
i+=1
while j < len(right):
sortedlist.append(right[j])
j+=1
# NEED TO RETURN THE LIST HERE!
return sortedlist
if your function does not return anything statements like left = merge_sort(A[:mid]) will assign None to left instead of the sorted (half of the) list.
you could then test that with:
import random
lst = list(range(15))
random.shuffle(lst)
ret = merge_sort(lst)
print(ret)
Your function doesn't contain a return statement. You should add return sortedlist at it's end.
ab = [5, 89, 23, 9]
def mergsort(array):
mid = len(array) / 2
if mid > 0:
print (array)
mergsort(array[:mid])
mergsort(array[mid:])
print(array)
merg(array)
return array
def merg(array):
print (array)
mid = len(array)//2
left = array[:mid]
right = array[mid:]
i = j = k = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
array[k] = left[i]
i+=1
else:
array[k] = right[j]
j+=1
k+=1
while i < len(left):
array[k]=left[i]
i+=1
k+=1
while j < len(right):
array[k] = right[j]
j+=1
k+=1
print (array)
mergsort(ab)
print (ab)
The merge function sort the array given and the array is updated. But in the next recursion the array going into the merg function is not the mutated array.
In the example, first sorting happens and [5,89] and [23,9] are sorted as [5,89] and [9,23] but the merged input in the next recursion is [5,89,23,9] instead of [5,89,9,23].
I am unable to find any reason as mutating the array should affect the parent array.
One problem is with the recursive calls:
mergsort(array[:mid])
mergsort(array[mid:])
the results of these calls are not recorded - so when we continue, it's done with the same original unsorted array.
The fix:
def mergsort(array):
if len(array) == 1:
return array
mid=len(array)/2
left = mergsort(array[:mid]) # save into a parameter
right = mergsort(array[mid:]) # save into a parameter
return merge(left, right) # use the previous two
The second issue is actually the same kind of issue only with:
def merg(array)
the merge operation is done between two arrays, which means that two distinct arrays should be sent to this function, otherwise there is no recollection of mid from the function mergesort() and declaring mid to be length/2 treats the whole array and not the specific two parts that we intend to merge. The idea behind the logic inside this function is correct but should be done, as I mentioned, on two "distinct" arrays.
Last problem is the in-place swap which is incorrectly done, for example in:
array[k]=right[j]
by doing do, we erase the element at array[k]!
The fix:
def merge(left, right):
if len(left+right) < 2:
return left+right
res = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
res.append(left[i])
i += 1
elif j < len(right):
res.append(right[j])
j += 1
while i < len(left):
res.append(left[i])
i += 1
while j < len(right):
res.append(right[j])
j += 1
return res
After applying both fixes and running:
print mergsort(ab)
The output is:
[5, 9, 23, 89]
as required.