Связанный список Python - PullRequest
       93

Связанный список Python

171 голосов
/ 11 ноября 2008

Какой самый простой способ использовать связанный список в python? В схеме связанный список определяется просто как '(1 2 3 4 5). Списки Python [1, 2, 3, 4, 5] и кортежи (1, 2, 3, 4, 5) на самом деле не являются связанными списками, и связанные списки имеют некоторые приятные свойства, такие как конкатенация в постоянном времени, и возможность ссылаться на отдельные их части. Сделайте их неизменными, и с ними действительно легко работать!

Ответы [ 26 ]

154 голосов
/ 12 ноября 2008

Для некоторых нужд может также пригодиться deque . Вы можете добавлять и удалять элементы на обоих концах deque по цене O (1).

from collections import deque
d = deque([1,2,3,4])

print d
for x in d:
    print x
print d.pop(), d
69 голосов
/ 12 ноября 2008

Вот некоторые функции списка, основанные на представлении Мартина против Лёвиса :

cons   = lambda el, lst: (el, lst)
mklist = lambda *args: reduce(lambda lst, el: cons(el, lst), reversed(args), None)
car = lambda lst: lst[0] if lst else lst
cdr = lambda lst: lst[1] if lst else lst
nth = lambda n, lst: nth(n-1, cdr(lst)) if n > 0 else car(lst)
length  = lambda lst, count=0: length(cdr(lst), count+1) if lst else count
begin   = lambda *args: args[-1]
display = lambda lst: begin(w("%s " % car(lst)), display(cdr(lst))) if lst else w("nil\n")

где w = sys.stdout.write

Хотя двусвязные списки, как известно, используются в рецепте упорядоченного набора Рэймонда Хеттингера *, односвязные списки не имеют практического значения в Python.

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

Томас Уотнедал предложил хороший образовательный ресурс Как мыслить как ученый, Глава 17: Связанные списки :

Связанный список:

  • пустой список, представленный None, или
  • узел, который содержит объект груза и ссылку на связанный список.

    class Node: 
      def __init__(self, cargo=None, next=None): 
        self.car = cargo 
        self.cdr = next    
      def __str__(self): 
        return str(self.car)
    
    def display(lst):
      if lst:
        w("%s " % lst)
        display(lst.cdr)
      else:
        w("nil\n")
    
66 голосов
/ 11 ноября 2008

Я написал это на днях

#! /usr/bin/env python

class Node(object):
    def __init__(self):
        self.data = None # contains the data
        self.next = None # contains the reference to the next node


class LinkedList:
    def __init__(self):
        self.cur_node = None

    def add_node(self, data):
        new_node = Node() # create a new node
        new_node.data = data
        new_node.next = self.cur_node # link the new node to the 'previous' node.
        self.cur_node = new_node #  set the current node to the new one.

    def list_print(self):
        node = self.cur_node # cant point to ll!
        while node:
            print node.data
            node = node.next



ll = LinkedList()
ll.add_node(1)
ll.add_node(2)
ll.add_node(3)

ll.list_print()
34 голосов
/ 21 августа 2010

Принятый ответ довольно сложный. Вот более стандартный дизайн:

L = LinkedList()
L.insert(1)
L.insert(1)
L.insert(2)
L.insert(4)
print L
L.clear()
print L

Это простой LinkedList класс, основанный на простом дизайне C ++ и Глава 17: Связанные списки , как рекомендует Томас Ватнедал .

class Node:
    def __init__(self, value = None, next = None):
        self.value = value
        self.next = next

    def __str__(self):
        return 'Node ['+str(self.value)+']'

