Сортировка кортежей в LinkedList, сформированном с помощью конструктора класса без использования встроенных функций — Python

Мне нужна помощь, чтобы начать работу с назначенной задачей сортировки LinkedList. Мне дали начальный код ниже и попросили придумать подходящий метод сортировки - я думаю, что MergeSort может работать лучше всего? - но большинство примеров, которые я вижу в Интернете, добавляются в LinkedList с помощью функции addNode вместе с отдельным классом Node, поэтому я не уверен, как действовать с приведенным ниже кодом, поскольку он указывает, что я должен использовать data[0][0] для self.name и data[0][1] для self.quantity, для которого я изо всех сил пытаюсь извлечь значения.

class LinkedList:
    def __init__(self, data):
        self.name = data[0][0]
        self.quantity = data[0][1]
        self.tail = None if (len(data) == 1) else LinkedList(data[1:])

# Shortened list to improve readability
fruits = LinkedList([("Apples",7),("Bananas",2),("Dragon Fruit",1),("Pomelo",14),("Grapes",65),("Cherries",43),("Pears",6),("Mangoes",31)])

Я попытался выполнить итерацию по LinkedList, но решения, которые я пробовал, включали преобразование в список или использование встроенных функций сортировки Python (оба из которых мне не разрешено использовать для этой задачи).

Кроме того, я попытался использовать цикл for in, как показано ниже, создав массив self.result, а затем добавив resultName и resultQuantity в виде кортежа, который, как мне кажется, является частичным решением, но я не смог найти способ решения для этого используются предоставленные атрибуты self.name (data[0][0]) и self.quantity (data[0][1]).

# Added to LinkedList class __init__
self.data = data
self.result = []
        
        for each_tuple in self.data:
            resultName = each_tuple[0]
            resultQuantity = each_tuple[1]
            self.result.append((resultName, resultQuantity))

print(fruits.result)

Я предполагаю, что это легко исправить, но я не уверен, как действовать, не меняя начальный код.

Спасибо за любую помощь!

Поскольку это похоже на задачу назначения, пожалуйста, укажите другие ограничения, которые могут у вас быть. Например, разрешено ли вам изменять/не использовать стартовый код? Существуют ли ограничения в виде пространственно-временной сложности? Вам необходимо добавить/удалить из списка позже? Также проясните, как это должно быть отсортировано. Вы сортируете его аналогично сортировке кортежа по умолчанию? Или только по значению int во 2-м столбце? Наконец, вы, скорее всего, получите больше ответов, если спросите о конкретной проблеме, а не о полном решении данной проблемы.

Shorn 15.02.2023 06:05

Спасибо @Shorn - результаты будут отсортированы по количеству, значение int во 2-м столбце, но мне разрешено сортировать результаты с использованием любого алгоритма сортировки, поэтому нет ограничений по временной/пространственной сложности. Кроме того, мне необходимо распечатать LinkedList до и после сортировки, поэтому я считаю, что можно редактировать существующий LinkedList. На самом деле я не надеялся на полное решение для сортировки, а вместо этого решил, как обработать предоставленный массив кортежей, используя указанные значения для self.name и self.quantity (data[0][0] и data[0][1 ]), поскольку я изо всех сил пытаюсь извлечь эти значения.

danielMBell 15.02.2023 06:37

Надеюсь, вы понимаете, что len — это встроенная функция. Этот класс LinkedList неэффективно использует память, использует неинтуитивные имена (tail ссылается на узел, который не является хвостом списка), в нем отсутствует понятие «голова»…

trincot 15.02.2023 08:53

Спасибо @trincot - это часть начального кода, которую я не могу изменить, а ссылки на data[0][0] и self.tail сделали задачу намного сложнее, чем просто использование отдельного класса Node с self.data и self.next вместе с LinkedList, указывающим вместо этого на голову.

danielMBell 15.02.2023 09:51

@trincot tail — это LinkedList, а не просто LinkedListNode, с именем все в порядке.

Kelly Bundy 15.02.2023 12:12

Какие атрибуты объектов LinkedList вы можете изменить? Можно ли менять name и quantity? Или только tail? Или ни один из них (поэтому вы должны создать новый отсортированный список)?

