**π‘ Problem Formulation:** The bisect algorithm is used for locating the position where an element should be inserted within a sorted list to maintain the sorted order. For example, if we have a list `[1, 2, 4, 5]`

and we want to insert the element `3`

, bisect helps us find the correct index, which is `2`

in this case.

## Method 1: Using `bisect.bisect_left()`

The `bisect.bisect_left()`

function locates the insertion point for a specified value in a sorted list, to the left of any existing entries. It’s ideal for maintaining a sorted list and finding the index at which a value should be inserted before existing elements.

Here’s an example:

import bisect sorted_list = [1, 2, 4, 5] value_to_insert = 3 index = bisect.bisect_left(sorted_list, value_to_insert) print(index)

**Output:** `2`

The code snippet imports the `bisect`

module and then uses `bisect.bisect_left()`

to find the appropriate index where the value `3`

would fit in the sorted list while preserving the order. The function returns the index `2`

, which is correct as `3`

would indeed go between `2`

and `4`

.

## Method 2: Using `bisect.bisect_right()`

The `bisect.bisect_right()`

function, also known as `bisect.bisect()`

, finds the insertion point to the right of any existing entries. It is the counterpart of `bisect_left()`

for when equal elements should be positioned after the inserted value.

Here’s an example:

import bisect sorted_list = [1, 2, 3, 3, 4] value_to_insert = 3 index = bisect.bisect_right(sorted_list, value_to_insert) print(index)

**Output:** `4`

In this instance, the `bisect.bisect_right()`

function is used to determine where the value `3`

should be inserted in a list that already contains the value `3`

. The function suggests inserting the value after the existing threes, at index `4`

.

## Method 3: Using `bisect.insort_left()`

The `bisect.insort_left()`

function not only locates the insertion index but also inserts the value into the list at the correct position to the left of any existing entries that are equal to the value being inserted.

Here’s an example:

import bisect sorted_list = [1, 2, 4, 5] value_to_insert = 3 bisect.insort_left(sorted_list, value_to_insert) print(sorted_list)

**Output:** `[1, 2, 3, 4, 5]`

With `bisect.insort_left()`

, the list `sorted_list`

is modified in place by inserting the value `3`

before index `2`

. This maintains the list order and saves the user from having to insert the element manually.

## Method 4: Using `bisect.insort_right()`

The `bisect.insort_right()`

function performs a similar function to `insort_left()`

, but inserts items to the right of existing entries. It is often used when the insertion order of equal elements must be preserved.

Here’s an example:

import bisect sorted_list = [1, 3, 3, 4] value_to_insert = 3 bisect.insort_right(sorted_list, value_to_insert) print(sorted_list)

**Output:** `[1, 3, 3, 3, 4]`

Here, `bisect.insort_right()`

is used to add the value `3`

to the right of the existing threes. Unlike `bisect_left()`

, this ensures that the newly inserted element does not precede any identical elements already in the list.

## Bonus One-Liner Method 5: Custom bisect with key

Python’s bisect module lacks a built-in key function, but you can achieve this by using a wrapper object. This is particularly useful when you need to bisect complex structures like lists of tuples based on one of the tuple’s values.

Here’s an example:

import bisect class KeyedBisect: def __init__(self, key, list_of_tuples): self.key = key self.list_of_tuples = sorted(list_of_tuples, key=self.key) def find_pos(self, value): index = bisect.bisect_left([self.key(item) for item in self.list_of_tuples], value) return index sorted_tuples = [(1, 'a'), (2, 'b'), (4, 'd')] key_function = lambda x: x[0] kb = KeyedBisect(key_function, sorted_tuples) index = kb.find_pos(3) print(index)

**Output:** `2`

This code snippet provides a workaround to bisect lists based on a specific attribute. By wrapping the list and the key function in a class, we are able to apply `bisect_left()`

to the desired subset of data and get the correct index, which, in our example with tuples sorted by their first element, is `2`

.

## Summary/Discussion

**Method 1:**`bisect.bisect_left()`

. Precise insertion before equal values. Best for maintaining sorted order when you don’t want to insert duplicates ahead of identical items. Not suitable when equal elements should follow the inserted value.**Method 2:**`bisect.bisect_right()`

. Good for positioning new values after existing ones. Necessary when the order of insertion for equal elements counts. Cannot insert before similar items.**Method 3:**`bisect.insort_left()`

. Convenient for automatic insertion while maintaining sorted order. It alters the list in place which might not always be desired if the original list must remain unchanged.**Method 4:**`bisect.insort_right()`

. Ensures new equals are placed at the end, keeping an order of insertion. As with insort_left, it modifies the original list, which could be a drawback.**Bonus Method 5:**Custom bisect with key. Enables bisect functionality on complex data structures. Involves additional coding and may require the sorted list to be reconstructed if it changes often.