Методы сортировки в языке программирования Python

Раздел: Алгоритмы -> Алгоритмы сортировки

Сортировка данных - одна из базовых операций при работе с коллекциями в Python. Стандартная библиотека предоставляет мощные и гибкие инструменты для упорядочивания элементов, которые подходят для большинства задач. Рассмотрим основные подходы, начиная с самого универсального и заканчивая специализированными сценариями.

Встроенные функции сортировки

sorted() - наиболее эффективное решение для получения новой отсортированной копии коллекции. Функция работает с любым итерируемым объектом (списки, кортежи, строки, множества, словари) и возвращает список. Алгоритм основан на Timsort, который имеет временную сложность O(n log n) в среднем и O(n) в лучшем случае для частично отсортированных данных.


# Пример: сортировка списка чисел по возрастанию
data = [5, 2, 9, 1, 5, 6]
sorted_data = sorted(data)
print(sorted_data)  # [1, 2, 5, 5, 6, 9]

алгоритмы сортировки python (алгоритмы сортировки в python)

[1, 2, 5, 5, 6, 9]

сортировка python примеры (примеры сортировки в python)

Пояснение: функция sorted() не изменяет исходный список data, а создает новый. Параметр reverse=True изменяет порядок на убывающий.

Типичные ошибки:

  • Попытка отсортировать список, содержащий элементы разных типов (например, числа и строки) - возникнет TypeError, так как Python не может сравнить их напрямую.
  • Забывают, что sorted() возвращает None при вызове без присваивания результата, хотя на самом деле это не так - функция всегда возвращает список.
  • Модификация списка во время итерации вместе с сортировкой может привести к непредсказуемому порядку.

Как отсортировать список на месте, не создавая новый объект?


data = [5, 2, 9, 1, 5, 6]
data.sort()
print(data)  # [1, 2, 5, 5, 6, 9]

быстрая сортировка python (реализация быстрой сортировки на python)

[1, 2, 5, 5, 6, 9]

сортировка выбором python (сортировка выбором в python)

Метод list.sort() изменяет сам список, не возвращая нового. Это позволяет экономить память, если исходный список больше не нужен в первоначальном порядке. Применим только к спискам, а не к другим итерируемым объектам.

Проблемы: Если требуется сохранить исходный порядок, лучше использовать sorted(). Также list.sort() не работает с кортежами или строками - для них применяют sorted() и затем преобразование.

Как отсортировать элементы по заданному правилу, например, по длине строки?


words = ['apple', 'kiwi', 'banana', 'pear']
sorted_by_len = sorted(words, key=len)
print(sorted_by_len)  # ['kiwi', 'pear', 'apple', 'banana']

сортировка данных python (сортировка данных в python)

['kiwi', 'pear', 'apple', 'banana']

Параметр key принимает функцию, которая применяется к каждому элементу перед сравнением. В примере len - встроенная функция, возвращающая длину строки. Итоговый порядок основан на этих значениях.

Частая ошибка: Использование key=len() с круглыми скобками - это приводит к вызову функции на этапе определения, что вызывает ошибку. Нужно передавать саму функцию без скобок.

Как выполнить сортировку по нескольким критериям: сначала по одному полю, затем по другому?


data = [('Alice', 25), ('Bob', 20), ('Alice', 30)]
sorted_multi = sorted(data, key=lambda x: (x[0], x[1]))
print(sorted_multi)  # [('Alice', 25), ('Alice', 30), ('Bob', 20)]
[('Alice', 25), ('Alice', 30), ('Bob', 20)]

Ключом выступает кортеж: сначала сравнивается первый элемент, при равенстве - второй. Можно комбинировать с reverse для отдельных полей, используя отрицательные значения для чисел или хитрости для строк.

Сложность: Если требуется для одного поля сортировка по возрастанию, а для другого - по убыванию, нужно применять reverse глобально или использовать functools.cmp_to_key с пользовательской функцией сравнения. Для чисел можно инвертировать значение: key=lambda x: (x[0], -x[1]).

