Bisect keep indexes of inserted items - python

I am using the bisect module to keep a list sorted while inserting numbers.
Lets say I have am going to insert three numbers 9, 2, 5 in this order.
The last state of this list would be obviously [2, 5, 9], however, is there any chance that I can find the index list that numbers are inserted into this list. For this list it would be [1, 2, 0]. So the list I need is the indexes [0, 1, 2] after the sort is happened which in bisect is in happening with each insertion, thats why I could not find a way. I could just sort it with key feature of the sorted function however I dont want to increase the complexity. So my question is this achievable with the bisect module ?
Here is the code I use,
import bisect
lst = []
bisect.insort(lst, 9)
bisect.insort(lst, 2)
bisect.insort(lst, 5)
print lst
Edit: Another example would be, i am going to insert the numbers 4, 7, 1, 2, 9 to some empty list. (Let's first assume without bisect, that I already have the numbers in the list)
[4, 7, 1, 2, 9]
# indexes [0, 1, 2, 3, 4], typical enumeration
after sorting,
[1, 2, 4, 7, 9]
# now the index list [2, 3, 0, 1, 4]
Can it be done with bisect without increasing complexity.
Note: The order of the insertion is not arbitrary. It is known, thats why I try to use indexes with bisect.

insort has no idea in what order the items were inserted. You'll have to add that logic yourself. One way to do so could be to insert 2-tuples consisting of the value and the index:
bisect.insort(lst, (9, 0))
bisect.insort(lst, (2, 1))
bisect.insort(lst, (5, 2))
You would need to keep track of the index yourself as you're adding objects, but as sequences are sorted first by the first item, then by the next, etc., this will still sort properly without any extra effort.

Related

if i have a list of characters, how would i return its most two frequencies with the less time complexity?

I'm using python language.
The clear algorithm will be enough for me.
I've tried using a dictionary, and counting the existence of each character if it is not in the list.
But I'm not sure if it has the possible less complexity.
Use the in built Counter(list).most_common(n) method, as below.
from collections import Counter
input_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 5, 7, 3, 1]
most_common_values = [value[0] for value in Counter(input_list).most_common(2)]
print(most_common_values)
This outputs: [1, 2].
The advantages to this approach are that it is fast, simple, and returns a list of the items in order. In addition, if their is a 'tie' in value count, it will return the example that appears first, as displayed in the example above.
Use built-int Counter in collection library

How to sort iterable with Python without using stable sort?

So, I have an iterable in Input like this:
[4, 6, 2, 2, 6, 4, 4, 4]
And I want to sort it based on decreased frequency order. So that the result will be this:
[4, 4, 4, 4, 6, 6, 2, 2]
So what happened here is that, when an element has the same frequency of another one, they will be in the same order (6 appeared first so the 6 goes before the 2).
I tried to implement this mechanism using the sorted function but I have a big problem.
def frequency_sort(items):
return sorted(items, key=lambda elem: sum([True for i in items if i == elem]), reverse=True)
I know this short way is difficult to read but it just sort the array using the key parameter to extract the frequency of a number. But, the output is this:
[4, 4, 4, 4, 6, 2, 2, 6]
As you can see the output is a little different from what it should be. And that happened (I think) because sorted() is a function that does a "stable sort" i.e. a sort that will keep the order as it is if there are same keys.
So what is happening here is like a strong stable sort. I want more like a soft-sort that will take into account the order but will put the same elements next to each other.
You could use collections.Counter and use most_common that returns in descending order of frequency:
from collections import Counter
def frequency_sorted(lst):
counts = Counter(lst)
return [k for k, v in counts.most_common() for _ in range(v)]
result = frequency_sorted([4, 6, 2, 2, 6, 4, 4, 4])
print(result)
Output
[4, 4, 4, 4, 6, 6, 2, 2]
From the documentation on most_common:
Return a list of the n most common elements and their counts from the
most common to the least. If n is omitted or None, most_common()
returns all elements in the counter. Elements with equal counts are
ordered in the order first encountered

How to reorder a python list backwards starting with the 0th element?