class LinkedList:
    def __init__(self):
        self.first = None
        self.last = None

    def insert(self, x):
        if self.first == None:
            self.first = Node(x, None)
            self.last = self.first
        elif self.last == self.first:
            self.last = Node(x, None)
            self.first.next = self.last
        else:
            current = Node(x, None)
            self.last.next = current
            self.last = current

    def __str__(self):
        if self.first != None:
            current = self.first
            out = 'LinkedList [\n' +str(current.value) +'\n'
            while current.next != None:
                current = current.next
                out += str(current.value) + '\n'
            return out + ']'
        return 'LinkedList []'

    def clear(self):
        self.__init__()
17 голосов
/ 11 ноября 2008

Неизменяемые списки лучше всего представлены в виде двух кортежей, причем None представляет NIL. Для упрощения составления таких списков вы можете использовать эту функцию:

def mklist(*args):
    result = None
    for element in reversed(args):
        result = (element, result)
    return result

Чтобы работать с такими списками, я бы предпочел предоставить весь набор функций LISP (т. Е. Первый, второй, n-тый и т. Д.), Чем вводить методы.

13 голосов
/ 11 ноября 2008

Вот немного более сложная версия класса связанного списка, с интерфейсом, похожим на типы последовательностей Python (т. Е. Поддерживает индексацию, секцию, конкатенацию с произвольными последовательностями и т. Д.). Он должен иметь предварение O (1), не копировать данные, если в этом нет необходимости, и его можно взаимозаменяемо использовать с кортежами.

Это не будет столь же эффективно по пространству или времени, как у lisp cons-ячеек, поскольку классы python, очевидно, немного более тяжелые (вы могли бы немного улучшить ситуацию с помощью __slots__ = '_head','_tail', чтобы уменьшить использование памяти) Тем не менее, он будет иметь желаемые характеристики при больших значениях O.

Пример использования:

>>> l = LinkedList([1,2,3,4])
>>> l
LinkedList([1, 2, 3, 4])
>>> l.head, l.tail
(1, LinkedList([2, 3, 4]))

# Prepending is O(1) and can be done with:
LinkedList.cons(0, l)
LinkedList([0, 1, 2, 3, 4])
# Or prepending arbitrary sequences (Still no copy of l performed):
[-1,0] + l
LinkedList([-1, 0, 1, 2, 3, 4])

# Normal list indexing and slice operations can be performed.
# Again, no copy is made unless needed.
>>> l[1], l[-1], l[2:]
(2, 4, LinkedList([3, 4]))
>>> assert l[2:] is l.next.next

# For cases where the slice stops before the end, or uses a
# non-contiguous range, we do need to create a copy.  However
# this should be transparent to the user.
>>> LinkedList(range(100))[-10::2]
LinkedList([90, 92, 94, 96, 98])

Реализация:

import itertools

