Скажем, у меня есть список таких кортежей, где int — это «id» для этой цели:
records_by_id = [(10, 'bubble1'), (5, 'bubble2'), (4, 'bubble3'), (0, 'bubble4'), (3, 'bubble5'),]
... и я сортирую это по первому элементу кортежа:
records_by_id.sort(key = lambda x: x[0])
... это дает:
[(0, 'bubble4'), (3, 'bubble5'), (4, 'bubble3'), (5, 'bubble2'), (10, 'bubble1'),]
Теперь, учитывая число 4, как мне найти индекс списка «(4, 'bubble3')»? Очевидно, что эти кортежи теперь сортируются по первому элементу, поэтому перебор списка методом грубой силы не идеален. Я думаю, должен быть какой-то способ использовать bisect ... или что-то подобное. Есть идеи?
Почему вы не используете дикт?
@ Кен 3.10. Спасибо, я посмотрю, смогу ли я увидеть, как это работает для этого варианта использования (если вы не хотите дать ответ...).
Что делать, если данное число не встречается или встречается несколько раз?
Создание dict будет быстрее, чем сортировка.
@nocomment Могу, конечно. Но я хотел бы знать, есть ли способ сделать это без dict. Также в этом случае можно учесть ситуацию, когда два кортежа имеют одинаковое значение первого элемента.






Вы действительно можете использовать модуль bisect, чтобы найти индекс кортежа с указанным вами идентификатором.
import bisect
# sorting
records_by_id = [(0, 'bubble4'), (3, 'bubble5'), (4, 'bubble3'), (5, 'bubble2'), (10, 'bubble1')]
# id you wanna find
target_id = 4
# create a list of the first ids from the tuples
ids = [x[0] for x in records_by_id]
# use bisect_left to find the index where the target_id would be inserted to maintain sorted order and check if the id at that index is the target_id
index = bisect.bisect_left(ids, target_id)
if index < len(records_by_id) and records_by_id[index][0] == target_id:
print(f"Found at index: {index}")
else:
print("Not found")
Вы недалеко от фактического решения. Поскольку вы уже отсортировали список по первому элементу кортежа, вы действительно можете использовать модуль bisect, чтобы найти индекс кортежа по первому его элементу.
import bisect
def find_tuple_index(sorted_list, target_id):
index = bisect.bisect_left(sorted_list, (target_id,))
if index < len(sorted_list) and sorted_list[index][0] == target_id:
return index
return -1 # Not found
# Your sorted list
records_by_id = [(0, 'bubble4'), (3, 'bubble5'), (4, 'bubble3'), (5, 'bubble2'), (10, 'bubble1')]
# Search for the tuple with id 4
result = find_tuple_index(records_by_id, 4)
print(result) # Output: 2
Позвольте мне немного подробнее рассказать о коде.
Сначала мы используем bisect.bisect_left(), чтобы найти точку вставки для целевого идентификатора. Эта функция работает, потому что наш список сортируется по первому элементу каждого кортежа.
Теперь bisect_left() возвращает самое левое место в списке, куда мы можем вставить целевое значение, сохраняя порядок сортировки.
Затем мы проверяем, находится ли найденный индекс в границах списка и соответствует ли первый элемент кортежа по этому индексу нашему целевому идентификатору.
Если совпадение есть, мы возвращаем индекс, в противном случае мы возвращаем -1, чтобы сказать, что элемент не найден.
Кстати, я предположил, что идентификаторы в вашем списке уникальны. Если у вас могут быть повторяющиеся идентификаторы, вам нужно будет изменить функцию для обработки этого случая, возможно, вернув список всех совпадающих индексов.
Обновлено: сложность подхода составляет O (log n), кстати.
«сложность подхода равна O(log n)» Шаг сортировки — O(n.log(n)).
@Оливье Очевидно, Джон говорит только о поиске в уже отсортированном списке, именно так, как и предполагалось в вопросе.
@nocomment Конечно, но вопрос включает этап сортировки (исходные данные не отсортированы). Построение словаря занимает O(n), а поиск словаря — O(1).
Я ценю твою страсть @Olivier, но давай проясним это. Да, сортировка — O(nlogn). Здесь никто не оспаривает базовые алгоритмы. Но, как вы могли заметить, вопрос конкретно касался поиска в отсортированном списке. Вот тут и применим мой комментарий O(logn). Мы здесь не для того, чтобы перефразировать «Сортировку 101», верно? ;) Так что давайте сохранять хладнокровие и сосредоточимся на помощи сообществу, я уверен, что вы с этим согласитесь.
Повторно используйте ключ сортировки для разделения пополам:
from bisect import bisect_left
records_by_id = [(10, 'bubble1'), (5, 'bubble2'), (4, 'bubble3'), (0, 'bubble4'), (3, 'bubble5'),]
key = lambda x: x[0]
records_by_id.sort(key=key)
index = bisect_left(records_by_id, 4, key=key)
print(index)
Если возможно, что данное число не встречается, добавьте в конце проверку, проверяющую, действительно ли оно встречается по результирующему индексу.
Спасибо. Эта новая функция «сортировка/разделение по ключу» (начиная с версии 3.10) действительно представляет собой элегантное решение.
@mikerodent Да, я думаю, что это самый правильный/общий способ. Хотя в данном конкретном случае мне также нравится поиск кортежа Джоном (4,).
import bisect
records_by_id = [(0, 'bubble4'), (3, 'bubble5'), (4, 'bubble3'), (5, 'bubble2'), (10, 'bubble1')]
def find_tuple_index(records, target_id):
# Create a list of just the IDs for binary search
ids = [record[0] for record in records]
# Use bisect to find the insertion point
index = bisect.bisect_left(ids, target_id)
# Check if the index is valid and the ID matches
if index != len(records) and records[index][0] == target_id:
return index
return -1 # Not found
# Example usage
target_id = 4
result_index = find_tuple_index(records_by_id, target_id)
print(f"Tuple with ID {target_id} found at index: {result_index}")
Учитывая целевой идентификатор (например, 4), мне нужно эффективно найти индекс соответствующего кортежа в этом списке. В этом случае я хочу найти индекс
(4, «пузырь3»)
Линейный поиск не идеален, поскольку список уже отсортирован. И мы используем биссектрису.
Мой ответ импортирует биссектрису и использует
биссект.бисект.левый()
чтобы найти точку выхода целевого идентификатора.
Проверяет, действителен ли найденный индекс и соответствует ли идентификатор целевому индексу, возвращает индекс найден или -1, если нет.
bisect имеет ключевой аргумент начиная с Python 3.10. Какую версию Python вы используете?