I'm trying to go through a list in reverse order, starting with the -0 indexed item (which is also the 0th item), rather than the -1 indexed item, so that I'll now have the new list to use. I've come up with two ways to do this, but neither seems both concise and clear.
a_list = [1, 2, 3, 4, 5]
print(a_list[:1] + a_list[:0:-1]) # take two slices of the list and add them
# [1, 5, 4, 3, 2]
list_range = range(-len(a_list)+1,1)[::-1] # create an appropriate new index range mapping
print([a_list[i] for i in list_range]) # list comprehension on the new range mapping
# [1, 5, 4, 3, 2]
Is there a way in python 3 to use slicing or another method to achieve this more simply?
If you are up for a programming golf:
>>> a_list = [1, 2, 3, 4, 5]
>>> [a_list[-i] for i in range(len(a_list))]
[1, 5, 4, 3, 2]
I think your first suggestion is the cleanest way of doing this. If you're really optimizing for character count, you can remove two characters from the first slice:
print(a_list[:1] + a_list[:0:-1])
Shift everything left by one and reverse.
my_list.append(my_list.pop(0))
print my_list[::-1]

Why is the range loop in bubble sort reversed?

I am new to Python and learning data structure in Python. I am trying to implement a bubble sort algorithm in python and I did well but I was not getting a correct result. Then I found some tutorial and there I saw that they are first setting a base range for checking.
So the syntax of range in python is:
range([start], stop[, step])
And the bubble sort algorithm is:
def bubbleSort(alist):
for i in range(len(alist) - 1, 0, -1):
for j in range(i):
if alist[j] > alist[j+1]:
temp = alist[j]
alist[j] = alist[j+1]
alist[j+1] = temp
return alist
print(bubbleSort([5, 1, 2, 3, 9, 8, 0]))
I understood all the other logic of the algorithm but I am not able to get why the loop is starting from the end of the list and going till first element of the list:
for i in range(len(alist) - 1, 0, -1):
Why is this traversing the list in reverse? The main purpose of this loop is setting the range condition only so why can't we traverse from the first element to len(list) - 1 like this:
for i in range(0, len(alist) - 1, 1):
In your code, the index i is the largest index that the inner loop will consider when swapping the elements. The way bubble sort works is by swapping sibling elements to move the largest element to the right.
This means that after the first outer iteration (or the first full cycle of the inner loop), the largest element of your list is positioned at the far end of the list. So it’s already in its correct place and does not need to be considered again. That’s why for the next iteration, i is one less to skip the last element and only look at the items 0..len(lst)-1.
Then in the next iteration, the last two elements will be sorted correctly, so it only needs to look at the item 0..len(lst)-2, and so on.
So you want to decrement i since more and more elements at the end of the list will be already in its correct position and don’t need to be looked at any longer. You don’t have to do that; you could also just always have the inner loop go up to the very end but you don’t need to, so you can skip a few iterations by not doing it.
I asked why we are going reverse in the list like len(list)-1,0. Why are we not going forward way like 0,len(list)-1?
I was hoping that the above explanation would already cover that but let’s go into detail. Try adding a print(i, alist) at the end of the outer loop. So you get the result for every iteration of i:
>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
6 [1, 3, 5, 2, 8, 0, 9]
5 [1, 3, 2, 5, 0, 8, 9]
4 [1, 2, 3, 0, 5, 8, 9]
3 [1, 2, 0, 3, 5, 8, 9]
2 [1, 0, 2, 3, 5, 8, 9]
1 [0, 1, 2, 3, 5, 8, 9]
As you can see, the list will be sorted from the right to the left. This works well for our index i which will limit how far the inner loop will go: For i = 4 for example, we already have 3 sorted elements at the end, so the inner loop will only have to look at the first 4 elements.
Now, let’s try changing the range to go in the other direction. The loop will be for i in range(0, len(alist)). Then we get this result:
>>> bubbleSort([5, 1, 3, 9, 2, 8, 0])
0 [5, 1, 3, 9, 2, 8, 0]
1 [1, 5, 3, 9, 2, 8, 0]
2 [1, 3, 5, 9, 2, 8, 0]
3 [1, 3, 5, 9, 2, 8, 0]
4 [1, 3, 5, 2, 9, 8, 0]
5 [1, 3, 2, 5, 8, 9, 0]
6 [1, 2, 3, 5, 8, 0, 9]
As you can see, this is not sorted at all. But why? i still limits how far the inner loop will go, so at i = 1, the loop will only look at the first pair and sort that; the rest will stay the same. At i = 2, the loop will look at the first two pairs and swap those (once!); the rest will stay the same. And so on. By the time the inner loop can reach the last element (which is only on the final iteration), there aren’t enough iterations left to swap the zero (which also happens to be the smallest element) to the very left.
This is again because bubble sort works by sorting the largest elements to the rightmost side first. So we have to start the algorithm by making the inner loop be able to reach that right side completely. Only when we are certain that those elements are in the right position, we can stop going that far.
There is one way to use a incrementing outer loop: By sorting the smallest elements first. But this also means that we have to start the inner loop on the far right side to make sure that we check all elements as we look for the smallest element. So we really have to make those loops go in the opposite directions.
It's because when you bubble from the start of the list to the end, the final result is that the last item in the list will be sorted (you've bubbled the largest item to the end). As a result, you don't want to include the last item in the list when you do the next bubble (you know it's already in the right place). This means the list you need to sort gets shorter, starting at the end and going down towards the start. In this code, i is always the length of the remaining unsorted list.
You can use this for:
for i in range(0,len(alist)-1,1):
but consequently you should change your second iteration:
for j in range(0,len(alist)-i,1):
I think the purpose of using reverse iteration in the first line is to simplify the second iteration. This is the advantage of using python
as #Jeremy McGibbon's answer, the logic behind bubble sort is to avoid j reach the "sorted part" in the behind of list. When using the example code, j range will be decreased as the value of i decrease. When you change i to increasing, you should handle j iteration differently
You can write the code as follow
lst = [9,6,5,7,8,3,2,1,0,4]
lengthOfArray = len(lst) - 1
for i in range(lengthOfArray):
for j in range(lengthOfArray - i):
if lst[j] > lst[j + 1]:
lst[j], lst[j + 1] = lst[j + 1], lst[j]
print(lst)

