K-th smallest quick sort python - python

I have to get K-th smallest element in unsorted array.
Not to sort the whole array I am trying only sort the subarrays that include K-th element. Then I am printing all K-th from 0 to len(array)
array = [6,5,4,3,2,1]
def quick_sort(lst, k):
if len(lst) <= 1:
return lst
else:
p = (lst[0] + lst[len(lst)-1])/2
left = [x for x in lst if x <= p]
right = [x for x in lst if x > p]
if k > len(left) -1 :
k = k - len(left)+1
return quick_sort(right,k)
else:
return quick_sort(left, k)
for i in range(len(array)):
print(*quick_sort(array,i+1))
I want to get 1,2,3,4,5,6
but my code does 2,3,5,6,6,6.
What I need to change?
P.S. THe main idea is not sorting all arrays and not using python sort functions

array = [6, 5, 4, 3, 2, 1]
def quick_sort(lst, k):
if len(lst) <= 1:
return lst
else:
p = (lst[0] + lst[-1]) / 2
left = [x for x in lst if x <= p]
right = [x for x in lst if x > p]
if k > len(left):
k = k - len(left)
return quick_sort(right, k)
else:
return quick_sort(left, k)
for i in range(len(array)):
print(*quick_sort(array, i + 1))

Related

Find a sum in n arrays equal to a given value?

Let's say i have n arrays :
a = [1,2,3,4,5,6]
b = [0,3,5,7,9,10]
c = [3,3,5,6,7,8]
...
How to find a sum in these arrays that equals a given value X ?
I get there when I do this :
for i in a:
for j in b:
for k in c:
if i + j + k == X:
...
But it's hard-coded, how to do it with n arrays ?
You can use recursion with lists joined into single 2D list:
a = [[1,2,3,4,5,6], [0,3,5,7,9,10],[3,3,5,6,7,8]]
def sumof(a, idx, value, lst):
if value < 0:
return
if idx == len(a):
if (value == 0):
print(lst)
return
for x in a[idx]:
sumof(a, idx + 1, value - x, lst + [x])
sumof(a, 0, 22, [])
This will make the product of any number of arrays and print the values whose sum equal a certain value.
import itertools
a = [1,2,3,4,5,6]
b = [0,3,5,7,9,10]
c = [3,3,5,6,7,8]
arrays = [a, b, c]
X = 15
for x in itertools.product(*arrays):
if sum(x) == X:
print("+".join(str(v) for v in x))

How to find distance metrics in python

This is the array.
arr=[1,2,2,1,5,1]
i need to calculate the abs value of index differences between in and all the other elements if the same value, like explained below.
distance metric for a[0] = |0-3|+|0-5|=8
distance metric for a[1] = |1-2|=1
distance metric for a[2] = |2-1|=1
distance metric for a[3] = |3-0|+|3-5|=5
distance metric for a[4] = 0
distance metric for a[5] = |5-0|+|5-3|=7
output is
[8,1,1,5,0,7]
can someone help in coding this in python.
You can use this working example:
arr=[1,2,2,1,5,1]
res = []
for i, n in enumerate(arr):
val = 0
occur = [j for j, x in enumerate(arr) if x == n and j != i]
for o in occur:
val += abs(i-o)
res.append(val)
print(res)
[8, 1, 1, 5, 0, 7]
a bit more efficient version with a complexity of O(n**2)
arr=[1,2,2,1,5,1]
res = []
for i, n in enumerate(arr):
val = 0
for j, x in enumerate(arr):
if x == n and j != i:
val += abs(i-j)
res.append(val)
print(res)
Using list comprehensions and avoiding intermediate variables
arr = [1, 2, 2, 1, 5, 1]
output = []
for index in range(len(arr)):
output.append(sum(abs(index - k) if arr[k] == arr[index] else 0 for k in range(len(arr))))
print(output)

Python Quicksort implementation

