Index out of range in python for mergesort - python

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)

Related

change in list in the recursive call in python

I was studying merge sort and I understood the whole concept of it but when i went through the code i have a doubt this is the code
def mergeSort(arr):
if len(arr) > 1:
print('array before function', arr)
# Finding the mid of the array
mid = len(arr) // 2
print('mid',mid)
# Dividing the array elements
L = arr[:mid]
# into 2 halves
R = arr[mid:]
# Sorting the first half
mergeSort(L)
# Sorting the second half
mergeSort(R)
print('array after function',arr)
print('l is',L)
print('R is',R)
i = j = k = 0
# Copy data to temp arrays L[] and R[]
while i < len(L) and j < len(R):
if L[i] < R[j]:
arr[k] = L[i]
i += 1
else:
arr[k] = R[j]
j += 1
k += 1
# Checking if any element was left
while i < len(L):
arr[k] = L[i]
i += 1
k += 1
while j < len(R):
arr[k] = R[j]
j += 1
k += 1
# Code to print the list
def printList(arr):
for i in range(len(arr)):
print(arr[i], end=" ")
print()
# Driver Code
if __name__ == '__main__':
arr = [3,1,2,5,4]
print("Given array is", end="\n")
printList(arr)
mergeSort(arr)
print("Sorted array is: ", end="\n")
printList(arr)
what i cant understand is when mergeSort(L) is called how are the changes made in arr in the recursive call reflect back after scope of the recursive call is over.How is this possible ,if for example l=[2,3,1] is passed in mergesort(L) it will sort L but why is this sorted L value change when the recursive call is over ,is this only in the case of Recursion and If so How
arr is a global variable. when mergeSort is called, the recursive calls are slices of the list, which are local copies of the list. This goes on until the situation when len(mergeSort) == 1. Thus, in the call stack of the requisite function, for each call, there is a separate copy of the arr list.
Next, python passes mutable data (in particular, a list) by reference. When using arr [k], even though writing to a local variable occurs, this local variable refers to the object created in the mergeSort functions earlier.

Trouble tracing my Merge sort algorithm (Python 3)