Kelly Bundy 15.02.2023 12:21
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
Потяните за рычаг выброса энергососущих проектов
Потяните за рычаг выброса энергососущих проектов
На этой неделе моя команда отменила проект, над которым я работал. Неделя усилий пошла насмарку.
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Инструменты для веб-скрапинга с открытым исходным кодом: Python Developer Toolkit
Веб-скрейпинг, как мы все знаем, это дисциплина, которая развивается с течением времени. Появляются все более сложные средства борьбы с ботами, а...
Библиотека для работы с мороженым
Библиотека для работы с мороженым
Лично я попрощался с операторами print() в python. Без шуток.
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Эмиссия счетов-фактур с помощью Telegram - Python RPA (BotCity)
Привет, люди RPA, это снова я и я несу подарки! В очередном моем приключении о том, как создавать ботов для облегчения рутины. Вот, думаю, стоит...
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
1
6
53
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий
Я думаю, что MergeSort может работать лучше всего?

Да, это хороший кандидат.

большинство примеров, которые я вижу в Интернете, добавляются к LinkedList, используя функцию addNode вместе с отдельным классом Node

Вам не нужна функция addNode, так как функция __init__ уже имеет дело с добавлением узлов. Ваша работа будет заключаться в том, чтобы перемонтировать существующие узлы, изменив их tail ссылки.

Упражнение дает вам только один класс, в то время как другие реализации используют два класса: один для экземпляров узла и один для экземпляра связанного списка, имеющего ссылку на головной узел связанного списка. Однако здесь единственный класс соответствует тому, что другие реализации называют классом Node, несмотря на название LinkedList. Здесь нет класса-контейнера, который содержит ссылку на головной узел. Это вы должны управлять "самим собой". Но это не главная проблема.

решения, которые я пробовал, включали преобразование в список или использование встроенных функций сортировки Python.

Вам не нужно добавлять код в метод __init__. Идея состоит в том, что вы создаете другой метод, который будет выполнять сортировку.

Этот метод не должен создавать список, подобный self.result = [], так как тогда вы просто переносите проблему сортировки в стандартный список, и вам все равно не разрешено использовать его, пусть вы можете вызвать sort или sorted для него. Более того, конечным результатом должен быть отсортированный связанный список, поэтому вам все равно придется копировать данные отсортированного списка обратно в связанный список. Идея этого упражнения состоит в том, что вы сортируете связанный список без этой дополнительной структуры данных.

вероятно, это легко исправить

Нет, так как вы не должны использовать какой-либо код, который создает и повторяет стандартный список. Так что этот код нужно сбросить.

Вы можете облегчить себе работу, сначала создав метод __iter__ в своем классе. Это не обязательно, но немного упрощает работу над этим упражнением, так как его можно использовать для печати списка или преобразования его в стандартный список для целей отладки. В связи с этим у нас есть содержимое узла, состоящее из двух компонентов: имени и количества. Поскольку мы хотим сортировать по количеству, кажется хорошей идеей иметь метод, который возвращает эти данные в виде кортежа: кортежи можно сравнивать при сортировке. Это также дает способ решить «связи» — когда два узла имеют одинаковое количество, мы, возможно, захотим использовать имя, чтобы определить, какой из них будет первым.

Вот два метода, которые я бы добавил в класс:

    def tuple(self):  # To get the content of a node as a tuple, easing comparisons
        return self.quantity, self.name
    
    def __iter__(self):  # This helps to visualise the linked list
        yield self.tuple()
        if self.tail:
            yield from self.tail

Имея это на месте, вы можете делать такие вещи, как:

fruits = LinkedList([("Apples",7),("Bananas",2),("Dragon Fruit",1),("Pomelo",14),("Grapes",65),("Cherries",43),("Pears",6),("Mangoes",31)])
print(*fruits)

... и он напечатает записи в связанном списке (с количеством в качестве первого члена кортежа). Это очень полезно при отладке.

Сортировка слиянием

Как вы предложили, давайте перейдем к сортировке слиянием. Это рекурсивный алгоритм, поэтому вам нужен базовый случай. В базовом случае список состоит только из одного элемента, т. е. когда его атрибут tail равен None. В этом случае список уже отсортирован и может быть возвращен без каких-либо дополнительных действий.

В рекурсивном случае вам необходимо:

  • Разделить список на две половины (partition):
    • Первая половина будет начинаться с того же узла, что и до разделения, поэтому единственная информация, которая нам нужна после этого разделения, — это ссылка на первый узел второй половины.
  • Отсортировать каждую из половин рекурсивно
  • Объедините две отсортированные половины

