Insertion sort doesn't sort - python

I have attempted to create an insertion sort in python, however the list returned is not sorted. What is the problem with my code?
Argument given: [3, 2, 1, 4, 5, 8, 7, 9, 6]
Result: 2
1
3
6
4
7
5
8
9
Python code:
def insertion_sort(mylist):
sorted_list = []
for i in mylist:
posfound = 0 #defaults to 0
for j in range(len(sorted_list)):
if sorted_list[j] > i:
sorted_list.insert(j-1, i) #put the number in before element 'j'
posfound = 1 #if you found the correct position in the list set to 1
break
if posfound == 0: #if you can't find a place in the list
sorted_list.insert(len(sorted_list), i) #put number at the end of the list
return sorted_list

You need to change sorted_list.insert(j-1, i) to be sorted_list.insert(j, i) to insert before position j.
insert(j-1, ..) will insert before the previous element, and in the case where j=0 it'll wrap around and insert before the last element.
The Python data structures tutorial may be useful.

As Efferalgan & tzaman have mentioned your core problem is due to an off-by-one error. To catch these sorts of errors it's useful to print i, j and sorted_list on each loop iteration to make sure they contain what you think they contain.
Here are a few versions of your algorithm. First, a repaired version of your code that fixes the off-by-one error; it also implements Efferalgan's suggestion of using .append if an insertion position isn't found.
def insertion_sort(mylist):
sorted_list = []
for i in mylist:
posfound = 0 #defaults to 0
for j in range(len(sorted_list)):
if sorted_list[j] > i:
sorted_list.insert(j, i) #put the number in before element 'j'
posfound = 1 #if you found the correct position in the list set to 1
break
if posfound == 0: #if you can't find a place in the list
sorted_list.append(i) #put number at the end of the list
return sorted_list
Here's a slightly improved version that uses an else clause on the loop instead of the posfound flag; it also uses slice assignment to do the insertion.
def insertion_sort(mylist):
sorted_list = []
for i in mylist:
for j in range(len(sorted_list)):
if sorted_list[j] > i:
sorted_list[j:j] = [i]
break
else: #if you can't find a place in the list
sorted_list.append(i) #put number at the end of the list
return sorted_list
Finally, a version that uses enumerate to get the indices and items in sorted_list rather than a simple range loop.
def insertion_sort(mylist):
sorted_list = []
for u in mylist:
for j, v in enumerate(sorted_list):
if v > u:
sorted_list[j:j] = [u]
break
else: #if you can't find a place in the list
sorted_list.append(u) #put number at the end of the list
return sorted_list

As often, it was a off-by-one error, the code below is fixed. I also made some parts a bit prettier.
def insertion_sort(mylist):
sorted_list = []
for i in mylist:
for index, j in enumerate(sorted_list):
if j > i:
sorted_list.insert(index, i) #put the number in before element 'j'
break
else:
sorted_list.append(i) #put number at the end of the list
return sorted_list

Related

Sum of lowest numbers that are part of an arithmetic sequence

I would like to iterate through a list of integers, calculate the sum of the lowest numbers that belongs to an arithmetic sequence with common difference 1 + the numbers that are not part of a sequence:
mylist = [2,3,4,10,12,13]
So, from mylist it would be 2 (from 2,3,4) + 10 (not part of a sequence) + 12 (from 12,13)
I've managed to make something work, but I could only figure out how to do it, if the list is reversed. And I am sure there is a better/cleaner solution that mine:
mylist = [13,12,10,4,3,2]
result = mylist[-1] #if I don't do this, I don't know how to grab the last item in the list
for i in range(len(mylist)-1):
if mylist[i] - mylist[i+1] == 1:
continue
else:
result += mylist[i]
Hope someone will help me out, and that I get a little wiser in my coding journey. Thanks.
#KennethRasch - earlier post would work. Alternatively, you can do this as well:
Try to find each possible subsequence's starting numbers as starts then just sum them to get answer.
L = [13,12,10,4,3,2]
L.sort()
starts = [x for x in L if x-1 not in L]
print(starts)
result = sum(starts)
print(result) # 24
Alternatively, you can put this into a function for future re-use it:
def sum_lowests(L):
'''L is a numbers sequence '''
L.sort()
starts = [x for x in L if x-1 not in L]
#ans = sum(starts) # can skip this step; just to make it clear
return sum(starts)
if __name__ == '__main__':
L = [13,12,10,4,3,2]
A = [1, 2, 3, 5,6, 9,10,11, 16]
print(sum_lowests(L))
print(sum_lowests(A)) # 31
Keep a running sum, a running index, and iterate while it's still a sequence:
mylist = [13,12,10,4,3,2]
mylist.sort() # this way sequences will be contiguous in the list
cur_index = 0
cur_sum = 0
while cur_index < len(mylist):
# add the current element to the sum
cur_sum += mylist[cur_index]
# now iterate through while it's a contiguous sequence
cur_index += 1
while cur_index < len(mylist) and mylist[cur_index] == mylist[cur_index - 1] + 1:
cur_index += 1
print(cur_sum)