Understanding the set() function

In python, set() is an unordered collection with no duplicate elements. However, I am not able to understand how it generates the output.
For example, consider the following:
>>> x = [1, 1, 2, 2, 2, 2, 2, 3, 3]
>>> set(x)
set([1, 2, 3])
>>> y = [1, 1, 6, 6, 6, 6, 6, 8, 8]
>>> set(y)
set([8, 1, 6])
>>> z = [1, 1, 6, 6, 6, 6, 6, 7, 7]
>>> set(z)
set([1, 6, 7])
Shouldn't the output of set(y) be: set([1, 6, 8])? I tried the above two in Python 2.6.
Sets are unordered, as you say. Even though one way to implement sets is using a tree, they can also be implemented using a hash table (meaning getting the keys in sorted order may not be that trivial).
If you'd like to sort them, you can simply perform:
sorted(set(y))
which will produce a sorted list containing the set's elements. (Not a set. Again, sets are unordered.)
Otherwise, the only thing guaranteed by set is that it makes the elements unique (nothing will be there more than once).
Hope this helps!
As an unordered collection type, set([8, 1, 6]) is equivalent to set([1, 6, 8]).
While it might be nicer to display the set contents in sorted order, that would make the repr() call more expensive.
Internally, the set type is implemented using a hash table: a hash function is used to separate items into a number of buckets to reduce the number of equality operations needed to check if an item is part of the set.
To produce the repr() output it just outputs the items from each bucket in turn, which is unlikely to be the sorted order.
As +Volatility and yourself pointed out, sets are unordered. If you need the elements to be in order, just call sorted on the set:
>>> y = [1, 1, 6, 6, 6, 6, 6, 8, 8]
>>> sorted(set(y))
[1, 6, 8]
Python's sets (and dictionaries) will iterate and print out in some order, but exactly what that order will be is arbitrary, and not guaranteed to remain the same after additions and removals.
Here's an example of a set changing order after a lot of values are added and then removed:
>>> s = set([1,6,8])
>>> print(s)
{8, 1, 6}
>>> s.update(range(10,100000))
>>> for v in range(10, 100000):
s.remove(v)
>>> print(s)
{1, 6, 8}
This is implementation dependent though, and so you should not rely upon it.
After reading the other answers, I still had trouble understanding why the set comes out un-ordered.
Mentioned this to my partner and he came up with this metaphor: take marbles. You put them in a tube a tad wider than marble width : you have a list. A set, however, is a bag. Even though you feed the marbles one-by-one into the bag; when you pour them from a bag back into the tube, they will not be in the same order (because they got all mixed up in a bag).

Categories