I tried to implement the recursive quicksort in Python, but it doesn't work. I know that there is the problem that the array doesn't get sorted because the pivot is always higher than i, which results in the problem that i is always equals to m.
def partition(array):
pivot = array[-1]
m = 0
for i in range(len(array) - 1):
if array[i] < pivot:
array[i], array[m] = array[m], array[i]
m += 1
else:
continue
array[m], array[len(array)-1] = array[len(array)-1], array[m]
return m
def quicksort(array):
if len(array) > 1:
m = partition(array)
quicksort(array[:m])
quicksort(array[m+1:])
return array
def main():
testarray = [3,6,2,4,5,1,9,8,7,10,14]
print(quicksort(testarray))
if __name__ == '__main__':
main()
Two things. Firstly, you forgot to return array when it's of length 1, and secondly you aren't actually modifying array before returning. This will work.
def quicksort(array):
if len(array) > 1:
m = partition(array)
# return the concatenation of the two sorted arrays
return quicksort(array[:m]) + quicksort(array[m:])
else:
return array
For those looking for an iterative/non-recursive version of Quicksort, here's an implementation I came up with in Python:
from random import randint
def default_comparator_fn(a, b):
return -1 if a < b else (1 if a > b else 0)
def reverse_comparator_fn(a, b):
return default_comparator_fn(b, a)
def quick_sort(A, comparator_fn=default_comparator_fn):
n = len(A)
if n < 2:
# The list has only 1 element or does not have any.
return A
# There are at least 2 elements.
partitions = [[0, n - 1]] # [[start, end]]
while len(partitions):
partition = partitions.pop()
start = partition[0]
end = partition[1]
pivot_index = randint(start, end)
pivot = A[pivot_index]
A[pivot_index], A[start] = A[start], A[pivot_index]
breakpoint_index = start
k = start + 1
m = end
while k <= m:
res = comparator_fn(A[k], pivot)
if res < 0:
breakpoint_index = k
else:
while m > k:
res = comparator_fn(A[m], pivot)
if res < 0:
breakpoint_index = k
A[m], A[k] = A[k], A[m]
m -= 1
break
m -= 1
k += 1
A[start], A[breakpoint_index] = A[breakpoint_index], A[start]
if start < breakpoint_index - 1:
partitions.append([start, breakpoint_index - 1])
if breakpoint_index + 1 < end:
partitions.append([breakpoint_index + 1, end])
return A
# Example:
A = [4, 2, 5, 1, 3]
quick_sort(A) # Sort in ascending order ([1, 2, 3, 4, 5]).
quick_sort(A, reverse_comparator_fn) # Sort in descending order ([5, 4, 3, 2, 1]).
This implementation of Quicksort accepts an optional custom comparator function which defaults to a comparator which compares the elements of the list in ascending order.

Get the absolute difference between elements of a circular array

Let's say I have a array like l = [1, 3, 4, 5, 6, 8]
where the nth element represents the distance between the nth and n+1th object.
I want to find the distance between any two objects, and I used this code for this:
def dis(l_list, index1, index2, mylist):
m = mylist.index(index1)
n = mylist.index(index2)
i=0
j=0
if n > m:
while n >= m:
i = i + mylist[m]
m = m + 1
elif n < m:
while n <= m:
i = i + mylist[n]
n = n + 1
else:
return(0)
j = mylist[n] % l_mylist
print(abs(i - j))
l_mylist = input()
l_mylist = int(l_mylist)
mylist = []
mylist = list(map(int, input().split()))
i,j = input().split()
i, j=int(i), int(j)
dis(l_mylist, i, j, mylist)
but I am still getting the wrong output. Can anyone please point out where I am wrong?
If you want to sum around a potentially circular list. You can use a collections.deque() to rotate the list, e.g.:
from collections import deque
def dist(l, i1, i2):
d = deque(l)
d.rotate(-i1)
return sum(list(d)[:i2-i1]))
In []:
l = [1,2,3,4,5,6,7,8]
dist(l, 3-1, 6-1) # 3, 4, 5
Out[]:
12
In []:
dist(l, 6-1, 3-1) # 6, 7, 8, 1, 2
Out[]:
24
def distance(first_index, second_index, my_list):
temp_list = my_list + my_list
if (first_index > second_index):
first_index += len(my_list)
requested_sum = sum(my_list[second_index-1:first_index-1])
else:
requested_sum = sum(my_list[first_index-1:second_index-1])
return requested_sum
If I understood you correctly, then this should do the trick.
There are much more compact and efficient ways to do this, but this is the simplest and easiest to understand in my opinion.

Mergesort with Python

