I was trying to implement the insertion sort algorithm in python and was able to code it as below, I am not getting the whole array sorted and was wondering if this implementation has the proper thought process behind it, if not I would greatly appreciate if someone can help me in understanding it as the function is considering the sorted part of the array when inserting an element from the unsorted part. Also, in your review kindly consider if the implementation is correct and if so what can make my solution output correct.
def insertion_sort(array):
for i in range(1, len(array)):
for j in reversed(range(1, i)):
if array[j-1] > array[j]:
temp = array[j-1]
array[j-1] = array[j]
array[j] = temp
return array
to_sort = [4, 3, 1, 5, 6, 2]
print(insertion_sort(to_sort))
This is the output I got:
[1, 3, 4, 5, 6, 2]
tl;dr:
Insertion Sort algorithm not giving perfect output. Is implementation correct and what can be done to fix the output.
The goal of your function would be to shift array[i] into the sorted partition, but it actually does that for array[i-1], because the inner loop starts with j equal to i-1.
So the easy fix is to change:
for j in reversed(range(1, i)):
to:
for j in reversed(range(1, i + 1)):
Improvements
More Pythonic would be to use the capability of range to produce a descending range:
for j in range(i, 0, -1):
When the if condition is not true, it is useless to continue the inner loop, as then it is guaranteed that the if condition will never be true in the rest of the inner loop's iterations. So add a break:
if array[j-1] <= array[j]:
break
temp = array[j-1]
array[j-1] = array[j]
array[j] = temp
As these swaps will always move the same value to the left, i.e. array[j] will always be the value that originally was at array[i], it is less costly to first find the index where array[i] should be moved to, and then perform a single rotation to get it there.
def insertion_sort(array):
for i in range(1, len(array)):
temp = array[i]
for k in range(i - 1, -1, -1):
if array[k] <= temp:
break
else:
k = -1
# rotate
array[k+2:i+1] = array[k+1:i]
array[k+1] = temp
return array
I used k here, so not to confuse it with the meaning of j in the original code: k is j - 1.
This search for k (or j) can be done with a call to next:
def insertion_sort(array):
for i in range(1, len(array)):
temp = array[i]
j = 1 + next((k for k in range(i - 1, -1, -1) if array[k] <= temp), -1)
array[j+1:i+1] = array[j:i]
array[j] = temp
return array
You are never touching the last element of the array, I suggest changing len(array) with len(array)+1, i.e.,
def insertion_sort(array):
for i in range(1, len(array)+1):
for j in reversed(range(1, i)):
if array[j-1] > array[j]:
temp = array[j-1]
array[j-1] = array[j]
array[j] = temp
return array
This is because i has maximum value len(array)-1 and j has as maximum value i-1, which is len(array)-2.
However, the last element in the array has index len(array)-1.
Related
my method for sorting an array written in python:
array = [3, 2, 1]
for i in range(len(array)):
minVal = array[i]
for j in range(i + 1, len(array)):
if (array[j] < array[i]):
if (array[j] <= minVal):
minVal = array[j]
if i < len(array) - 1:
array[i + 1] = array[i]
array[i] = minVal
print(array)
how do i think it works?
the first cycle will be executed 3 times
first iteration:
#i will check number 3 with numbers 2 and 1
#expected output:
minVal = 1
array = [1, 3, 2]
second iteration:
#i will check number 3 with number 2
#expected output:
minVal = 2
array = [1, 2, 3]
last iteration:
#I do not know if he is needed.
#perhaps it is worth writing `range(len(array) - 1)` in the first loop?
#and because of this iteration, I also added the check `if i <len (array) - 1:`
expected output at the end: [1, 2, 3]
what i got: [1, 1, 3]
Think about your logic: You find the minimum value and then replace the i item with it but only advance the i item one index ahead to i+1. By doing so, after the first iteration you have the list as [1, 3, 1]. Let's break it down:
you start with [3, 2, 1]
minVal = array[i] = 3
After the j loop we find that minVal = 1
Now we do array[i + 1] = array[i] -> array[1] = 3. So now we have [3, 3, 1].
Now we do array[i] = minVal -> array[0] = 1. So now we have [1, 3, 1].
The element 2 has disappeared from the list!
To fix this, you need to save the minVal's index and replace the two items, not override another item:
array = [3, 2, 1]
for i in range(len(array)):
minIndex = i
for j in range(i + 1, len(array)):
if (array[j] < array[i]):
if (array[j] <= array[minIndex]):
minIndex = j
minVal = array[minIndex]
if i < len(array) - 1:
array[minIndex] = array[i]
array[i] = minVal
print(array)
Now there are some points to simplify and improve your code:
There is no need to check that array[j] < array[i] on every iteration - in the first iteration it is equal to minVal/array[minIndex] anyway, so you can remove one if.
There is no need for the last if - instead of checking every iteration if we got to the last element, just remove it from the loop - for i in range(len(array)-1).
The replacement can be done in one line without the need to even save minVal - see this SO question.
So the improved version according to the above can be:
array = [3, 2, 1]
for i in range(len(array)-1):
minIndex = i
for j in range(i + 1, len(array)):
if array[j] < array[minIndex]:
minIndex = j
array[minIndex], array[i] = array[i], array[minIndex]
print(array)
You are currently searching an array for the minimum value including and past the current index. This is an O(N^2) approach, and quite robust. However, you need to implement it correctly.
The check
if (array[j] < array[i]):
if (array[j] <= minVal):
is redundant. You shouldn't restrict your test by the original minimum value, although it does no harm, since minVal <= array[i] in all cases.
You don't really care about the value of minVal as much as you do about its location. You more-or-less correctly identify minVal as array[j], but then you swap it out as array[i + 1] = array[i]. Where is the correct j recorded?
Here is the same idea, but without the extra check, and using minJ to illustrate the caching of j vs the value:
array = [3, 2, 1]
for i in range(len(array) - 1): # Don't bother iterating the last element
min_j = i # Only record indices
for j in range(i + 1, len(array)):
if array[j] < array[min_j]: # Only test current minimum
min_j = j
array[i], array[min_j] = array[min_j], array[i]
print(array)
Couple of small tweaks:
You don't need to iterate over the last element, so shorten the outer range by one.
The idiomatic way to swap two quantities in python is x, y = y, x. If you're going to use a temporary variable anyway, it may as well be a tuple that enhances legibility.
I´m working on a simple bubble sort script. I've tried several test cases, but some of them doesn't order the numbers as expected. Here is my code with one test case that does not work.
def bubble_sort(array):
i = 0
j = 1
for x in range(0, len(array)):
for y in range(1000):
if(i == 4 and j == 5):
i, j = 0, 1
if(array[i] <= array[j]):
pass
if(array[i] > array[j]):
array[i], array[j] = array[j], array[i]
#print(array)
i, j = i + 1, j + 1
pass
pass
return(array)
I´m passing this list to the code
[7, 3, 1, 2, 3, 3, 10, 15, 2]
And the output is
[1, 2, 3, 3, 7, 3, 10, 15, 2]
I can´t find the mistake on the code, although I think is in the number and logic of the iterations.
Hope someone can help me.
This is just a fixed up version of your code:
I don't understand why your loops use x and y as enumeration values in the for-loops, but then you use another pair of indices: i and j. The i and j don't seem needed.
if(i == 4 and j == 5) - not needed. Is this just a debugging thing?
pass is just a no-op. I don't think you need it.
It's spelled bubble, not buble
It gets simple pretty fast:
def bubble_sort(array):
for x in range(0, len(array)):
for y in range(x+1, len(array)):
if(array[x] > array[y]):
array[x], array[y] = array[y], array[x]
return(array)
Here is different version of your code.
def bubbleSort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
After this codes, you need to initialize(create) your array, then call your def like this; bubbleSort(arr) after all this things you can print your array in for loop.
for i in range(len(arr)):
print(arr[i])
Hello i have been struggling with implementing this selection sort for quite some days now. I feel my code is close to it, but can't figure out why am not getting it.
Here is my code with comments
def selectionSort(aList):
#For each index in the list...
for i in range(len(aList)):
#Assume first that current item is already correct...
minIndex = i
#For each index from i to the end...
for j in range(i + 1, len(aList)):
if aList[j] >= aList[j - 1]:
break
aList[j], aList[j - 1] = aList[j - 1], aList[j]
minIndex = aList.index(aList[j - 1])
#Save the current minimum value since we're about
#to delete it
minValue = aList[minIndex]
#Delete the minimum value from its current index
del aList[minIndex]
#Insert the minimum value at its new index
aList.insert(i, minValue)
#Return the resultant list
return aList
This is the result am getting
[4, 2, 1, 3, 5]
Instead of this:
[1, 2, 3, 4, 5]
Thanks for your help in advance
for j in range(i + 1, len(aList)):
if aList[j] >= aList[j - 1]:
break
aList[j], aList[j - 1] = aList[j - 1], aList[j]
minIndex = aList.index(aList[j - 1])
Selection sort is sorting by iteratively finding the minimum element in the list. Just set the first element as minimum, iterate through the list, if the current element smaller than the minimum then record it as the minimum and record its index. The part after is correct.
you don't need to delete and insert, just swap em!
def selectionSort(aList):
#For each index in the list (not the last one)
for i in range(len(aList)-1):
#initialized minIndex
minIndex = i
#For each index from i+1 to the end...
for j in range(i + 1, len(aList)):
#find the min of the list and update minIndex
if aList[j] < aList[minIndex]:
minIndex = j;
#if minIndex changed, swap i and minIndex values
if minIndex != i:
aList[i], aList[minIndex] = aList[minIndex], aList[i]
#Return the resultant list
return aList
Here is the working code guys:
def selectionSort(aList):
#For each index in the list...
for i in range(len(aList)):
minIndex = i
#For each index from i+1 to the end...
for j in range(i + 1, len(aList)):
if aList[minIndex] > aList[j]:
minIndex = j
#Save the current minimum value since we're about
#to delete it
minValue = aList[minIndex]
#Delete the minimum value from its current index
del aList[minIndex]
#Insert the minimum value at its new index
aList.insert(i, minValue)
#Return the resultant list
return aList
Thanks once more.
Can't believe that just two lines of code gave me a nightmare. pheew
I tried implementing Insertion sort with for loops only and wrote the following code:
def isort(L): #implementation with a for loop
for i in range(1,len(L)):
small = L[i]
M = range(i)
M.reverse()
for j in M:
if small<L[j]:
L[j+1]=L[j]
else:
break
L[j+1] = small
return L
L = [5,4,3,2,1]
M = isort(L)
print M
This gives the output [5,1,2,3,4]. Can someone please point out where I am making a mistake
Change (the fix shown in the question is easy, the one-off error was caused by one little +1 :)):
L[j+1] = small
To:
L[j] = small
Testing:
>>> isort([5, 4, 3, 2, 1])
[1, 2, 3, 4, 5]
However, there are some other things with your code, as illustrated- it will not work alot of the time. With a fair few tweaks, we can get it to work:
def isort(L):
for i in range(1,len(L)):
small = L[i]
M = range(-1, i)
M.reverse()
for j in M:
if j>=0 and small<L[j]:
L[j+1]=L[j]
else:
break
L[j+1] = small
return L
Testing:
>>> isort([4, 5, 3, 2, 1])
[1, 2, 3, 4, 5]
The post condition for the inner loop is that j is pointing for the first value that is smaller than small (this is achieved by the break call). However, the loop naturally exists when j=0, therefore in every last inner iteration, the condition is not what you'd expect.
To fix it, I suggest initializing M from -1:
M = range(-1, i)
But then, you have to check as well that j is positive (to avoid making changes you don't want to):
if j>=0 and small<L[j]:
L[j+1]=L[j]
This is little tricky :
I took the inner loop range function as range(j, -2, -1) , so the inner loop always breaks at one position ahead, so the last statement arr[j + 1] = key works perfectly
arr = [5, 4, 3, 2, 1]
for i in range(1, len(arr)):
j = i - 1
key = arr[i]
for j in range(j, -2, -1):
if j < 0 or key >= arr[j]:
break
else:
arr[j + 1] = arr[j]
arr[j + 1] = key
if __name__ == "__main__":
n = int(input("How many numbers ?\t"))
nums = [int(x) for x in input("Enter {} numbers\t".format(n)).split()]
for i in range(1,n):
val = nums[i]
for j in range(i-1,-2,-1):
if j < 0 : break
if nums[j] > val:
nums[j+1] = nums[j]
else:
break
nums[j+1] = val
for num in nums:
print(num,end=' ')
print()
I have been trying to implement quicksort for like 2 days now (Looks like my programming skills are getting rusty). I do not know what I am doing wrong. I was about to give up so I thought I should consult the discussion forum.
here is the code that I am trying to implement in python. But it is not giving the desired result. Anyone can please point out what I am doing wrong?
def QuickSort(A,p,r):
if p < r:
pivotIndex = Partition(A,p,r)
QuickSort(A,p,pivotIndex-1)
QuickSort(A,pivotIndex+1,r)
return A
def Partition(A,p,r):
m = A[p]
i = p+1
for j in range( p+1 , r ):
if A[j] < m:
A[j] , A[i] = A[i] , A[j]
i+=1
A[p], A[i-1] = A[i-1] , A[p]
return i-1
The output for test input is:
>>>QuickSort([9,8,7,6,5,4,3,2,1],0,9)
[1, 3, 5, 6, 7, 4, 8, 2, 9]
I will be very thankful if anyone help me in implementing this.
Regards
Slicing doesn't return a view of the original list; it makes a new list out of data from the old list. That means the recursive calls to QuickSort don't change the original list.
You can try this implementation in one line of code:
def QuickSort(list):
return [] if list==[] else QuickSort([x for x in list[1:] if x < list[0]]) + [list[0]] + QuickSort([x for x in list[1:] if x >= list[0]])
print QuickSort([9,8,7,6,5,4,3,2,1])
I have figured out the answer. It appeared that I was passing one-less to the QuickSort method
def QuickSort(A,p,r):
if r-p <= 1: return
pivotIndex = Partition(A,p,r)
QuickSort(A,p,pivotIndex)
QuickSort(A,pivotIndex+1,r)
return A
def Partition(A,p,r):
m = A[p]
i = p+1
for j in range( p+1 , r ):
if A[j] < m:
A[j] , A[i] = A[i] , A[j]
i= i + 1
A[p], A[i-1] = A[i-1] , A[p]
return i-1
It is the correct implementation
It seems that you wanted to keep the pivot on the left side and skip it, but that didn't turn out well, so I just moved it to the end of the array and reduced the iteration index accordingly and then reversed the post-partition swap (to take into consideration the pivot movement).
def Partition(A,p,r):
m = A[p]
A[p], A[r] = A[r], A[p]
i = p
for j in range( p, r ):
if A[j] < m:
A[j] , A[i] = A[i] , A[j]
i+=1
A[r], A[i] = A[i] , A[r]
return i
I think that you could do it with the pivot on the left side, but then you would have to change the loop direction I guess, but I am not sure.
EDIT: Sorry I forgot to add the call is then QuickSort([9,8,7,6,5,4,3,2,1],0,8), since the indices are inclusive now.