Давайте сделаем метод partition. Нам нужно найти средний узел, и для этого мы можем использовать подход черепахи и зайца:

    def partition(self):
        # Use Tortoise and Hare approach to find (almost) middle node
        slow = self
        fast = self.tail  # Start one step ahead
        while fast and fast.tail:
            fast = fast.tail.tail
            slow = slow.tail
        head2 = slow.tail  # Second half starts after the middle node
        slow.tail = None  # Disconnect the first half of the list from the second half
        return head2  # Return start of second half

Затем нам нужен метод merge, который получает второй отсортированный связанный список в качестве аргумента, а затем объединяет узлы обоих связанных списков в один связанный список. Мы можем сначала упорядочить два списка по их головным узлам, взять первый, а затем объединить остальную часть этого первого списка со вторым, пока в одном из списков не останется узлов:

    @classmethod
    def merge(Cls, head1, head2):
        if not (head1 and head2):  # Base case: If at least one of the lists is empty,
            # ...return the other one
            return head1 or head2
        # Order the lists so that head1 has the node that should come first
        if head1.tuple() > head2.tuple():
            head1, head2 = head2, head1
        head1.tail = Cls.merge(head1.tail, head2)  # Merge rest and prepend head1
        return head1

Наконец, нам нужно реализовать алгоритм mergesort:

    def mergesort(self):
        if not self.tail:  # Base case. When list has just one node, then it is sorted. 
            return self
        # Split list into two, sort the two halves recursively 
        #    and then merge the sorted halves
        return LinkedList.merge(self.partition().mergesort(), self.mergesort())

Вот и все. Беги как:

fruits = LinkedList([("Apples",7),("Bananas",2),("Dragon Fruit",1),("Pomelo",14),("Grapes",65),("Cherries",43),("Pears",6),("Mangoes",31)])
print(*fruits)
sortedfruits = fruits.mergesort()
print("sorted:")
print(*sortedfruits)

Они уже использовали линейную рекурсию в своих __init__, вы уже использовали линейную рекурсию в своих __iter__. Как насчет того, чтобы использовать его и в merge вместо цикла? Я думаю, это будет лучше.

Kelly Bundy 15.02.2023 12:57

(Под линейным я имел в виду глубину рекурсии. Они занимают квадратичное время. Мое предложение для рекурсивного merge будет иметь линейную глубину и линейное время.)

Kelly Bundy 15.02.2023 13:08

@KellyBundy, хорошее замечание. Сделанный.

trincot 15.02.2023 13:13

Почему метод класса? Моя версия.

Kelly Bundy 15.02.2023 13:18

Я считаю, что вызывающий код будет немного проще понять, если оба списка передаются в качестве аргументов. Но это всего лишь дело вкуса.

trincot 15.02.2023 13:20

Хорошо. Я подумал, может быть, вы подумали о head1.tail = head1.tail.merge(head2) и поняли, что это не работает, когда head1.tail есть None. Вот почему я делаю head1.tail = head2.merge(head1.tail), поскольку head2 не является None. (Использование этого факта также делает мой базовый код короче.)

Kelly Bundy 15.02.2023 13:27

Большое спасибо @trincot за ответ и четкие объяснения, я очень ценю это! Теперь это имеет больше смысла (я рад, что это не было простым исправлением, в некотором смысле, поскольку я уже потратил много времени, пытаясь решить проблему самостоятельно), и вы превзошли все, на что я надеялся. для.

danielMBell 15.02.2023 14:44

Простое решение для сортировки выбором, которое всегда заменяет наименьшие данные на текущую позицию, а затем переходит к следующей позиции. Это занимает квадратичное время, но вы отметили, что у вас нет ограничений по сложности, и ваш __init__, а также __iter__ тринкота также требует квадратичного времени.

def print_list(lst):
    print('LinkedList:')
    while lst:
        print(' ', lst.name, lst.quantity)
        lst = lst.tail
    print()

def sort_list(lst):
    while lst:
        mini = curr = lst
        while curr:
            if curr.quantity < mini.quantity:
                mini = curr
            curr = curr.tail
        lst.name, mini.name = mini.name, lst.name
        lst.quantity, mini.quantity = mini.quantity, lst.quantity
        lst = lst.tail

print_list(fruits)
sort_list(fruits)
print_list(fruits)

Вывод (Попробуйте онлайн!):

LinkedList:
  Apples 7
  Bananas 2
  Dragon Fruit 1
  Pomelo 14
  Grapes 65
  Cherries 43
  Pears 6
  Mangoes 31

LinkedList:
  Dragon Fruit 1
  Bananas 2
  Pears 6
  Apples 7
  Pomelo 14
  Mangoes 31
  Cherries 43
  Grapes 65

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