class LinkedList(object):
    """Immutable linked list class."""

    def __new__(cls, l=[]):
        if isinstance(l, LinkedList): return l # Immutable, so no copy needed.
        i = iter(l)
        try:
            head = i.next()
        except StopIteration:
            return cls.EmptyList   # Return empty list singleton.

        tail = LinkedList(i)

        obj = super(LinkedList, cls).__new__(cls)
        obj._head = head
        obj._tail = tail
        return obj

    @classmethod
    def cons(cls, head, tail):
        ll =  cls([head])
        if not isinstance(tail, cls):
            tail = cls(tail)
        ll._tail = tail
        return ll

    # head and tail are not modifiable
    @property  
    def head(self): return self._head

    @property
    def tail(self): return self._tail

    def __nonzero__(self): return True

    def __len__(self):
        return sum(1 for _ in self)

    def __add__(self, other):
        other = LinkedList(other)

        if not self: return other   # () + l = l
        start=l = LinkedList(iter(self))  # Create copy, as we'll mutate

        while l:
            if not l._tail: # Last element?
                l._tail = other
                break
            l = l._tail
        return start

    def __radd__(self, other):
        return LinkedList(other) + self

    def __iter__(self):
        x=self
        while x:
            yield x.head
            x=x.tail

    def __getitem__(self, idx):
        """Get item at specified index"""
        if isinstance(idx, slice):
            # Special case: Avoid constructing a new list, or performing O(n) length 
            # calculation for slices like l[3:].  Since we're immutable, just return
            # the appropriate node. This becomes O(start) rather than O(n).
            # We can't do this for  more complicated slices however (eg [l:4]
            start = idx.start or 0
            if (start >= 0) and (idx.stop is None) and (idx.step is None or idx.step == 1):
                no_copy_needed=True
            else:
                length = len(self)  # Need to calc length.
                start, stop, step = idx.indices(length)
                no_copy_needed = (stop == length) and (step == 1)

            if no_copy_needed:
                l = self
                for i in range(start): 
                    if not l: break # End of list.
                    l=l.tail
                return l
            else:
                # We need to construct a new list.
                if step < 1:  # Need to instantiate list to deal with -ve step
                    return LinkedList(list(self)[start:stop:step])
                else:
                    return LinkedList(itertools.islice(iter(self), start, stop, step))
        else:       
            # Non-slice index.
            if idx < 0: idx = len(self)+idx
            if not self: raise IndexError("list index out of range")
            if idx == 0: return self.head
            return self.tail[idx-1]

    def __mul__(self, n):
        if n <= 0: return Nil
        l=self
        for i in range(n-1): l += self
        return l
    def __rmul__(self, n): return self * n

    # Ideally we should compute the has ourselves rather than construct
    # a temporary tuple as below.  I haven't impemented this here
    def __hash__(self): return hash(tuple(self))

    def __eq__(self, other): return self._cmp(other) == 0
    def __ne__(self, other): return not self == other
    def __lt__(self, other): return self._cmp(other) < 0
    def __gt__(self, other): return self._cmp(other) > 0
    def __le__(self, other): return self._cmp(other) <= 0
    def __ge__(self, other): return self._cmp(other) >= 0

    def _cmp(self, other):
        """Acts as cmp(): -1 for self<other, 0 for equal, 1 for greater"""
        if not isinstance(other, LinkedList):
            return cmp(LinkedList,type(other))  # Arbitrary ordering.

        A, B = iter(self), iter(other)
        for a,b in itertools.izip(A,B):
           if a<b: return -1
           elif a > b: return 1

        try:
            A.next()
            return 1  # a has more items.
        except StopIteration: pass

        try:
            B.next()
            return -1  # b has more items.
        except StopIteration: pass

        return 0  # Lists are equal

    def __repr__(self):
        return "LinkedList([%s])" % ', '.join(map(repr,self))

class EmptyList(LinkedList):
    """A singleton representing an empty list."""
    def __new__(cls):
        return object.__new__(cls)

    def __iter__(self): return iter([])
    def __nonzero__(self): return False

    @property
    def head(self): raise IndexError("End of list")

    @property
    def tail(self): raise IndexError("End of list")

# Create EmptyList singleton
LinkedList.EmptyList = EmptyList()
del EmptyList
6 голосов
/ 24 марта 2017

llist - типы данных связанного списка для Python

Модуль List реализует структуры данных связанного списка. Он поддерживает двусвязный список, т.е. dllist и односвязную структуру данных sllist.

объекты dllist

Этот объект представляет собой двунаправленную структуру данных списка.

first

Первый dllistnode объект в списке. None если список пуст.

last

Последний dllistnode объект в списке. Нет, если список пуст.

Объекты dllist также поддерживают следующие методы:

append(x)

Добавьте x в правую часть списка и вставьте возврат dllistnode.

appendleft(x)

Добавьте x в левую часть списка и вставьте возврат dllistnode.

appendright(x)

Добавьте x в правую часть списка и вставьте возврат dllistnode.

clear()

Удалить все узлы из списка.

extend(iterable)

Добавление элементов из iterable в правую часть списка.

extendleft(iterable)

Добавить элементы из iterable в левую часть списка.

extendright(iterable)

Добавление элементов из iterable в правую часть списка.