Как отсортировать пользовательские объекты по одному из атрибутов?


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __repr__(self):
        return f'Person({self.name}, {self.age})'

people = [Person('Alice', 25), Person('Bob', 20), Person('Charlie', 30)]
from operator import attrgetter
sorted_people = sorted(people, key=attrgetter('age'))
print(sorted_people)  # [Person(Bob, 20), Person(Alice, 25), Person(Charlie, 30)]
[Person(Bob, 20), Person(Alice, 25), Person(Charlie, 30)]

operator.attrgetter - эффективный способ извлечения атрибута. Альтернатива - лямбда: key=lambda p: p.age. Если класс реализует __lt__, можно сортировать без key.

Проблема: Если атрибут отсутствует у некоторых объектов, возникнет AttributeError. Решение: использовать getattr со значением по умолчанию или проверять наличие.

Как отсортировать данные с помощью функции сравнения (для совместимости со старыми подходами)?


from functools import cmp_to_key

def compare(x, y):
    if x < y:
        return -1
    elif x > y:
        return 1
    else:
        return 0

data = [3, 1, 4, 1, 5, 9]
sorted_data = sorted(data, key=cmp_to_key(compare))
print(sorted_data)  # [1, 1, 3, 4, 5, 9]
[1, 1, 3, 4, 5, 9]

cmp_to_key преобразует функцию сравнения (принимающую два аргумента и возвращающую -1, 0, 1) в ключевую функцию. Этот способ менее производительнее, чем key, но полезен при портировании кода с Python 2 или для сложной логики сравнения.

Недостаток: Начиная с Python 3, cmp параметр удален, и cmp_to_key - единственный способ. Однако его использование увеличивает время сортировки, так как функция сравнения вызывается для каждой пары.

Как отсортировать строки с учетом локали (например, для букв с диакритическими знаками)?


import locale
locale.setlocale(locale.LC_ALL, 'ru_RU.UTF-8')

words = ['ёлка', 'яблоко', 'ананас', 'ёжик']
sorted_locale = sorted(words, key=locale.strxfrm)
print(sorted_locale)  # ['ананас', 'ёжик', 'ёлка', 'яблоко']
['ананас', 'ёжик', 'ёлка', 'яблоко']

Функция locale.strxfrm преобразует строку в форму, пригодную для сравнения с правилами локали. Без нее буква 'ё' может оказаться после 'я' из-за кодовой точки Unicode.

Проблема: Не все системы поддерживают нужную локаль. Если локаль не установлена, setlocale вызовет locale.Error. Альтернатива - использовать библиотеку pyuca или сортировку с помощью unicodedata.normalize.

Дополнительные примеры демонстрируют более сложные сценарии сортировки, включая работу с большими данными, нестандартные ключи и комбинирование нескольких подходов.

Пример

# Сортировка списка словарей по вложенному ключу
data = [
    {'name': 'Alice', 'info': {'age': 25}},
    {'name': 'Bob', 'info': {'age': 20}},
    {'name': 'Charlie', 'info': {'age': 30}}
]
sorted_data = sorted(data, key=lambda x: x['info']['age'])
print(sorted_data)
[{'name': 'Bob', 'info': {'age': 20}}, {'name': 'Alice', 'info': {'age': 25}}, {'name': 'Charlie', 'info': {'age': 30}}]

Пояснение: ключом выступает лямбда, которая извлекает возраст из вложенного словаря. Обратите внимание на безопасность: если ключ 'info' или 'age' отсутствует, возникнет KeyError. Для защиты используют dict.get.

Пример

# Сортировка с преобразованием типов: строки, представляющие числа
data = ['10', '2', '30', '1']
sorted_int = sorted(data, key=int)
print(sorted_int)  # ['1', '10', '2', '30']  (лексикографически без key было бы ['1', '10', '2', '30'])
['1', '10', '2', '30']

