Why this Merge Sort does not work properly? - python

I need to implement Merge Sort using Python 3. I coded it. But It doesn't give proper output. Can anybody check it please?
Here my code is,
def mergeSort(A, p, r):
if p < r:
q = (p + r) // 2
mergeSort(A, p, q)
mergeSort(A, q+1, r)
Merge(A, p, q, r)
def Merge(A, p, q, r):
i = 1
j = q+1
k = 0
TEMP = [0] * (r+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
if (j > r) :
for t in range(0, q-1):
A[r-t] = A[q-t]
for t in range(0, k-1):
A[p+t] = TEMP[t+1]
A = [15, 16, 13, 10, 19, 18]
mergeSort(A, 0, len(A)-1)
print(A)
Thank you

The way you perform merge looks weird (to me), but I will correct on what you have so far.
1- Initialization value of i is wrong, it should be:
i = p
because i is the first element you will look in array A.
2- Initialization value of size of TEMP array is wrong, it should be:
(r - p + 1)
3- There seems a mistake in filling in TEMP array and/or replacing A array elements, here is the fixed code. I wrote a comment about the part after first while loop to indicate what needs to be done at that point.
def mergeSort(A,p,r):
if p < r:
q = (p+r)//2
mergeSort(A,p,q)
mergeSort(A,q+1,r)
Merge(A,p,q,r)
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r - p + 1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
"""
There are currently 2 cases
1- i > q, means we exhausted left but there are elements in the right
2- j > r, means we exhausted right but there are elements in the left
"""
if (j > r):
# copy elements at the left side to temp
while (i <= q):
TEMP[k] = A[i]
i += 1
k += 1
else:
# copy elements at the right side to temp
while (j <= r):
TEMP[k] = A[j]
j += 1
k += 1
# replace elements in A with elements in TEMP
for t in range(k):
A[p+t] = TEMP[t]
A = [15,16,13,10,19,18]
mergeSort(A,0,len(A)-1)
print(A)

The error lies in the Merge() function.
Initialisation of i=p and not i=1
After the while loop terminates, there's a chance that either i<q or j<r. We need to accommodate those cases as well.
Size of array TEMP was incorrect.
Corrected Merge Function:
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r-p+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
while i<=q:
TEMP[k] = A[i]
k+=1
i += 1
while j<=r:
TEMP[k] = A[j]
k+=1
j += 1
for t in range (p,r+1):
A[t] = TEMP[t-p]
Note: Please try using more meaningful variable names.

Related

I am not getting the correct output for merge sort in Python

I am trying to writing the algorithm for merge sort in Python, but I am not getting the correct output. The error I am getting is "list assignment index out of range", so there is some logical error.
This is my code:
def mergeSort(a, l, r):
if l < r:
mid = (l+r)//2
mergeSort(a, l, mid)
mergeSort(a, mid+1, r)
merge(a, l, mid, r)
def merge(a, l, mid, r):
b = []
i = l
j = mid + 1
k = l
while i <= mid and j <= r:
if a[i] < a[j]:
b[k] = a[i]
i = i + 1
else:
b[k] = a[j]
j = j + 1
k = k + 1
if i > mid:
while j <= r:
b[k] = a[j]
k = k + 1
j = j + 1
else:
while i <= mid:
b[k] = a[i]
k = k + 1
j = j + 1
for k in range(l, r+1):
a[k] = b[k]
a = []
n = int(input("Enter the number of elements: "))
print("Enter the elements now")
for i in range(0, n):
element = int(input())
a.append(element)
print("Given array: ", a)
mergeSort(a, 0, len(a) - 1)
print("Sorted array is: ", a)
The first thing is that the array out of bounds error is coming because you are using b = []. When you do b[k] = a[i], it will throw the out of bounds exception. You have to initialize the b array.
Secondly, there are some issues in the logic. I corrected your code as follows:
def mergeSort(a):
if len(a) > 1:
mid = len(a) // 2
L = a[:mid]
R = a[mid:]
mergeSort(L)
mergeSort(R)
merge(a, mid, L, R)
def merge(a, mid, L, R):
i = 0
j = 0
k = 0
while i < len(L) and j < len(R):
if L[i] < R[j]:
a[k] = L[i]
i = i + 1
else:
a[k] = R[j]
j = j + 1
k = k + 1
while j < len(R):
a[k] = R[j]
k = k + 1
j = j + 1
while i < len(L):
a[k] = L[i]
k = k + 1
i = i + 1
a = []
n = int(input("Enter the number of elements: "))
print("Enter the elements now")
for i in range(0, n):
element = int(input())
a.append(element)
print("Given array: ", a)
mergeSort(a)
print("Sorted array is: ", a)

Getting List Index out of Range Error while using Merge Sort function

I am getting List index out of range error.
I have also used GeeksforGeeks program as a reference but still got that error.
I get no error when I run this without using it inside Merge_Sort() function.
def Merge(arr, p, q, r):
n1 = q-p+1
n2 = r-q
L = [0]*n1
M = [0]*n2
for i in range(0, n1):
L[i] = arr[p+i-1]
for j in range(0, n2):
M[j] = arr[q+j]
i = 0
j = 0
for k in range(r-1):
if L[i] <= M[j]:
arr[k] = L[i]
i = i+1
else:
arr[k] = M[j]
j = j+1
def Merge_Sort(arr, p, r):
if p < r:
q = int((p+r)/2)
Merge_Sort(arr, p, q)
Merge_Sort(arr, q+1, r)
Merge(arr, p, q, r)
ar = [5, 3, 6, 1, 2, 9, 7, 8]
n = len(ar)
Merge_Sort(ar, 1, n)
print(ar)
Error:
line 14, in Merge
if L[i]<=M[j]:
IndexError: list index out of range
The code is incorrect: the is some confusion about index values and slice boundaries, and other mistakes too:
array indices start at 0 in python, so you should call Merge_sort(ar, 0, n)
the slice length n1 is off by one, it should be n1 = q - p
the test for recursion should compute the slice length and only recurse for slices with at least 2 elements.
the merge loop is incorrect: you should test if i and j are both inside the slices. An efficient way to do this is to replace the comparison with this test:
if i < n1 and (j == n2 or L[i] <= M[j]):
the merge loop should iterate for k from p to r excluded, not from 0 to r excluded
the increment code for j is misaligned, it should be indented one more step
It is much simpler to consider the first index to be included and the second to be excluded. There are far too many tutorials in the net in various languages that insist on other conventions, invariably causing confusion for newbie programmers.
Here is a corrected and simplified version:
def Merge(arr, p, q, r):
n1 = q - p
n2 = r - q
L = arr[p : q]
M = arr[q : r]
i = 0
j = 0
for k in range(p, r):
if i < n1 and (j == n2 or L[i] <= M[j]):
arr[k] = L[i]
i = i + 1
else:
arr[k] = M[j]
j = j + 1
def Merge_Sort(arr, p, r):
if r - p >= 2:
q = (p + r) // 2
Merge_Sort(arr, p, q)
Merge_Sort(arr, q, r)
Merge(arr, p, q, r)
ar = [5, 3, 6, 1, 2, 9, 7, 8]
Merge_Sort(ar, 0, len(ar))
print(ar)
Note that you can further simplify the MergeSort function with a single temporary array if you ensure that the left slice is always at least as large as the right slice:
def Merge(arr, p, q, r):
tmp = arr[p : q]
i = 0
n = q - p
while i < n:
if q == r or tmp[i] <= arr[q]:
arr[p] = tmp[i]
i += 1
p += 1
else:
arr[p] = arr[q]
q += 1
p += 1
def Merge_Sort(arr, p, r):
if r - p >= 2:
q = (p + r + 1) // 2
Merge_Sort(arr, p, q)
Merge_Sort(arr, q, r)
Merge(arr, p, q, r)
ar = [5, 3, 6, 1, 2, 9, 7, 8]
Merge_Sort(ar, 0, len(ar))
print(ar)
Your code differs from the GeeksforGeeks code. I corrected the merge function to match theirs. You need three loops:
Take the smaller of the first elements from L or M until either L or M is empty
Append the elements remaining in L (if any)
Append the elements remaining in M (if any)
You also need a variable that tracks the current index in arr (k in this case).
GeeksforGeeks code: https://www.geeksforgeeks.org/merge-sort/
Corrected python code:
def Merge(arr, p, q, r):
n1 = q-p+1
n2 = r-q
L = [0]*n1
M = [0]*n2
for i in range(0,n1):
L[i] = arr[p+i]
for j in range(0, n2):
M[j] = arr[q+1+j]
i = 0
j = 0
# result index
k = p
# take smallest element until either L or M are empty
while i < n1 and j < n2:
if L[i]<=M[j]:
arr[k] = L[i]
i = i+1
else:
arr[k] = M[j]
j = j+1
k = k+1
# write remaining elements from L
while i < n1:
arr[k] = L[i]
i = i+1
k = k+1
# write remaining elements from M
while j < n2:
arr[k] = M[j]
j = j+1
k = k+1
def Merge_Sort(arr, p, r):
if p < r:
q = int((p+r)/2)
Merge_Sort(arr, p, q)
Merge_Sort(arr, q+1,r)
Merge(arr, p, q, r)
ar = [5,3,6,1,2,9,7,8]
n = len(ar)
Merge_Sort(ar,0,n-1)
print(ar)
If you only want to use one loop you can combine all of the above like so (takes away from the readability though):
def Merge(arr, p, q, r):
n1 = q-p+1
n2 = r-q
L = [0]*n1
M = [0]*n2
for i in range(0,n1):
L[i] = arr[p+i]
for j in range(0, n2):
M[j] = arr[q+1+j]
i = 0
j = 0
for k in range(n1+n2):
if (i < n1 and j < n2 and L[i]<=M[j]) or j >= n2:
arr[p+k] = L[i]
i = i+1
else:
arr[p+k] = M[j]
j = j+1
def Merge_Sort(arr, p ,r):
if p < r:
q = int((p+r)/2)
Merge_Sort(arr, p, q)
Merge_Sort(arr, q+1,r)
Merge(arr, p, q, r)
ar = [5,3,6,1,2,9,7,8,]
n = len(ar)
Merge_Sort(ar,0,n-1)
print(ar)

QuickSort in Python. Increment in array trouble

I am trying to implement quicksort in python. Problem is how to increment/decrement value of i/j in array a. I know that I should write i=i+1 and there are no such thing like i++ in python, but I don't understand in which way I should do this.
I am newbie, here is my code.
def quicksort(a,lo,hi):
if(hi<=lo):
return
i = lo - 1
j = hi
v = a[hi]
while True:
while(a[++i] < v):
pass
while(v < a[--j]):
if(j==lo):
break
if(i>=j):
break
t = a[i]
a[i] = a[j]
a[j] = t
t = a[i]
a[i] = a[hi]
a[hi] = t
quicksort(a, lo, i - 1)
quicksort(a, i + 1, hi)
in python, you cannot assign and get the value, that's an intentional limitation to avoid issues with typos, find the proper sequence point...
You have no choice than to "emulate" the C-ported code:
while(a[++i] < v):
pass
while(v < a[--j]):
if(j==lo):
break
(note that both constructs generate an infinite loop because:
++i == i
and
--j == j
(applying unary plus any number of times or unary minus an even number of times gives the same number, see Why Don't Two Plus Operators Throw an Error (e.g., 1 + + 2))
So change to:
i += 1
while(a[i] < v):
i += 1
j -= 1
while(v < a[j]):
if(j==lo):
break
j -= 1
The following construct does not work the same way in Python as it does in C++:
while(a[++i] < v):
as well as this one:
while(v < a[--j]):
The way you change the code is the following:
def quicksort(a,lo,hi):
if(hi<=lo):
return
i = lo - 1
j = hi
v = a[hi]
while True:
i += 1
while(a[i] < v):
i += 1
pass
j -= 1
while(v < a[j]):
j -= 1
if(j==lo):
break
if(i>=j):
break
t = a[i]
a[i] = a[j]
a[j] = t
t = a[i]
a[i] = a[hi]
a[hi] = t
quicksort(a, lo, i - 1)
quicksort(a, i + 1, hi)

Implementing 3-way quicksort

I'm a new to algorithms and I'm confused as to where are the errors in my code that I'm writing as an assignment. I'm trying to implement a quicksort algorithm in Python 3 that deals with equal values in the array.
Here's a quicksort function (a stands for the array):
def randomized_quick_sort(a, l, r):
if l >= r:
return
k = random.randint(l, r)
a[l], a[k] = a[k], a[l]
m1, m2 = partition3(a, l, r)
randomized_quick_sort(a, l, m1 - 1);
randomized_quick_sort(a, m2 + 1, r);
And here's my partition function:
def partition3(a, l, r):
x, j, t = a[l], l, r
for i in range(l + 1, r + 1):
if a[i] < x:
j +=1
a[i], a[j] = a[j], a[i]
elif a[i] > x:
a[i], a[t] = a[t], a[i]
t -=1
else:
j +=1
a[l], a[j] = a[j], a[l]
return j, t
You should rectify your partition function:
Here is a working example :
def partition3(a, l, r):
x, j, t = a[l], l, r
i = j
while i <= t :
if a[i] < x:
a[j], a[i] = a[i], a[j]
j += 1
elif a[i] > x:
a[t], a[i] = a[i], a[t]
t -= 1
i -= 1 # remain in the same i in this case
i += 1
return j, t
Here is a dead simple quicksort implementation in python. While it is still nlogn there are a bunch of performance optimizations that can be made. For example the partitioning into less,equal,greater can be made in a single pass instead of 3 passes of the array.
def qsort(arr):
if len(arr) <= 1: return arr
pivot = arr[0]
less = [x for x in arr if x < pivot]
equal = [x for x in arr if x == pivot]
greater = [x for x in arr if x > pivot]
return qsort(less) + equal + qsort(greater)
To make that partition happen in one pass of the array, make a helper function like follows:
def partition(arr, pivot):
less, equal, greater = [], [], []
for val in arr:
if val < pivot: less.append(val)
if val == pivot: equal.append(val)
if val > pivot: greater.append(val)
return less, equal, greater
def qsort(arr):
if len(arr) <= 1: return arr
pivot = arr[0]
less, equal, greater = partition(arr, pivot)
return qsort(less) + equal + qsort(greater)
Another implementation with for loop
def partition3(a, l, r):
x = a[l]
m1 = l
m2 = l
i = m1
for i in range(l + 1, r + 1):
if a[i] < x:
a[i], a[m1] = a[m1], a[i]
a[i], a[m2+1] = a[m2+1], a[i]
m1 += 1
m2 += 1
elif a[i] == x:
a[i], a[m2+1] = a[m2+1], a[i]
m2 += 1
return m1, m2

Why won't this merge sort implementation work?

I am following along with the CLRS book and am implementing all the algorithms in python as I go along, this is the merge sort algorithm. I tested the merge function on a test array and it works, so it must be the merge_sort function, however it's pretty much the same as the pseudocode in the book.
def merge_sort(self, array, p, r):
if p < r:
q = int((p + r) / 2)
self.merge_sort(array, p, q)
self.merge_sort(array, q + 1, r)
self.merge(array, p, q , r)
def merge(self, array, p, q, r):
n1 = q - p + 1
n2 = r - q
left = []
right = []
for i in range(0, n1):
left.append(array[p + i])
for j in range (0, n2):
right.append(array[q + j + 1])
left.append(math.inf)
right.append(math.inf)
i = 0
j = 0
for k in range(p, r):
if left[i] <= right[j]:
array[k] = left[i]
i += 1
else:
array[k] = right[j]
j += 1
I'm going to guess that this:
for k in range(p, r):
should be:
for k in range(p, r + 1):
This will make k take on all values from p to r, before it was taking on the values of p to r - 1 -- just the way range() works.

Categories

Resources