insert(x[, before])

Добавьте x справа от списка, если before не указано, или вставьте x слева от dllistnode before. Возврат вставлен dllistnode.

nodeat(index)

Вернуть узел (типа dllistnode) в index.

pop()

Удалить и вернуть значение элемента из правой части списка.

popleft()

Удалить и вернуть значение элемента из левой части списка.

popright()

Удалить и вернуть значение элемента из правой части списка

remove(node)

Удалить node из списка и вернуть элемент, который в нем хранился.

dllistnode объекты

класс llist.dllistnode([value])

Возвращает новый двунаправленный узел списка, инициализированный (опционально) с value.

dllistnode объекты предоставляют следующие атрибуты:

next

Следующий узел в списке. Этот атрибут только для чтения.

prev

Предыдущий узел в списке. Этот атрибут только для чтения.

value

Значение хранится в этом узле. Составлено по этой ссылке

sllist

класс llist.sllist([iterable]) Вернуть новый односвязный список, инициализированный элементами из iterable. Если итерация не указана, новый sllist пуст.

Аналогичный набор атрибутов и операций определен для этого sllist объекта. См. Эту ссылку для получения дополнительной информации.

4 голосов
/ 01 февраля 2017
class Node(object):
    def __init__(self, data=None, next=None):
        self.data = data
        self.next = next

    def setData(self, data):
        self.data = data
        return self.data

    def setNext(self, next):
        self.next = next

    def getNext(self):
        return self.next

    def hasNext(self):
        return self.next != None


class singleLinkList(object):

    def __init__(self):
        self.head = None

    def isEmpty(self):
        return self.head == None

    def insertAtBeginning(self, data):
        newNode = Node()
        newNode.setData(data)

        if self.listLength() == 0:
            self.head = newNode
        else:
            newNode.setNext(self.head)
            self.head = newNode

    def insertAtEnd(self, data):
        newNode = Node()
        newNode.setData(data)

        current = self.head

        while current.getNext() != None:
            current = current.getNext()

        current.setNext(newNode)

    def listLength(self):
        current = self.head
        count = 0

        while current != None:
            count += 1
            current = current.getNext()
        return count

    def print_llist(self):
        current = self.head
        print("List Start.")
        while current != None:
            print(current.getData())
            current = current.getNext()

        print("List End.")



if __name__ == '__main__':
    ll = singleLinkList()
    ll.insertAtBeginning(55)
    ll.insertAtEnd(56)
    ll.print_llist()
    print(ll.listLength())
2 голосов
/ 25 сентября 2013

Я только что сделал эту как забавную игрушку. Он должен быть неизменным до тех пор, пока вы не коснетесь методов с префиксом подчеркивания, и он реализует кучу магии Python, таких как индексация и len.

2 голосов
/ 03 марта 2013

Вот то, что я придумал. В этой теме он похож на Риккардо С., за исключением того, что он печатает числа по порядку, а не в обратном порядке. Я также сделал объект LinkedList итератором Python, чтобы распечатать список так же, как обычный список Python.

class Node:

    def __init__(self, data=None):
        self.data = data
        self.next = None

    def __str__(self):
        return str(self.data)


class LinkedList:

    def __init__(self):
        self.head = None
        self.curr = None
        self.tail = None

    def __iter__(self):
        return self

    def next(self):
        if self.head and not self.curr:
            self.curr = self.head
            return self.curr
        elif self.curr.next:
            self.curr = self.curr.next
            return self.curr
        else:
            raise StopIteration

    def append(self, data):
        n = Node(data)
        if not self.head:
            self.head = n
            self.tail = n
        else:
            self.tail.next = n
            self.tail = self.tail.next


# Add 5 nodes
ll = LinkedList()
for i in range(1, 6):
    ll.append(i)

# print out the list
for n in ll:
    print n

"""
Example output:
$ python linked_list.py
1
2
3
4
5
"""
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...