Параметр key=int преобразует строки в целые числа перед сравнением, что дает числовой порядок. Без key строки сравнивались бы лексикографически (сначала '1', потом '10', '2', '30').

Пример

# Устойчивость сортировки: сохранение относительного порядка равных элементов
pairs = [(1, 'z'), (2, 'a'), (1, 'y')]
sorted_pairs = sorted(pairs)
print(sorted_pairs)  # [(1, 'z'), (1, 'y'), (2, 'a')] - порядок между (1,'z') и (1,'y') сохранен
[(1, 'z'), (1, 'y'), (2, 'a')]

Сортировка в Python устойчива (stable). Это значит, что элементы, равные по ключу, сохраняют исходный порядок. Данное свойство позволяет выполнять многоуровневую сортировку последовательными вызовами sorted() с разными ключами.

Пример

# Сортировка с помощью operator.itemgetter для списков кортежей
data = [(2, 'banana'), (1, 'apple'), (3, 'cherry')]
from operator import itemgetter
sorted_by_first = sorted(data, key=itemgetter(0))
print(sorted_by_first)  # [(1, 'apple'), (2, 'banana'), (3, 'cherry')]
[(1, 'apple'), (2, 'banana'), (3, 'cherry')]

itemgetter(0) возвращает первый элемент каждого кортежа. Аналог lambda x: x[0], но работает немного быстрее.

Пример

# Сортировка с разными направлениями для разных полей
# Первое поле по возрастанию, второе - по убыванию
students = [('Alice', 85), ('Bob', 90), ('Alice', 95)]
sorted_students = sorted(students, key=lambda x: (x[0], -x[1]))
print(sorted_students)  # [('Alice', 95), ('Alice', 85), ('Bob', 90)]
[('Alice', 95), ('Alice', 85), ('Bob', 90)]

Пояснение: для числового поля убывание достигается инвертированием значения (умножение на -1). Для строк пришлось бы использовать functools.cmp_to_key или сортировать дважды.

Пример

# Сортировка с использованием def вместо lambda (для сложного ключа)
def sort_key(person):
    # Вернем кортеж: возраст, длина имени, имя
    return (person.age, len(person.name), person.name)

people = [Person('Alice', 25), Person('Bob', 20), Person('Alexander', 25)]
sorted_people = sorted(people, key=sort_key)
print(sorted_people)  # [Person(Bob, 20), Person(Alice, 25), Person(Alexander, 25)]
[Person(Bob, 20), Person(Alice, 25), Person(Alexander, 25)]

Именованная функция улучшает читаемость, когда ключ состоит из нескольких шагов. Здесь сравниваются сначала возраст, потом длина имени, потом само имя (лексикографически).

Пример

# Сортировка словаря по значениям
d = {'apple': 3, 'banana': 1, 'cherry': 2}
sorted_by_value = sorted(d.items(), key=lambda x: x[1])
print(sorted_by_value)  # [('banana', 1), ('cherry', 2), ('apple', 3)]
[('banana', 1), ('cherry', 2), ('apple', 3)]

Метод items() возвращает пары (ключ, значение). Сортируя по второму элементу, получаем список, упорядоченный по значениям. Чтобы получить отсортированный словарь, можно передать результат в dict().

Пример

# Сортировка с помощью частичного применения и default
from functools import partial

def compare_age(person1, person2):
    return person1.age - person2.age

# Непосредственно cmp_to_key с partial не нужен, но можно:
from functools import cmp_to_key
sorted_people = sorted(people, key=cmp_to_key(compare_age))
print(sorted_people)
[Person(Bob, 20), Person(Alice, 25), Person(Alexander, 25)]

Использование cmp_to_key с пользовательской функцией сравнения даёт полный контроль над порядком. Однако в большинстве случаев key с лямбдой предпочтительнее.

Сортировка данных в Python - comments

En
сортировка данных python (python)