Глубокая копия двусвязного списка в Python

У меня возникли проблемы с реализацией метода глубокого копирования для класса DoublyLinkedList. Предполагается, что глубокая копия возвращает новый исходный двусвязный список, который не ссылается на исходную DLL (в отличие от поверхностной копии).

Вот что у меня есть до сих пор:

class EmptyCollection(Exception):
    pass


class DoublyLinkedList:
    class Node:
        def __init__(self, data=None, next=None, prev=None):
            self.data = data
            self.next = next
            self.prev = prev

        def disconnect(self):
            self.data = None
            self.next = None
            self.prev = None

    def __init__(self):
        self.header = DoublyLinkedList.Node()
        self.trailer = DoublyLinkedList.Node()
        self.header.next = self.trailer
        self.trailer.prev = self.header
        self.size = 0

    def __len__(self):
        return self.size

    def is_empty(self):
        return (len(self) == 0)

    def first_node(self):
        if (self.is_empty()):
            raise EmptyCollection("List is empty")
        return self.header.next

    def last_node(self):
        if (self.is_empty()):
            raise EmptyCollection("List is empty")
        return self.trailer.prev

    def add_first(self, elem):
        return self.add_after(self.header, elem)

    def add_last(self, elem):
        return self.add_after(self.trailer.prev, elem)

    def add_after(self, node, elem):
        prev = node
        succ = node.next
        new_node = DoublyLinkedList.Node()
        new_node.data = elem
        new_node.prev = prev
        new_node.next = succ
        prev.next = new_node
        succ.prev = new_node
        self.size += 1
        return new_node

    def add_before(self, node, elem):
        return self.add_after(node.prev, elem)

    def delete(self, node):
        prev = node.prev
        succ = node.next
        prev.next = succ
        succ.prev = prev
        self.size -= 1
        data = node.data
        node.disconnect()
        return data

    def __iter__(self):
        if(self.is_empty()):
            return
        cursor = self.first_node()
        while(cursor is not self.trailer):
            yield cursor.data
            cursor = cursor.next

    def __str__(self):
        return '[' + '<-->'.join([str(elem) for elem in self]) + ']'

    def __repr__(self):
        return str(self)




def deepCopy(lnk_lst):
    currenthead = lnk_lst.first_node()
    temp = DoublyLinkedList()
    while currenthead is not lnk_lst.trailer:
        temp.add_last(currenthead.data)
        currenthead = currenthead.next
    return temp


lnk_lst1 = DoublyLinkedList()
elem1 = DoublyLinkedList()
elem1.add_last(1)
elem1.add_last(2)
lnk_lst1.add_last(elem1)
elem2 = 3
lnk_lst1.add_last(elem2)
lnk_lst2 = deepCopy(lnk_lst1)
e1 = lnk_lst1.first_node()
e1_1 = e1.data.first_node()
e1_1.data = 10
e2 = lnk_lst2.first_node()
e2_1 = e2.data.first_node()
print(e2_1.data) #should print 1

Мой метод глубокого копирования, похоже, возвращает поверхностную копию. Вывод программы должен быть равен 1 (поскольку lnk_lst2 не должен ссылаться ни на какие элементы в lnk_lst1).

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

Я не могу использовать для этого встроенную глубокую или мелкую копию Python. Любая помощь будет оценена по достоинству.


person swing1234    schedule 02.11.2017    source источник
comment
Ваш пример немного сбивает с толку. Почему elem1 — это список, а не узел?   -  person RobertB    schedule 02.11.2017
comment
Поскольку вы написали только поверхностную копию: temp.add_last(currenthead.data) добавляет тот же объект из списка, который вы копируете, в копию. Это это поверхностная копия. Обычно функция deepcopy должна рекурсивно воздействовать на объекты, поэтому что-то вроде temp.add_last(deepCopy(currenthead.data)), а ваша deepCopy должна знать, как обращаться с объектами, которые вы ожидаете.   -  person juanpa.arrivillaga    schedule 02.11.2017
comment
Обратите внимание, что это может довольно быстро усложниться, если ваша функция deepCopy будет ожидать любой произвольный объект.   -  person juanpa.arrivillaga    schedule 02.11.2017
comment
Кстати, вы можете сами прочитать реализацию deepcopy: github.com/python /cpython/blob/master/Lib/copy.py   -  person juanpa.arrivillaga    schedule 02.11.2017


Ответы (2)


Чтобы выполнить глубокую копию, вам нужно скопировать встроенные связанные списки:

def deepCopy(lnk_lst):
    currenthead = lnk_lst.first_node()
    temp = DoublyLinkedList()
    while currenthead is not lnk_lst.trailer:
        if isinstance(currenthead.data, DoublyLinkedList):
            temp.add_last(deepCopy(currenthead.data))
        else:
            temp.add_last(currenthead.data)
        currenthead = currenthead.next
    return temp
person janos    schedule 02.11.2017

Многие базовые объекты можно скопировать с помощью type(obj)(obj). Например. dict(dct) или list(lst) создаст копию. Неизменяемые типы будут возвращать один и тот же объект, и это нормально. int(42) – 42, str("string") – "string" и т. д.

Следующее решение будет ограничено такими типами.

Воспользуемся этим и добавим DoublyLinkedList в набор типов, создающих копию (в нашем случае глубокую копию, но только на первом уровне вложенности) таким образом. Изменено __init__:

def __init__(self, iterable=()):
    self.header = DoublyLinkedList.Node()
    self.trailer = DoublyLinkedList.Node()
    self.header.next = self.trailer
    self.trailer.prev = self.header
    self.size = 0 
    for item in iterable:
        self.add_last(type(item)(item))

Теперь нам больше не нужен deepCopy(). Осталось только заменить:

lnk_lst2 = deepCopy(lnk_lst1)

by:

lnk_lst2 = DoublyLinkedList(lnk_lst1)
person VPfB    schedule 02.11.2017