I wrote a short Merge sort algorithm in Python 3. I have trouble understanding how it manages to achieve the correct result, as when I try to trace its logical steps I end up with an out of order list. The annotated code can be seen below.
What I'm specifically referring to is the merging part of the code. The three 'while' loops.
Let me use an example to demonstrate what confuses me. I explain the details in the annotations.
Thank you in advance for your time and help.
Let's assume we want to merge two arrays.
left = [2,6]
right = [4,8]
def merge_sort(array):
if len(array) > 1:
middle = len(array)//2
left = array[:middle]
right = array[middle:]
merge_sort(left)
merge_sort(right)
i = j = k = 0
while i < len(left) and j < len(right):
# i.e. if 2 < 4
if left[i] < right[j]:
# The first index of the array is assigned the value 2
array[k] = left[i]
# i = 1 now
i += 1
# The 'else' statement is not executed, because the 'if' statement was.
else:
array[k] = right[j]
j += 1
# k = 1 now
k += 1
# The 'while' loop below assigns '6' as the value for index 1 of the array and terminates.
# k = 2 now
while i < len(left):
array[k] = left[i]
i += 1
k += 1
# The last 'while' loop assigns '4' and '8' to indexes 2 and 3 of the array, respectively.
while j < len(right):
array[k] = right[j]
j += 1
k += 1
# The algorithm terminates and from what I can see I should end up with the array of [2,6,4,8].
# I do not, however. It is sorted in ascending order and I cannot see where I'm making a mistake.
Firstly, careful with your wording, to be clear merge sort isn't merging distinct arrays, merge sort cleverly deconstructs a single unsorted array into sub-arrarys (in our case left and right) and sorts them individually and merges them back into a single array again with a final sort. In other words, you pass this function a single array unsorted and it returns a single sorted array. If you need to merge two arrays, you would do so before calling it.
Merge Sort
"Merge sort is a recursive algorithm that continually splits a list in half. If the list is empty or has one item, it is sorted by definition (the base case). If the list has more than one item, we split the list and recursively invoke a merge sort on both halves. Once the two halves are sorted, the fundamental operation, called a merge, is performed. Merging is the process of taking two smaller sorted lists and combining them together into a single, sorted, new list."
Debug/Analyze Code
To help with understanding how it works (and debug), inject print comments at the very least to best show what is going on in more detail. I have taken what you wrote and added print comments and pass the function a string to help determine which array (left or right) it is sorting. You can see the splitting sorting, and merging as it accomplishes the sort by splitting the array down to size one and merging the sorted sub arrays etc. in the process ...
def merge_sort(array,type):
print('merge_sort =>' + type)
if len(array) < 2:
print('Array < 2 nothing changed')
return array
middle = len(array) // 2
left = array[:middle]
right = array[middle:]
print('splitting : ' + str(array))
merge_sort(left,'left')
merge_sort(right,'right')
i = j = k = 0
print('sorting.. Left/Right:' + str(left) + str(right))
while i < len(left) and j < len(right):
if left[i] < right[j]:
print(' - left[i] < right[j] ('+ str(left[i]) + ' < ' + str(right[j]) + ') set array[' + str(k) + '] = ' + str(left[i]) + '')
array[k] = left[i]
i += 1
else:
print(' - else left[i] >= right[j] ('+ str(left[i]) + ' >= ' + str(right[j]) + ') set array[' + str(k) + '] = ' + str(right[j]) + '')
array[k] = right[j]
j += 1
k += 1
while i < len(left):
print(' - WHILE i < len(left), ('+str(i) +' < '+str(len(left))+'), set array[' + str(k) + '] = ' + str(left[i]) + '')
array[k] = left[i]
i += 1
k += 1
while j < len(right):
print(' - while j < len(right) ('+str(j) +' < ' + str(len(right)) + '), set array[' + str(k) + '] = ' + str(right[j]) + '')
array[k] = right[j]
j += 1
k += 1
print("returning.." + str(array))
return array
arr = [2,6,4,8]
result = merge_sort(arr,'full')
print(result)
Which provides the following output:
merge_sort =>full
splitting : [2, 6, 4, 8]
merge_sort =>left
splitting : [2, 6]
merge_sort =>left
Array < 2 nothing changed
merge_sort =>right
Array < 2 nothing changed
sorting.. Left/Right:[2][6]
- left[i] < right[j] (2 < 6) set array[0] = 2
- while j < len(right) (0 < 1), set array[1] = 6
returning..[2, 6]
merge_sort =>right
splitting : [4, 8]
merge_sort =>left
Array < 2 nothing changed
merge_sort =>right
Array < 2 nothing changed
sorting.. Left/Right:[4][8]
- left[i] < right[j] (4 < 8) set array[0] = 4
- while j < len(right) (0 < 1), set array[1] = 8
returning..[4, 8]
sorting.. Left/Right:[2, 6][4, 8]
- left[i] < right[j] (2 < 4) set array[0] = 2
- else left[i] >= right[j] (6 >= 4) set array[1] = 4
- left[i] < right[j] (6 < 8) set array[2] = 6
- while j < len(right) (1 < 2), set array[3] = 8
returning..[2, 4, 6, 8]
This yields something apx. like so:
References:
How do I merge arrays in python?
https://runestone.academy/runestone/books/published/pythonds/SortSearch/TheMergeSort.html
It seems in your annotations you exit the first while loop prematurely, you stop after one run when the code actually does 3 runs. Here is how you would follow wgat actually happens:
you run through it once, then you have k=1, i=1 and j=0,
so you go through this loop again (this time it is the else that is executed, and assigns 4 to index 1 of the array, now k=2, i=1 and j=1
so you run through the loop a third time, with thte if executed, finally k=3, i=2 and j=1, so you get out of the first while.

Sort a list of tuples, using nonlibrary sorting function (Python)

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

Why can't I implement merge sort this way

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!

Merge sort in python doesnt update the array after sorting

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.

Categories

Resources