Doubly-linked list/Definition: Difference between revisions

Content added Content deleted
(Added Wren)
(Example usage of Python built-in and an alternative Python implementation)
Line 2,609: Line 2,609:
=={{header|Python}}==
=={{header|Python}}==
In the high level language Python, its <code>list</code> native datatype should be used. It automatically preserves the integrity of the list w.r.t. loops and allows insertion at any point using [http://docs.python.org/library/stdtypes.html#typesseq-mutable list.insert()] via an integer index into the list rather than a machine-code level pointer to a list element.
In the high level language Python, its <code>list</code> native datatype should be used. It automatically preserves the integrity of the list w.r.t. loops and allows insertion at any point using [http://docs.python.org/library/stdtypes.html#typesseq-mutable list.insert()] via an integer index into the list rather than a machine-code level pointer to a list element.

===collections.deque===
If, as you would expect from a doubly-linked list, you need to efficiently
append items to the start of an ordered collection, use Python’s built-in
[https://docs.python.org/3/library/collections.html#collections.deque collections.deque]
datatype. See [https://wiki.python.org/moin/TimeComplexity TimeComplexity].

<lang python>
from collections import deque

some_list = deque(["a", "b", "c"])
print(some_list)

some_list.appendleft("Z")
print(some_list)

for value in reversed(some_list):
print(value)
</lang>

{{out}}
<pre>
$ python deque_example.py
deque(['a', 'b', 'c'])
deque(['Z', 'a', 'b', 'c'])
c
b
a
Z
</pre>

===Alternative===
Although a <code>deque</code> is preferable in most cases, this implementation gives you the
option to access nodes and slice "views" from a <code>DoublyLinkedList</code>. Things that
<code>collections.deque</code> does not do.

Retrieving an item by it’s index returns the node’s value. Similarly, iterating
a <code>DoublyLinkedList</code> yields a sequence of values, not a sequence of nodes. The
return value of a slice is a <code>DoublyLinkedListView</code>, which shares common nodes with
the original list.

Note that, without a concrete use case, the decision as to which of the special
methods should return a node’s value or a <code>DoublyLinkedListView</code> is somewhat
arbitrary. One might also choose to do away with <code>DoublyLinkedListView</code> and return
a new <code>DoublyLinkedList</code>, accepting that update operations on a derived list could
break links between nodes in other lists.

<lang python>
"""A doubly-linked list. Requires Python >=3.7"""
from __future__ import annotations

import math

from abc import ABC

from collections.abc import MutableSequence
from collections.abc import Sequence
from dataclasses import dataclass
from dataclasses import field

from typing import Any
from typing import Iterable
from typing import Iterator
from typing import Optional
from typing import Union


@dataclass
class Node:
value: Any
prev: Optional[Node] = field(default=None)
next: Optional[Node] = field(default=None)


class BaseDoublyLinkedList(ABC):
def __len__(self):
return self.size

def __iter__(self) -> Iterator[Any]:
node = self.head
cnt = 1
while node is not None and cnt <= self.size:
yield node.value
node = node.next
cnt += 1

def __reversed__(self) -> Iterator[Any]:
node = self.tail
while node is not None:
yield node.value
node = node.prev

def __getitem__(self, key: Union[int, slice]) -> Optional[Any]:
if isinstance(key, int):
node = self._find_node(key)
return node.value

if key.step not in (None, 1):
raise IndexError("can't step more than 1")

start = key.start or 0
node = self._find_node(start)
return DoublyLinkedListView(node, key.stop - start - 1)

def _find_node(self, index) -> Node:
if index > self.size - 1 or index < -self.size:
raise IndexError("list index out of range")

if index >= 0:
node = self.head
for _ in range(index):
node = node.next
else:
node = self.tail
for _ in range(self.size - 1, self.size - abs(index), -1):
node = node.prev

return node


class DoublyLinkedListView(BaseDoublyLinkedList, Sequence):
def __init__(self, node, stop=math.inf):
self.head = node

# Find the tail
cnt = 1
while node is not None and cnt <= stop:
node = node.next
cnt += 1

self.tail = node
self.size = cnt

def __repr__(self):
return f"DoublyLinkedListView([{', '.join(repr(v) for v in self)}])"

def __str__(self):
return repr(self)


class DoublyLinkedList(BaseDoublyLinkedList, MutableSequence):
def __init__(self, iterable: Iterable):
it = iter(iterable)

# Possibly empty iterable
try:
self.head = Node(value=next(it))
self.tail = self.head
self.size = 1
except StopIteration:
self.head = None
self.tail = None
self.size = 0

node = self.head

for val in it:
self.tail = Node(value=val, prev=node)
node.next = self.tail
node = self.tail
self.size += 1

def __repr__(self):
return f"DoublyLinkedList([{', '.join(repr(v) for v in self)}])"

def __str__(self):
return repr(self)

def __setitem__(self, key, value):
node = self._find_node(key)
node.value = value

def __delitem__(self, key) -> None:
node = self._find_node(key)
node.prev.next = node.next
node.next.prev = node.prev
self.size -= 1

def _find_node(self, index) -> Node:
if index > self.size - 1 or index < -self.size:
raise IndexError("list index out of range")

if index >= 0:
node = self.head
for _ in range(index):
node = node.next
else:
node = self.tail
for _ in range(self.size - 1, self.size - abs(index), -1):
node = node.prev

return node

def insert(self, index: int, value: Any) -> None:
node = self._find_node(index)
new_node = Node(value=value, prev=node.prev, next=node)

node.prev.next = new_node
node.prev = node

self.size += 1

def append(self, value: Any) -> None:
tail = self.tail
node = Node(value=value, prev=tail)
tail.next = node

self.tail = node
self.size += 1

def appendleft(self, value: Any) -> None:
head = self.head
node = Node(value=value, next=head)
head.prev = node

self.head = node
self.size += 1

def pop(self) -> Any:
value = self.tail.value
self.tail = self.tail.prev
self.tail.next = None
self.size -= 1
return value

def popleft(self) -> Any:
value = self.head.value
self.head = self.head.next
self.head.prev = None
self.size -= 1
return value
</lang>


=={{header|Racket}}==
=={{header|Racket}}==