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.
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)
I want to count how many inversions there are in a list while sorting the list using mergesort. This is my code so far where 'x' counts the ammount of inversions while the rest sorts it:
import sys
x = 0
def merge_sort(A):
merge_sort2(A, 0, len(A) - 1)
def merge_sort2(A, first, last):
if first < last:
middle = (first + last) // 2
merge_sort2(A, first, middle)
merge_sort2(A, middle + 1, last)
merge(A, first, middle, last)
def merge(A, first, middle, last):
global x
L = A[first:middle + 1]
R = A[middle + 1:last + 1]
L.append(sys.maxsize)
R.append(sys.maxsize)
i = j = 0
for k in range(first, last + 1):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
x += 1
x += len(L[first + 1:])
When I call merge sort using a list, the variable x is support to give the amount of inversions in the list. So If the list was '[4,3,2,1], x would be 6. If the list was [1,2,3] x would be 0. I change the value of x whenever the right is greater than the left in the merge definition however, the number always gets way too big. What am I doing wrong?
Check my work but, I think instead of:
x += 1
x += len(L[first + 1:])
you want:
x += middle + 1 + j - k
basically, you want to add the difference between where item k is actually coming from, and where you'd expect it to come from if everything was already sorted.
Your merge step is a little hard for me to understand — I'm not sure why you are doing this (maybe just another way to merge?):
L.append(sys.maxsize)
R.append(sys.maxsize)
but I couldn't get everything to work out with the extra elements added to the partitions. And I think you end up counting the extra element in L as an inversion with each merge move from R
I think that's causing some of the problems. But you also have two other issues:
Your last line isn't quite the right logic:
x += len(L[first + 1:])
the number of inversions will the number of elements in L that you jump over. You're counting almost every element of L each time. Something like this works better:
x += len(L[i:])
and then at the end, you may have elements left over whose inversions you haven't counted yet. Maybe that's not an issue with your extra elements but in a more traditional merge it is. Here's the way I would count the inversions:
def merge(A, first, middle, last):
global x
L = A[first:middle+1]
R = A[middle+1:last+1]
i = j = 0
k = first
print(L, R)
while i<len(L) and j<len(R):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
# count how many left in L
x += len(L[i:])
k += 1
# take care of any leftovers in L or R
while i < len(L):
A[k] = L[i]
i += 1
k+=1
while j < len(R):
A[k] = R[j]
j += 1
k+=1
x += len(L[i:])
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!
How do you sort a list with a while loop? Having a bit of a problem, thanks very much in advance.
a = [12,0,39,50,1]
first = a[0]
i = 0
j = 1
while i < len(a):
if a[i] < first:
tmp = a[i]
a[i] = a[j]
a[j] = tmp
i += 1
print(a)
You can create an empty list that would store your sorted numbers
a = [12,0,39,50,1]
kk = len(a)
new_a = []
i = 0
while i < kk:
xx = min(a) ## This would retreive the minimum value from the list (a)
new_a.append(xx) ## You store this minimum number in your new list (new_a)
a.remove(xx) ## Now you have to delete that minimum number from the list a
i += 1 ## This starts the whole process again.
print(new_a)
Please, note that I used the original length of the list a (kk) for the while statement so as not to stop the iteration because the length of the list a decreases as we delete the minimum numbers.
Following is the implementation of basic sorting using two while loops.
In every iteration, the minimum element (considering ascending order) from the unsorted subarray is picked and moved to the sorted subarray.
:
a=[12,0,39,50,1]
i=0
while i<len(a):
key=i
j=i+1
while j<len(a):
if a[key]>a[j]:
key=j
j+=1
a[i],a[key]=a[key],a[i]
i+=1
print(a)
# By using For loop
def ascending_array(arr):
print(f"Original array is {arr}")
arr_length = len(arr)
if arr_length <= 1:
return arr
for i in range(len(arr)):
for j in range(i+1, len(arr)):
if arr[i] >= arr[j]:
arr[i], arr[j] = arr[j], arr[i]
print(f"The result array is {arr}") # [0,0,0,1,10,20,59,63,88]
arr = [1,10,20,0,59,63,0,88,0]
ascending_array(arr)
# By using While loop
def ascending_array(arr):
print(f"Original array is {arr}")
arr_length = len(arr)
if arr_length <= 1:
return arr
i = 0
length_arr = len(arr)
while (i<length_arr):
j = i+1
while (j<length_arr):
if arr[i] > arr[j]:
arr[i], arr[j] = arr[j], arr[i]
j+=1
i+=1
print(f"The result array is {arr}") # [0,0,0,1,10,20,59,63,88]
arr = [1,10,20,0,59,63,0,88,0]
ascending_array(arr)
For-loop is best in terms of performance. while-loop is checking condition every iteration.
You can also concatenate two lists and sort them in decreasing/increasing order using this example:
x = [2,9,4,6]
y = [7,8,3,5]
z = []
maxi = x[0]
pos = 0
print('x: '+str(x))
print('y: '+str(y))
for i in range(len(y)):
x.append(y[i])
for j in range(len(x)-1):
maxi = x[0]
for i in range(len(x)):
if maxi < x[i]:
maxi = x[i]
pos = i
z.append(maxi)
del x[pos]
z.append(x[0])
print('z: '+str(z))
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.