Мне нужна помощь, чтобы начать работу с назначенной задачей сортировки 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)
Я предполагаю, что это легко исправить, но я не уверен, как действовать, не меняя начальный код.
Спасибо за любую помощь!
Спасибо @Shorn - результаты будут отсортированы по количеству, значение int во 2-м столбце, но мне разрешено сортировать результаты с использованием любого алгоритма сортировки, поэтому нет ограничений по временной/пространственной сложности. Кроме того, мне необходимо распечатать LinkedList до и после сортировки, поэтому я считаю, что можно редактировать существующий LinkedList. На самом деле я не надеялся на полное решение для сортировки, а вместо этого решил, как обработать предоставленный массив кортежей, используя указанные значения для self.name и self.quantity (data[0][0] и data[0][1 ]), поскольку я изо всех сил пытаюсь извлечь эти значения.
Надеюсь, вы понимаете, что len — это встроенная функция. Этот класс LinkedList неэффективно использует память, использует неинтуитивные имена (tail ссылается на узел, который не является хвостом списка), в нем отсутствует понятие «голова»…
Спасибо @trincot - это часть начального кода, которую я не могу изменить, а ссылки на data[0][0] и self.tail сделали задачу намного сложнее, чем просто использование отдельного класса Node с self.data и self.next вместе с LinkedList, указывающим вместо этого на голову.
@trincot tail — это LinkedList, а не просто LinkedListNode, с именем все в порядке.
Какие атрибуты объектов LinkedList вы можете изменить? Можно ли менять name и quantity? Или только tail? Или ни один из них (поэтому вы должны создать новый отсортированный список)?
Я думаю, что 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. Нам нужно найти средний узел, и для этого мы можем использовать подход черепахи и зайца:
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 вместо цикла? Я думаю, это будет лучше.
(Под линейным я имел в виду глубину рекурсии. Они занимают квадратичное время. Мое предложение для рекурсивного merge будет иметь линейную глубину и линейное время.)
@KellyBundy, хорошее замечание. Сделанный.
Почему метод класса? Моя версия.
Я считаю, что вызывающий код будет немного проще понять, если оба списка передаются в качестве аргументов. Но это всего лишь дело вкуса.
Хорошо. Я подумал, может быть, вы подумали о head1.tail = head1.tail.merge(head2) и поняли, что это не работает, когда head1.tail есть None. Вот почему я делаю head1.tail = head2.merge(head1.tail), поскольку head2 не является None. (Использование этого факта также делает мой базовый код короче.)
Большое спасибо @trincot за ответ и четкие объяснения, я очень ценю это! Теперь это имеет больше смысла (я рад, что это не было простым исправлением, в некотором смысле, поскольку я уже потратил много времени, пытаясь решить проблему самостоятельно), и вы превзошли все, на что я надеялся. для.
Простое решение для сортировки выбором, которое всегда заменяет наименьшие данные на текущую позицию, а затем переходит к следующей позиции. Это занимает квадратичное время, но вы отметили, что у вас нет ограничений по сложности, и ваш __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
Поскольку это похоже на задачу назначения, пожалуйста, укажите другие ограничения, которые могут у вас быть. Например, разрешено ли вам изменять/не использовать стартовый код? Существуют ли ограничения в виде пространственно-временной сложности? Вам необходимо добавить/удалить из списка позже? Также проясните, как это должно быть отсортировано. Вы сортируете его аналогично сортировке кортежа по умолчанию? Или только по значению int во 2-м столбце? Наконец, вы, скорее всего, получите больше ответов, если спросите о конкретной проблеме, а не о полном решении данной проблемы.