Buggy merge sort implementation - python-3.x

My merge sort implementation is buggy since i am not getting two sorted list before calling merge.I am not sure what is wrong with it.
def mergeSort(arr):
if len(arr) == 1 : return arr
mid = len(arr) // 2
left_half = arr[:mid]
right_half = arr[mid:]
mergeSort(left_half)
mergeSort(right_half)
return merge(left_half,right_half)
def merge(list1,list2):
res = []
i = 0
j = 0
while i < len(list1) and j < len(list2):
if list1[i] < list2[j]:
res.append(list1[i])
i += 1
elif list1[i] > list2[j]:
res.append(list2[j])
j += 1
#Add remaining to res if any
while i < len(list1):
res.append(list1[i])
i += 1
while j < len(list2):
res.append(list2[j])
j += 1
return res
A = [5,1,2,15]
print(mergeSort(A))
My understanding of merge sort is that the space complexity is O(n) since n items in memory (in the final merge).Is quick sort preferred over merge sort just because quick sort is in-place?

I not python expert, but you should write
left_half = arr[:mid]
right_half = arr[mid:]
left_half = mergeSort(left_half)
right_half = mergeSort(right_half)
Because your mergeSort return copy of sorted array.

You have 3 mistakes in your code.
The first is that you don't handle the empty list. You need a <= instead of an == in your second line.
The second is that by simply calling mergeSort(left_half) you suppose that it will sort left_half “by reference”, which it doesn't (same with right_half).
The third is that you aren't doing anything in the case list1[i] == list2[j]. Actually you don't need that elif, you simply need an else. It doesn't matter whether you append list1[i] or list2[j] if they are equal, but you must append one of the two.
Your code should rather be:
def mergeSort(arr):
if len(arr) <= 1 : return arr
mid = len(arr) // 2
left_half = mergeSort(arr[:mid])
right_half = mergeSort(arr[mid:])
return merge(left_half, right_half)
def merge(list1, list2):
res = []
i = 0
j = 0
while i < len(list1) and j < len(list2):
if list1[i] < list2[j]:
res.append(list1[i])
i += 1
else:
res.append(list2[j])
j += 1
#Add remaining to res if any
...
As for your questions about space complexity and comparison with quicksort, there are already answers here on StackOverflow (here and here).

Related

Python Code Not Working!-- Convert array into Zig-Zag fashion

Given an array arr of distinct elements of size N, the task is to rearrange the elements of the array in a zig-zag fashion so that the converted array should be in the below form:
arr[0] < arr[1] > arr[2] < arr[3] > arr[4] < . . . . arr[n-2] < arr[n-1] > arr[n].
def zigZag(self,arr, n):
res=[]
r=(n//2)+1
arr.sort()
j=0
i=0
for k in range(0, r, 1):
res.insert(i, arr[j])
res.insert(i+1, arr[j+r])
i= i+2
j+= j
return res
You can sort the array and add to the new array from both sides of the array.
def zigZag(self, arr, n):
res=[None]*n
arr.sort()
i = 0
j = 0
while i < n//2:
res[j] = arr[i]
res[j+1] = arr[n-1-i]
i+=1
j+=2
if n%2 == 1:
res[-1] = arr[n//2]
return res

What am I doing wrong with this code for hackerrank?

I have been coding this problem for HackerRank and I ran into so many problems. The problem is called "Plus Minus" and I am doing it in Python 3. The directions are on https://www.hackerrank.com/challenges/plus-minus/problem. I tried so many things and it says that "there is no response on stdout". I guess a none-type is being returned. Here is the code.:
def plusMinus(arr):
p = 0
neg = 0
z = arr.count(0)
no = 0
for num in range(n):
if arr[num] < 0:
neg+=1
if arr[num] > 0:
p+=1
else:
no += 1
continue
return p/n
The following are the issues:
1) variable n, which represents length of the array, needs to be passed to the function plusMinus
2) No need to maintain the extra variable no, as you have already calculated the zero count. Therefore, we can eliminate the extra else condition.
3) No need to use continue statement, as there is no code after the statement.
4) The function needs to print the values instead of returning.
Have a look at the following code with proper naming of variables for easy understanding:
def plusMinus(arr, n):
positive_count = 0
negative_count = 0
zero_count = arr.count(0)
for num in range(n):
if arr[num] < 0:
negative_count += 1
if arr[num] > 0:
positive_count += 1
print(positive_count/n)
print(negative_count/n)
print(zero_count/n)
if __name__ == '__main__':
n = int(input())
arr = list(map(int, input().rstrip().split()))
plusMinus(arr, n)
The 6 decimals at the end are needed too :
Positive_Values = 0
Zeros = 0
Negative_Values = 0
n = int(input())
array = list(map(int,input().split()))
if len(array) != n:
print(f"Error, the list only has {len(array)} numbers out of {n}")
else:
for i in range(0,n):
if array[i] == 0:
Zeros +=1
elif array[i] > 0:
Positive_Values += 1
else:
Negative_Values += 1
Proportion_Positive_Values = Positive_Values / n
Proportion_Of_Zeros = Zeros / n
Proportion_Negative_Values = Negative_Values / n
print('{:.6f}'.format(Proportion_Positive_Values))
print('{:.6f}'.format(Proportion_Negative_Values))
print('{:.6f}'.format(Proportion_Of_Zeros))

Recursive merge sort - RecursionError: maximum recursion depth exceeded while calling a Python object