How to create a new list from a previous list where elements are added to their position if the position is even?

Define a function named addPosToElem(...) which receives a list with integer numbers and returns a new list containing the elements form the original list in even positions where the position is added to the element. The list can possibly be empty
As an example, the following code fragment:
lst = [0,10,20,30,40,50]
print(addPosToElem(lst))
gives the output:
[0, 22, 44]
My code:
def addPosToElem(lst):
for i in range(lst[0], len(lst)):
if (lst[i] % 2) == 1:
element_odd = int(lst[i])
elif (lst[i] % 2) == 0:
element_even = int(lst[i]) + lst.index(i)
new_lst = element_odd + element_even
return new_lst
I keep getting 1 is not in list, how to fix my code?
The most pythonic way to do this is with list comprehension:
[num + place for place, num in enumerate(lst) if place % 2 == 0]
# [0, 22, 44]
Or with regular loop:
new_lst = []
for place in range(len(lst)):
if place % 2 == 0:
new_lst.append(lst[place] + place)
new_lst # [0, 22, 44]
This will do what you want. It isn't as 'good' as list comprehension but may be slightly more readable for a beginner.
def addPosToElem(lst):
new_lst = []
for i in range(len(lst)):
if (i % 2) == 0:
new_lst.append(lst[i] + i)
return new_lst
There are a few error with this approach:
As mentioned in the comment, you are checking lst[i]%2, you should check i%2 to get the indices.
You are checking lst.index(i) which fails in the 2nd iteration, because i is 1 and 1 is not in list.
You are not using the element_odd and element_odd as lists, so even if somehow your algorithm worked you would get two values as output.
Instead try this:
def addPosToElem(lst):
return [index+value for index, value in enumerate(lst) if index%2 == 0]
Your fixed code will be:
def addPosToElem(lst):
new_lst = []
for i in range(lst[0], len(lst)):
if (i % 2) == 0:
element_even = int(lst[i]) + i
new_lst.append(element_even)
return new_lst

Minimum count to sort an array in Python by sending the element to the end

Here is the explanation of what I'm trying to say:-
Input:- 5 1 3 2 7
Output:- 3
Explanation:
In first move, we move 3 to the end. Our list becomes 5,1,2,7,3
In second move, we move 5 to the end. Our list becomes 1,2,7,3,5
In third move, we move 7 to the end. Our final list = 1,2,3,5,7
So, total moves are:- 3.
Here is what I tried to do, but failed.
a = [int(i) for i in input().split()]
count = 0
n = 0
while (n < len(a) - 1):
for i in range(0,n+1):
while (a[i] > a[i + 1]):
temp = a[i]
a.pop(i)
a.append(temp)
count += 1
n += 1
print(count, end='')
I'd like to request your assistance in helping in solving this question.
jdehesa's answer is basically right, but not optimal for cases, when there is more element of same value. Maybe more complex solution?
def min_moves(a):
c = 0
while(1):
tmp = None
for i in range(0, len(a)):
if a[i] != min(a[i:]) and (tmp is None or a[i] < a[tmp]):
tmp = i
if tmp is None:
return c
else:
a.append(a.pop(tmp))
c += 1
Edit:
Or if you don't need ordered list, there's much more easier solution just to count items that are out of order for the reason from jdehesa's solution :-D
def min_moves(a):
c = 0
for i in range(0, len(a)):
if a[i] != min(a[i:]):
c += 1
return c
Edit 2:
Or if you like jdehesa's answer more, small fix is to reduce lst to set, so it will get smallest index
sorted_index = {elem: i for i, elem in enumerate(sorted(set(lst)))}
I cannot comment yet.
I don't know if it can be done better, but I think the following algorithm gives the right answer:
def num_move_end_sort(lst):
# dict that maps each list element to its index in the sorted list
sorted_index = {elem: i for i, elem in enumerate(sorted(lst))}
moves = 0
for idx, elem in enumerate(lst):
if idx != sorted_index[elem] + moves:
moves += 1
return moves
print(num_move_end_sort([5, 1, 3, 2, 7]))
# 3
The idea is as follows. Each element of the list would have to be moved to the end at most once (it should be easy to see that a solution that moves the same element to the end more than once can be simplified). So each element in the list may or may not need to be moved once to the end. If an element does not need to be moved is because it ended up in the right position after all the moves. So, if an element is currently at position i and should end up in position j, then the element will not need to be moved if the number of previous elements that need to be moved, n, satisfies j == i + n (because, after those n moves, the element will indeed be at position j).
So in order to compute that, I sorted the list and took the indices of each element in the sorted list. Then you just count the number of elements that are not in the right position.
Note this algorithm does not tell you the actual sequence of steps you would need to take (the order in which the elements would have to be moved), only the count. The complexity is O(n·log(n)) (due to the sorting).
I think you can simplify your problem,
Counting elements that need to be pushed at the end is equivalent to counting the length of the elements that are not in sorted order.
l = [5, 1, 3, 2, 7]
sorted_l = sorted(l)
current_element = sorted_l[0]
current_index = 0
ans = 0
for element in l:
if current_element == element:
current_index += 1
if current_index < len(l):
current_element = sorted_l[current_index]
else:
ans += 1
print(ans)
Here the answer is 3