I couldn't find any working Python 3.3 mergesort algorithm codes, so I made one myself. Is there any way to speed it up? It sorts 20,000 numbers in about 0.3-0.5 seconds
def msort(x):
result = []
if len(x) < 2:
return x
mid = int(len(x)/2)
y = msort(x[:mid])
z = msort(x[mid:])
while (len(y) > 0) or (len(z) > 0):
if len(y) > 0 and len(z) > 0:
if y[0] > z[0]:
result.append(z[0])
z.pop(0)
else:
result.append(y[0])
y.pop(0)
elif len(z) > 0:
for i in z:
result.append(i)
z.pop(0)
else:
for i in y:
result.append(i)
y.pop(0)
return result
The first improvement would be to simplify the three cases in the main loop: Rather than iterating while some of the sequence has elements, iterate while both sequences have elements. When leaving the loop, one of them will be empty, we don't know which, but we don't care: We append them at the end of the result.
def msort2(x):
if len(x) < 2:
return x
result = [] # moved!
mid = int(len(x) / 2)
y = msort2(x[:mid])
z = msort2(x[mid:])
while (len(y) > 0) and (len(z) > 0):
if y[0] > z[0]:
result.append(z[0])
z.pop(0)
else:
result.append(y[0])
y.pop(0)
result += y
result += z
return result
The second optimization is to avoid popping the elements. Rather, have two indices:
def msort3(x):
if len(x) < 2:
return x
result = []
mid = int(len(x) / 2)
y = msort3(x[:mid])
z = msort3(x[mid:])
i = 0
j = 0
while i < len(y) and j < len(z):
if y[i] > z[j]:
result.append(z[j])
j += 1
else:
result.append(y[i])
i += 1
result += y[i:]
result += z[j:]
return result
A final improvement consists in using a non recursive algorithm to sort short sequences. In this case I use the built-in sorted function and use it when the size of the input is less than 20:
def msort4(x):
if len(x) < 20:
return sorted(x)
result = []
mid = int(len(x) / 2)
y = msort4(x[:mid])
z = msort4(x[mid:])
i = 0
j = 0
while i < len(y) and j < len(z):
if y[i] > z[j]:
result.append(z[j])
j += 1
else:
result.append(y[i])
i += 1
result += y[i:]
result += z[j:]
return result
My measurements to sort a random list of 100000 integers are 2.46 seconds for the original version, 2.33 for msort2, 0.60 for msort3 and 0.40 for msort4. For reference, sorting all the list with sorted takes 0.03 seconds.
Code from MIT course. (with generic cooperator )
import operator
def merge(left, right, compare):
result = []
i, j = 0, 0
while i < len(left) and j < len(right):
if compare(left[i], right[j]):
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
while i < len(left):
result.append(left[i])
i += 1
while j < len(right):
result.append(right[j])
j += 1
return result
def mergeSort(L, compare=operator.lt):
if len(L) < 2:
return L[:]
else:
middle = int(len(L) / 2)
left = mergeSort(L[:middle], compare)
right = mergeSort(L[middle:], compare)
return merge(left, right, compare)
def merge_sort(x):
if len(x) < 2:return x
result,mid = [],int(len(x)/2)
y = merge_sort(x[:mid])
z = merge_sort(x[mid:])
while (len(y) > 0) and (len(z) > 0):
if y[0] > z[0]:result.append(z.pop(0))
else:result.append(y.pop(0))
result.extend(y+z)
return result
You can initialise the whole result list in the top level call to mergesort:
result = [0]*len(x) # replace 0 with a suitable default element if necessary.
# or just copy x (result = x[:])
Then for the recursive calls you can use a helper function to which you pass not sublists, but indices into x. And the bottom level calls read their values from x and write into result directly.
That way you can avoid all that poping and appending which should improve performance.
Take my implementation
def merge_sort(sequence):
"""
Sequence of numbers is taken as input, and is split into two halves, following which they are recursively sorted.
"""
if len(sequence) < 2:
return sequence
mid = len(sequence) // 2 # note: 7//2 = 3, whereas 7/2 = 3.5
left_sequence = merge_sort(sequence[:mid])
right_sequence = merge_sort(sequence[mid:])
return merge(left_sequence, right_sequence)
def merge(left, right):
"""
Traverse both sorted sub-arrays (left and right), and populate the result array
"""
result = []
i = j = 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
# Print the sorted list.
print(merge_sort([5, 2, 6, 8, 5, 8, 1]))
As already said, l.pop(0) is a O(len(l)) operation and must be avoided, the above msort function is O(n**2). If efficiency matter, indexing is better but have cost too. The for x in l is faster but not easy to implement for mergesort : iter can be used instead here. Finally, checking i < len(l) is made twice because tested again when accessing the element : the exception mechanism (try except) is better and give a last improvement of 30% .
def msort(l):
if len(l)>1:
t=len(l)//2
it1=iter(msort(l[:t]));x1=next(it1)
it2=iter(msort(l[t:]));x2=next(it2)
l=[]
try:
while True:
if x1<=x2: l.append(x1);x1=next(it1)
else : l.append(x2);x2=next(it2)
except:
if x1<=x2: l.append(x2);l.extend(it2)
else: l.append(x1);l.extend(it1)
return l
Loops like this can probably be speeded up:
for i in z:
result.append(i)
z.pop(0)
Instead, simply do this:
result.extend(z)
Note that there is no need to clean the contents of z because you won't use it anyway.
A longer one that counts inversions and adheres to the sorted interface. It's trivial to modify this to make it a method of an object that sorts in place.
import operator
class MergeSorted:
def __init__(self):
self.inversions = 0
def __call__(self, l, key=None, reverse=False):
self.inversions = 0
if key is None:
self.key = lambda x: x
else:
self.key = key
if reverse:
self.compare = operator.gt
else:
self.compare = operator.lt
dest = list(l)
working = [0] * len(l)
self.inversions = self._merge_sort(dest, working, 0, len(dest))
return dest
def _merge_sort(self, dest, working, low, high):
if low < high - 1:
mid = (low + high) // 2
x = self._merge_sort(dest, working, low, mid)
y = self._merge_sort(dest, working, mid, high)
z = self._merge(dest, working, low, mid, high)
return (x + y + z)
else:
return 0
def _merge(self, dest, working, low, mid, high):
i = 0
j = 0
inversions = 0
while (low + i < mid) and (mid + j < high):
if self.compare(self.key(dest[low + i]), self.key(dest[mid + j])):
working[low + i + j] = dest[low + i]
i += 1
else:
working[low + i + j] = dest[mid + j]
inversions += (mid - (low + i))
j += 1
while low + i < mid:
working[low + i + j] = dest[low + i]
i += 1
while mid + j < high:
working[low + i + j] = dest[mid + j]
j += 1
for k in range(low, high):
dest[k] = working[k]
return inversions
msorted = MergeSorted()
Uses
>>> l = [5, 2, 3, 1, 4]
>>> s = msorted(l)
>>> s
[1, 2, 3, 4, 5]
>>> msorted.inversions
6
>>> l = ['e', 'b', 'c', 'a', 'd']
>>> d = {'a': 10,
... 'b': 4,
... 'c': 2,
... 'd': 5,
... 'e': 9}
>>> key = lambda x: d[x]
>>> s = msorted(l, key=key)
>>> s
['c', 'b', 'd', 'e', 'a']
>>> msorted.inversions
5
>>> l = [5, 2, 3, 1, 4]
>>> s = msorted(l, reverse=True)
>>> s
[5, 4, 3, 2, 1]
>>> msorted.inversions
4
>>> l = ['e', 'b', 'c', 'a', 'd']
>>> d = {'a': 10,
... 'b': 4,
... 'c': 2,
... 'd': 5,
... 'e': 9}
>>> key = lambda x: d[x]
>>> s = msorted(l, key=key, reverse=True)
>>> s
['a', 'e', 'd', 'b', 'c']
>>> msorted.inversions
5
Here is the CLRS Implementation:
def merge(arr, p, q, r):
n1 = q - p + 1
n2 = r - q
right, left = [], []
for i in range(n1):
left.append(arr[p + i])
for j in range(n2):
right.append(arr[q + j + 1])
left.append(float('inf'))
right.append(float('inf'))
i = j = 0
for k in range(p, r + 1):
if left[i] <= right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
def merge_sort(arr, p, r):
if p < r:
q = (p + r) // 2
merge_sort(arr, p, q)
merge_sort(arr, q + 1, r)
merge(arr, p, q, r)
if __name__ == '__main__':
test = [5, 2, 4, 7, 1, 3, 2, 6]
merge_sort(test, 0, len(test) - 1)
print test
Result:
[1, 2, 2, 3, 4, 5, 6, 7]
Many have answered this question correctly, this is just another solution (although my solution is very similar to Max Montana) but I have few differences for implementation:
let's review the general idea here before we get to the code:
Divide the list into two roughly equal halves.
Sort the left half.
Sort the right half.
Merge the two sorted halves into one sorted list.
here is the code (tested with python 3.7):
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.extend(left[i:]) # since we want to add each element and not the object list
result.extend(right[j:])
return result
def merge_sort(data):
if len(data)==1:
return data
middle=len(data)//2
left_data=merge_sort(data[:middle])
right_data=merge_sort(data[middle:])
return merge(left_data,right_data)
data=[100,5,200,3,100,4,8,9]
print(merge_sort(data))
here is another solution
class MergeSort(object):
def _merge(self,left, right):
nl = len(left)
nr = len(right)
result = [0]*(nl+nr)
i=0
j=0
for k in range(len(result)):
if nl>i and nr>j:
if left[i] <= right[j]:
result[k]=left[i]
i+=1
else:
result[k]=right[j]
j+=1
elif nl==i:
result[k] = right[j]
j+=1
else: #nr>j:
result[k] = left[i]
i+=1
return result
def sort(self,arr):
n = len(arr)
if n<=1:
return arr
left = self.sort(arr[:n/2])
right = self.sort(arr[n/2:] )
return self._merge(left, right)
def main():
import random
a= range(100000)
random.shuffle(a)
mr_clss = MergeSort()
result = mr_clss.sort(a)
#print result
if __name__ == '__main__':
main()
and here is run time for list with 100000 elements:
real 0m1.073s
user 0m1.053s
sys 0m0.017s
def merge(l1, l2, out=[]):
if l1==[]: return out+l2
if l2==[]: return out+l1
if l1[0]<l2[0]: return merge(l1[1:], l2, out+l1[0:1])
return merge(l1, l2[1:], out+l2[0:1])
def merge_sort(l): return (lambda h: l if h<1 else merge(merge_sort(l[:h]), merge_sort(l[h:])))(len(l)/2)
print(merge_sort([1,4,6,3,2,5,78,4,2,1,4,6,8]))
def merge(x):
if len(x) == 1:
return x
else:
mid = int(len(x) / 2)
l = merge(x[:mid])
r = merge(x[mid:])
i = j = 0
result = []
while i < len(l) and j < len(r):
if l[i] < r[j]:
result.append(l[i])
i += 1
else:
result.append(r[j])
j += 1
result += l[i:]
result += r[j:]
return result
A little late the the party, but I figured I'd throw my hat in the ring as my solution seems to run faster than OP's (on my machine, anyway):
# [Python 3]
def merge_sort(arr):
if len(arr) < 2:
return arr
half = len(arr) // 2
left = merge_sort(arr[:half])
right = merge_sort(arr[half:])
out = []
li = ri = 0 # index of next element from left, right halves
while True:
if li >= len(left): # left half is exhausted
out.extend(right[ri:])
break
if ri >= len(right): # right half is exhausted
out.extend(left[li:])
break
if left[li] < right[ri]:
out.append(left[li])
li += 1
else:
out.append(right[ri])
ri += 1
return out
This doesn't have any slow pop()s, and once one of the half-arrays is exhausted, it immediately extends the other one onto the output array rather than starting a new loop.
I know it's machine dependent, but for 100,000 random elements (above merge_sort() vs. Python built-in sorted()):
merge sort: 1.03605 seconds
Python sort: 0.045 seconds
Ratio merge / Python sort: 23.0229
def mergeSort(alist):
print("Splitting ",alist)
if len(alist)>1:
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
mergeSort(lefthalf)
mergeSort(righthalf)
i=0
j=0
k=0
while i < len(lefthalf) and j < len(righthalf):
if lefthalf[i] < righthalf[j]:
alist[k]=lefthalf[i]
i=i+1
else:
alist[k]=righthalf[j]
j=j+1
k=k+1
while i < len(lefthalf):
alist[k]=lefthalf[i]
i=i+1
k=k+1
while j < len(righthalf):
alist[k]=righthalf[j]
j=j+1
k=k+1
print("Merging ",alist)
alist = [54,26,93,17,77,31,44,55,20]
mergeSort(alist)
print(alist)
Glad there are tons of answers, I hope you find this one to be clear, concise, and fast.
Thank you
import math
def merge_array(ar1, ar2):
c, i, j= [], 0, 0
while i < len(ar1) and j < len(ar2):
if ar1[i] < ar2[j]:
c.append(ar1[i])
i+=1
else:
c.append(ar2[j])
j+=1
return c + ar1[i:] + ar2[j:]
def mergesort(array):
n = len(array)
if n == 1:
return array
half_n = math.floor(n/2)
ar1, ar2 = mergesort(array[:half_n]), mergesort(array[half_n:])
return merge_array(ar1, ar2)
After implementing different versions of solution,
I finally made a trade-off to achieve these goals based on CLRS version.
Goal
not using list.pop() to iterate values
not creating a new list for saving result, modifying the original one instead
not using float('inf') as sentinel values
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):
L = A[p:q+1]
R = A[q+1:r+1]
i = 0
j = 0
k = p
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
k += 1
if i < len(L):
A[k:r+1] = L[i:]
if __name__ == "__main__":
items = [6, 2, 9, 1, 7, 3, 4, 5, 8]
mergesort(items, 0, len(items)-1)
print items
assert items == [1, 2, 3, 4, 5, 6, 7, 8, 9]
Reference
[1] Book: CLRS
[2] https://github.com/gzc/CLRS/blob/master/C02-Getting-Started/exercise_code/merge-sort.py
Try this recursive version
def mergeList(l1,l2):
l3=[]
Tlen=len(l1)+len(l2)
inf= float("inf")
for i in range(Tlen):
print "l1= ",l1[0]," l2= ",l2[0]
if l1[0]<=l2[0]:
l3.append(l1[0])
del l1[0]
l1.append(inf)
else:
l3.append(l2[0])
del l2[0]
l2.append(inf)
return l3
def main():
l1=[2,10,7,6,8]
print mergeSort(breaklist(l1))
def breaklist(rawlist):
newlist=[]
for atom in rawlist:
print atom
list_atom=[atom]
newlist.append(list_atom)
return newlist
def mergeSort(inputList):
listlen=len(inputList)
if listlen ==1:
return inputList
else:
newlist=[]
if listlen % 2==0:
for i in range(listlen/2):
newlist.append(mergeList(inputList[2*i],inputList[2*i+1]))
else:
for i in range((listlen+1)/2):
if 2*i+1<listlen:
newlist.append(mergeList(inputList[2*i],inputList[2*i+1]))
else:
newlist.append(inputList[2*i])
return mergeSort(newlist)
if __name__ == '__main__':
main()
def merge(a,low,mid,high):
l=a[low:mid+1]
r=a[mid+1:high+1]
#print(l,r)
k=0;i=0;j=0;
c=[0 for i in range(low,high+1)]
while(i<len(l) and j<len(r)):
if(l[i]<=r[j]):
c[k]=(l[i])
k+=1
i+=1
else:
c[k]=(r[j])
j+=1
k+=1
while(i<len(l)):
c[k]=(l[i])
k+=1
i+=1
while(j<len(r)):
c[k]=(r[j])
k+=1
j+=1
#print(c)
a[low:high+1]=c
def mergesort(a,low,high):
if(high>low):
mid=(low+high)//2
mergesort(a,low,mid)
mergesort(a,mid+1,high)
merge(a,low,mid,high)
a=[12,8,3,2,9,0]
mergesort(a,0,len(a)-1)
print(a)
If you change your code like that it'll be working.
def merge_sort(arr):
if len(arr) < 2:
return arr[:]
middle_of_arr = len(arr) / 2
left = arr[0:middle_of_arr]
right = arr[middle_of_arr:]
left_side = merge_sort(left)
right_side = merge_sort(right)
return merge(left_side, right_side)
def merge(left_side, right_side):
result = []
while len(left_side) > 0 or len(right_side) > 0:
if len(left_side) > 0 and len(right_side) > 0:
if left_side[0] <= right_side[0]:
result.append(left_side.pop(0))
else:
result.append(right_side.pop(0))
elif len(left_side) > 0:
result.append(left_side.pop(0))
elif len(right_side) > 0:
result.append(right_side.pop(0))
return result
arr = [6, 5, 4, 3, 2, 1]
# print merge_sort(arr)
# [1, 2, 3, 4, 5, 6]
The following code pops at the end (efficient enough) and sorts inplace despite returning as well.
def mergesort(lis):
if len(lis) > 1:
left, right = map(lambda l: list(reversed(mergesort(l))), (lis[::2], lis[1::2]))
lis.clear()
while left and right:
lis.append(left.pop() if left[-1] < right[-1] else right.pop())
lis.extend(left[::-1])
lis.extend(right[::-1])
return lis
This is very similar to the "MIT" solution and a couple others above, but answers the question in a little more "Pythonic" manner by passing references to the left and right partitions instead of positional indexes, and by using a range in the for loop with slice notation to fill in the sorted array:
def merge_sort(array):
n = len(array)
if n > 1:
mid = n//2
left = array[0:mid]
right = array[mid:n]
print(mid, left, right, array)
merge_sort(left)
merge_sort(right)
merge(left, right, array)
def merge(left, right, array):
array_length = len(array)
right_length = len(right)
left_length = len(left)
left_index = right_index = 0
for array_index in range(0, array_length):
if right_index == right_length:
array[array_index:array_length] = left[left_index:left_length]
break
elif left_index == left_length:
array[array_index:array_length] = right[right_index:right_length]
break
elif left[left_index] <= right[right_index]:
array[array_index] = left[left_index]
left_index += 1
else:
array[array_index] = right[right_index]
right_index += 1
array = [99,2,3,3,12,4,5]
arr_len = len(array)
merge_sort(array)
print(array)
assert len(array) == arr_len
This solution finds the left and right partitions using Python's handy // operator, and then passes the left, right, and array references to the merge function, which in turn rebuilds the original array in place.
The trick is in the cleanup: when you have reached the end of either the left or the right partition, the original array is filled in with whatever is left over in the other partition.
#here is my answer using two function one for merge and another for divide and
#conquer
l=int(input('enter range len'))
c=list(range(l,0,-1))
print('list before sorting is',c)
def mergesort1(c,l,r):
i,j,k=0,0,0
while (i<len(l))&(j<len(r)):
if l[i]<r[j]:
c[k]=l[i]
i +=1
else:
c[k]=r[j]
j +=1
k +=1
while i<len(l):
c[k]=l[i]
i+=1
k+=1
while j<len(r):
c[k]=r[j]
j+=1
k+=1
return c
def mergesort(c):
if len(c)<2:
return c
else:
l=c[0:(len(c)//2)]
r=c[len(c)//2:len(c)]
mergesort(l)
mergesort(r)
return mergesort1(c,l,r)
def merge(arr, p, q, r):
left = arr[p:q + 1]
right = arr[q + 1:r + 1]
left.append(float('inf'))
right.append(float('inf'))
i = j = 0
for k in range(p, r + 1):
if left[i] <= right[j]:
arr[k] = left[i]
i += 1
else:
arr[k] = right[j]
j += 1
def init_func(function):
def wrapper(*args):
a = []
if len(args) == 1:
a = args[0] + []
function(a, 0, len(a) - 1)
else:
function(*args)
return a
return wrapper
#init_func
def merge_sort(arr, p, r):
if p < r:
q = (p + r) // 2
merge_sort(arr, p, q)
merge_sort(arr, q + 1, r)
merge(arr, p, q, r)
if __name__ == "__main__":
test = [5, 4, 3, 2, 1]
print(merge_sort(test))
Result would be
[1, 2, 3, 4, 5]
from run_time import run_time
from random_arr import make_arr
def merge(arr1: list, arr2: list):
temp = []
x, y = 0, 0
while len(arr1) and len(arr2):
if arr1[0] < arr2[0]:
temp.append(arr1[0])
x += 1
arr1 = arr1[x:]
elif arr1[0] > arr2[0]:
temp.append(arr2[0])
y += 1
arr2 = arr2[y:]
else:
temp.append(arr1[0])
temp.append(arr2[0])
x += 1
y += 1
arr1 = arr1[x:]
arr2 = arr2[y:]
if len(arr1) > 0:
temp += arr1
if len(arr2) > 0:
temp += arr2
return temp
#run_time
def merge_sort(arr: list):
total = len(arr)
step = 2
while True:
for i in range(0, total, step):
arr[i:i + step] = merge(arr[i:i + step//2], arr[i + step//2:i + step])
step *= 2
if step > 2 * total:
return arr
arr = make_arr(20000)
merge_sort(arr)
# run_time is 0.10300588607788086
Here is my attempt at the recursive merge_sort function in python. Note, this is my first python class and my first encounter with this problem so please bear with me if my code is rough, but it works.
def merge_sort(S):
temp = []
if len(S) < 2:
return S
split = len(S) // 2
left = merge_sort(S[:split])
right = merge_sort(S[split:])
finale = temp + merge(left, right)
return finale
def merge(left, right):
holder = []
while len(left) > 0 and len(right) > 0:
if left[0] < right[0]:
holder.append(left[0])
del left[0]
elif left[0] > right[0]:
holder.append(right[0])
del right[0]
if len(left) > 0:
holder.extend(left)
elif len(right) > 0:
holder.extend(right)
return holder
def splitArray(s):
return s[:len(s)//2], s[len(s)//2:]
# the idea here is i+j should sum to n as you increment i and j,
# but once out of bound, the next item of a or b is infinity
# therefore, the comparison will always switch to the other array
def merge(a, b, n):
result = [0] * n
a = a + [float('inf')]
b = b + [float('inf')]
result = [0] * n
i, j = 0, 0
for k in range(0, n):
if a[i] < b[j]:
result[k] = a[i]
i+=1
else:
result[k] = b[j]
j+=1
return result
def mergeSort(items):
n = len(items)
baseCase = []
if n == 0:
return baseCase
if n == 1:
baseCase.append(items[0])
return baseCase
if n == 2:
if items[0] < items[1]:
baseCase.append(items[0])
baseCase.append(items[1])
return baseCase
else:
baseCase.append(items[1])
baseCase.append(items[0])
return baseCase
left, right = splitArray(items)
sortedLeft = mergeSort(left)
sortedRight = mergeSort(right)
return merge(sortedLeft,sortedRight,n)
# Driver code to test above
arr = [12, 11, 13, 5, 6, 7]
n = len(arr)
print ("Given array is")
for i in range(n):
print ("%d" %arr[i]),
arr = mergeSort(arr)
print ("\n\nSorted array is")
for i in range(n):
print ("%d" %arr[i]),
def merge_sort(l):
if len(l) == 1:
if len(n)> 0:
for i in range(len(n)):
if n[i] > l[0]:
break
else:
i = i+1
n.insert(i, l[0])
else:
n.append(l[0])
else:
p = len(l)//2
a = l[:p]
b = l[p:]
merge_sort(a)
merge_sort(b)
m = [3,5,2,4,1]
n = []
merge_sort(m)
print(n)
first divide the array until it's size grater than 1(which is base condition) and do it by recursive function.
compare the left & right sub array value & place those value in your array.
check any item remain in left & right array...
def merge_sort(my_array):
base condition for recursively dividing the array...
if len(my_array) > 1:
middle = len(my_array) // 2
left_array = my_array[:middle]
right_array = my_array[middle:]
#recursive function
merge_sort(left_array)
merge_sort(right_array)
i = 0 # index of left array...
j = 0 # index of right array...
k = 0 # index of new array...
# conquer the array and sorted like below condition
while i < len(left_array) and j < len(right_array):
if left_array[i] < right_array[j]:
my_array[k] = left_array[i]
i += 1
else:
my_array[k] = right_array[j]
j += 1
k += 1
# checking any item remain in left sub array....
while i < len(left_array):
my_array[k] = left_array[i]
i += 1
j += 1
# checking any item remain in right sub array....
while j < len(right_array):
my_array[k] = right_array[j]
j += 1
k += 1
my_array = [11, 31, 7, 41, 101, 56, 77, 2]
print("Input Array: ",my_array)
merge_sort(my_array)
print("Sorted Array: ",my_array)
I would suggest to leverage Python3's protocols instead of passing a comparator here, there and everywhere.
Also a simple set of tests based Knuth's shuffle would be a decent idea to verify implementation correctness:
from abc import abstractmethod
from collections import deque
from typing import Deque, Protocol, TypeVar, List
from random import randint
class Comparable(Protocol):
"""Protocol for annotating comparable types."""
#abstractmethod
def __lt__(self: 'T', x: 'T') -> bool:
pass
#abstractmethod
def __gt__(self: 'T', x: 'T') -> bool:
pass
T = TypeVar('T', bound=Comparable)
def _swap(items: List[T], i: int, j: int):
tmp = items[i]
items[i] = items[j]
items[j] = tmp
def knuths_shuffle(items: List[T]):
for i in range(len(items) - 1, 1, -1):
j = randint(0, i)
_swap(items, i, j)
return items
def merge(items: List[T], low: int, mid: int, high: int):
left_q = deque(items[low: mid])
right_q = deque(items[mid: high])
def put(q: Deque[T]):
nonlocal low
items[low] = q.popleft()
low += 1
while left_q and right_q:
put(left_q if left_q[0] < right_q[0] else right_q)
def put_all(q: Deque[T]):
while q:
put(q)
put_all(left_q)
put_all(right_q)
return items
def mergesort(items: List[T], low: int, high: int):
if high - low <= 1:
return
mid = (low + high) // 2
mergesort(items, low, mid)
mergesort(items, mid, high)
merge(items, low, mid, high)
def sort(items: List[T]) -> List[T]:
"""
>>> for i in range(100):
... rand = knuths_shuffle(list(range(100)))
... assert sorted(rand) == sort(rand)
"""
mergesort(items, 0, len(items))
return items

Categories

Resources