I'm very new in programming and Python, so please excuse in advance my lack of knowledge.
I’m trying to implement a recursive merge sort method in a class. However, when I try to run the program I get the following error:
RecursionError: maximum recursion depth exceeded while calling a Python object
This is because of left = self.merge(lst[:middle]) and right = self.merge(lst[middle:]).
Does someone know how this problem could be solved?
def merge(self, lst, reverse=False):
middle = len(lst) // 2
left = self.merge(lst[:middle])
right = self.merge(lst[middle:])
result = []
i, j = 0, 0
if not len(left) or not len(right):
return left or right
while i < len(left) and j < len(right):
if reverse:
if left[i] > right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
else:
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
if i == len(left) or j == len(right):
result.extend(left[i:] or right[j:])
break
return result
def _merge_sort(self, reverse=False):
lst = list(self.unsorted_tuple)
if len(lst) < 2:
return lst
return self.merge(lst)
The problem is that you don't have a properly defined base case. In case of merge sort this would be checking if the passed list is either empty or has only one element (it is trivially true that it is sorted).
I'm guessing you're trying to do this with if not len(left) or not len(right). There's two problems with that. First, I don't think this code does what you think it does, play with it a bit in isolation to see how it behaves. See how I changed it and try to improve it.
Second, the test for the base case must be checked before calling the function again recursively. You never even come to this part because first thing that your merge function does is to call itself again! Here's a standalone merge function (not defined as a class method, I leave this to you):
def merge(lst, reverse=False):
if len(lst) <= 1:
return lst
middle = len(lst) // 2
left = merge(lst[:middle])
right = merge(lst[middle:])
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if reverse:
if left[i] > right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
else:
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
if i == len(left):
result.extend(right[j:])
if j == len(right):
result.extend(left[i:])
return result
I strongly recommend you Aditya Bhargava's Grokking Algorithms - it uses Python and is a good introductory book.

Troubleshooting creating a function to swap elements in Python 3

I am having trouble understanding the for loop needed to create a function that swaps elements in a Python list.
I am trying to create the function using one for loop but apparently more than one is required
Inputs:
n = int(input().strip())
a = list(map(int, input().strip().split(' ')))
My method:
def count_swaps(a):
total_swaps = 0
i = 0
if a[i + 1] > a[i]:
total_swaps += 1
temp = a[i+1]
a[i+1] = a[i]
a[i] = temp
return total_swaps
Correct solution:
def count_swaps(a):
total_swaps = 0
for i in range(n):
swaps = 0
for j in range(n-1):
if a[j] > a[j+1]:
temp = a[j+1]
a[j+1] = a[j]
a[j] = temp
swaps += 1
total_swaps += swaps
return total_swaps
My question is, why is there need of two for loops, i.e. the j in range(n-1) and i in range(n)? In addition, should the range not be the same in both?

Quick sort counting

Python questions again.
I want to count the number of comparison operations performed by quick sort. Because I use a recursive function, I do not think that assigning count = 0 to the beginning of the function body is inappropriate, so I made it as follows.
def QuickSort(lst, count = 0):
if len(lst) > 1:
pivot_idx = len(lst) // 2
smaller_nums, larger_nums = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
if num < lst[pivot_idx]:
smaller_nums.append(num)
else:
larger_nums.append(num)
count = QuickSort(smaller_nums, count + 1)[1]
count = QuickSort(larger_nums, count + 1)[1]
lst[:] = smaller_nums + [lst[pivot_idx]] + larger_nums
return lst, count
However, after counting, I confirmed the count which is much lower than my expectation. According to big o, the quick sort would have to show the calculation of n * log (n), but it showed a much lower count. For example, when sorting a list with 1000 random elements, we expected to see a count of 1000 * log (1000) = 6907, but actually only 1164 counts. I am wondering if I am misusing the count in the function or misunderstanding it.
Thank you.
Your post is mistaken on several points:
Big-O is allows arbitrary constant factors and also ignoring the values for "small" values of n, where "small" can be arbitrarily large for any given analysis. So your computations are meaningless.
Your counts are wrong. There's one comparison per loop iteration. You're counting something else.
This is a strange way to code the count. Just use a global variable.
Try this. Note really you're using twice as many comparisons as this reports. The check that the loop index isn't the pivot could be eliminated with a smarter implementation.
c = 0
def QuickSort(lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
global c
c += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return QuickSort(smaller) + [lst[pivot_idx]] + QuickSort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
QuickSort(lst)
print c
Run(1000)
If you're aghast at the prospect of using a global variable, then you can just wrap the sort in a class:
import random
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != pivot_idx:
self.comparisons += 1
(larger, smaller)[num < lst[pivot_idx]].append(num)
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
lst = [random.randint(0,1000) for r in xrange(n)]
quicksort = QuickSort()
print quicksort.sort(lst)
print quicksort.comparisons
Run(100)
Building on the answer provided by Gene by adding print statements and a sort "error" range, his example was very helpful to my understanding of quicksort and an error term on the big O impact of operations performance comparison.
class QuickSort:
def __init__(self):
self.comparisons = 0
def sort(self, lst):
k_err = 0 # k << n, the value the sort array can be in error
if len(lst) <= 1:
return lst
pivot_idx = len(lst) // 2
smaller, larger = [], []
for idx, num in enumerate(lst):
if idx != (pivot_idx) :
self.comparisons += 1
try:
(larger, smaller)[(num - k_err) < lst[pivot_idx]].append(num)
except:
(larger, smaller)[(num + k_err) < lst[pivot_idx]].append(num)
print(pivot_idx,"larger", self.comparisons, larger)
print(pivot_idx, "smaller", self.comparisons, smaller, )
return self.sort(smaller) + [lst[pivot_idx]] + self.sort(larger)
def Run(n):
random.seed(100)
lst = [random.randint(0,round(100,0)) for r in range(n)]
quicksort = QuickSort()
print(len(lst), lst)
print(quicksort.sort(lst))
print(quicksort.comparisons, quicksort.comparisons/n, ((quicksort.comparisons/n)/math.log(n,10)), math.log(n,10) )

Resources