Counting characters in a list

Write a function called count_numbers that takes in a list containing numbers and returns the count of numbers in the list. Your program has to cater to the possibility of having a list of numbers as element(s) in the input parameter. You can assume that the input parameter is at the most a 2-dimensional list.
Here is the output when file is run calling the function as:
>>> print("Count of numbers", count_numbers([4,6,[1,2],10,[-1,-3]]))
Count of numbers:7
My answer i got was 5 instead of 7.
here is my working:
def count_numbers(numbers):
result = []
for element in numbers:
for subelement in [element]:
result += [subelement]
results = len(result)
return results
If you want to do this manually, you are going to need to test the type of each element, to check if you are dealing with an int or a list, if a list you will have to create a deeper loop. After you count the element you will need to append it to a list, to check against so you don't count that same element again.
lst = [4,6,[1,2],10,[-1,-3]]
count = 0
seen = []
for i in lst:
if isinstance(i, list):
for j in i:
if j not in seen:
count += 1
seen.append(j)
else:
pass
else:
if i not in seen:
count += 1
seen.append(i)
else:
pass
print(count)
You can flatten your list and use collections.Counter as well
from collections import Counter
lst = [4,6,[1,2],10,[-1,-3]]
new_lst = []
for i in lst:
if isinstance(i, list):
new_lst.extend(i)
else:
new_lst.append(i)
c = Counter(new_lst)
print(c) # Counter({4: 1, 6: 1, 1: 1, 2: 1, 10: 1, -1: 1, -3: 1})
print(sum(c.values())) # 7
not quite sure what you what to do, but i guess this should work:
for elem in line:
if type(elem) == int:
result += 1
elif type(elem) == list:
for sub in elem:
if type(sub) == int:
result += 1
bear in mind that this code is realy ugly ;) but it should help get you started

List Index out of range error. Python

lst = []
for i in range(1,13196):
if 13195 % i == 0:
lst.append(i)
for k in range(len(lst)):
for j in range(1,lst[k]):
if lst[k] % j != 0:
lst.remove(lst[k])
print(lst)
When I run this code, it says if lst[k] % j != 0: List index out of range.
Can anyone tell me what I did wrong ?
You are iterating over a range of the current length of lst. Every time you do lst.remove(lst[k]), the lst gets shorter, so the range you were using becomes unsuitable. For example, if you start with 8 elements, it goes from indices of 0 to 7 inclusive, but if you remove an element, the last index will then be 6, not 7.
The easiest way to fix this is to simply create a new list:
lst = []
for i in range(1,13196):
if 13195 % i == 0:
lst.append(i)
result = lst[:] # create a copy to work with
for k in range(len(lst)):
for j in range(1,lst[k]):
if lst[k] % j != 0 and lst[k] in result: # check that it's there before removal
result.remove(lst[k])
print(result)
That said, this code eliminates every number which cannot be evenly divided by every positive integer smaller than that number, which understandably produces a result of [1]. If that's not what you wanted, you'll have to take another look at your algorithm.
lst = []
lst2 = []
for i in range(1,13196):
if 13195 % i == 0:
lst.append(i)
lst2.append(i)
for k in range(len(lst)):
for j in range(1,lst[k]):
if lst[k] % j != 0:
lst2.remove(lst[k])
print lst2
Give this a try. Your problem is that you were iterating over the value of the original list. When you removed parts of the original list, the length of this value shortened, but the for loop will not account for it.

Categories

Resources