Искать конкретный кортеж в списке кортежей, отсортированных по первому элементу?

Скажем, у меня есть список таких кортежей, где 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 ... или что-то подобное. Есть идеи?

bisect имеет ключевой аргумент начиная с Python 3.10. Какую версию Python вы используете?

ken 01.09.2024 14:53

Почему вы не используете дикт?

no comment 01.09.2024 14:57

@ Кен 3.10. Спасибо, я посмотрю, смогу ли я увидеть, как это работает для этого варианта использования (если вы не хотите дать ответ...).

mike rodent 01.09.2024 14:58

Что делать, если данное число не встречается или встречается несколько раз?

no comment 01.09.2024 14:58

Создание dict будет быстрее, чем сортировка.

Olivier 01.09.2024 14:59

@nocomment Могу, конечно. Но я хотел бы знать, есть ли способ сделать это без dict. Также в этом случае можно учесть ситуацию, когда два кортежа имеют одинаковое значение первого элемента.

mike rodent 01.09.2024 15:00
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
6
63
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы действительно можете использовать модуль 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)).

Olivier 01.09.2024 15:20

@Оливье Очевидно, Джон говорит только о поиске в уже отсортированном списке, именно так, как и предполагалось в вопросе.

no comment 01.09.2024 15:25

@nocomment Конечно, но вопрос включает этап сортировки (исходные данные не отсортированы). Построение словаря занимает O(n), а поиск словаря — O(1).

Olivier 01.09.2024 15:35

Я ценю твою страсть @Olivier, но давай проясним это. Да, сортировка — O(nlogn). Здесь никто не оспаривает базовые алгоритмы. Но, как вы могли заметить, вопрос конкретно касался поиска в отсортированном списке. Вот тут и применим мой комментарий O(logn). Мы здесь не для того, чтобы перефразировать «Сортировку 101», верно? ;) Так что давайте сохранять хладнокровие и сосредоточимся на помощи сообществу, я уверен, что вы с этим согласитесь.

John 01.09.2024 19:20
Ответ принят как подходящий

Повторно используйте ключ сортировки для разделения пополам:

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) действительно представляет собой элегантное решение.

mike rodent 01.09.2024 15:11

@mikerodent Да, я думаю, что это самый правильный/общий способ. Хотя в данном конкретном случае мне также нравится поиск кортежа Джоном (4,).

no comment 01.09.2024 15:22
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, если нет.

Другие вопросы по теме